JDK-8067846 : (sctp) InternalError when receiving SendFailedNotification
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.net
  • Affected Version: 8u25,9
  • Priority: P3
  • Status: Closed
  • Resolution: Fixed
  • OS: linux_redhat_6.0
  • CPU: x86
  • Submitted: 2014-12-15
  • Updated: 2016-08-24
  • Resolved: 2015-02-16
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 7 JDK 8 JDK 9
7u76Fixed 8u60Fixed 9 b52Fixed
Related Reports
Duplicate :  
Relates :  
Relates :  
Description
FULL PRODUCT VERSION :
java version "1.8.0_25"
Java(TM) SE Runtime Environment (build 1.8.0_25-b17)
Java HotSpot(TM) 64-Bit Server VM (build 25.25-b02, mixed mode)


ADDITIONAL OS VERSION INFORMATION :
Linux pclt-henjoh 3.16.0-25-generic #33-Ubuntu SMP Tue Nov 4 12:06:54 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux

EXTRA RELEVANT SYSTEM CONFIGURATION :
lksctp-tools 1.0.16

A DESCRIPTION OF THE PROBLEM :
JVM exits with InternalError when calling SctpMultiChannel.receive 

If a remote address which I am trying to send a packet to is down / not listening, 
I get a "java.lang.InternalError: should not reach here" error in SctpMultiChannel.receive, 
if the payload of my SctpMultiChannel.send call was 101 bytes or more. 

If I use a payload of 100 bytes or less, 
I get a sun.nio.ch.sctp.SendFailed notification instead, which seems more correct and is what I would expect. 

This behaviour was introduced in Java 1.8.0_20, and does not exist in 1.8.0_11 and earlier. 
(It also exists in Java 1.7.0_60 and later but not in 1.7.0_55 and earlier.)


REGRESSION.  Last worked in version 8u11

ADDITIONAL REGRESSION INFORMATION: 
java version "1.8.0_11"
Java(TM) SE Runtime Environment (build 1.8.0_11-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.11-b03, mixed mode)


STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run provided test program with Java 1.8.0_25 to reproduce the problem.
Run provided test program with Java 1.8.0_11 or earlier to see that it worked before.

The test program first shows that 100 bytes works and then that 101 bytes fails:
1. SctpMultiChannel.send(ByteBuffer.allocate(100 bytes), remoteAddressNotListening);
2. SctpMultiChannel.receive( notificationhandler )
3. SctpMultiChannel.receive( notificationhandler)

4. SctpMultiChannel.send(ByteBuffer.allocate(101 bytes), remoteAddressNotListening);
5. SctpMultiChannel.receive( notificationhandler )
6. SctpMultiChannel.receive( notificationhandler )



EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
1. SctpMultiChannel.send(ByteBuffer.allocate(100 bytes), remoteAddressNotListening);
2. SctpMultiChannel.receive( NotificationHandler AssociationChangeEvent CANT_START )
3. SctpMultiChannel.receive( NotificationHandler SendFailed )

4. SctpMultiChannel.send(ByteBuffer.allocate(101 bytes), remoteAddressNotListening);
5. SctpMultiChannel.receive( NotificationHandler AssociationChangeEvent CANT_START )
6. SctpMultiChannel.receive( NotificationHandler SendFailed )
ACTUAL -
1. SctpMultiChannel.send(ByteBuffer.allocate(100 bytes), remoteAddressNotListening);
2. SctpMultiChannel.receive( NotificationHandler AssociationChangeEvent CANT_START )
3. SctpMultiChannel.receive( NotificationHandler SendFailed )

4. SctpMultiChannel.send(ByteBuffer.allocate(101 bytes), remoteAddressNotListening);
5. SctpMultiChannel.receive( NotificationHandler AssociationChangeEvent CANT_START )
6. SctpMultiChannel.receive( ) "java.lang.InternalError: should not reach here"

ERROR MESSAGES/STACK TRACES THAT OCCUR :
    Exception in thread "main" java.lang.InternalError: should not reach here
        at sun.nio.ch.sctp.SctpChannelImpl.receive0(Native Method)
        at sun.nio.ch.sctp.SctpMultiChannelImpl.receive0(SctpMultiChannelImpl.java:978)
        at sun.nio.ch.sctp.SctpMultiChannelImpl.receiveIntoNativeBuffer(SctpMultiChannelImpl.java:582)
        at sun.nio.ch.sctp.SctpMultiChannelImpl.receive(SctpMultiChannelImpl.java:566)
        at sun.nio.ch.sctp.SctpMultiChannelImpl.receive(SctpMultiChannelImpl.java:496)
        at Main.send(Main.java:96)
        at Main.main(Main.java:61)

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import com.sun.nio.sctp.HandlerResult;
import com.sun.nio.sctp.MessageInfo;
import com.sun.nio.sctp.Notification;
import com.sun.nio.sctp.NotificationHandler;
import com.sun.nio.sctp.SctpMultiChannel;
import com.sun.nio.sctp.SctpStandardSocketOptions;
import com.sun.nio.sctp.SctpStandardSocketOptions.InitMaxStreams;

import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

/**
 * This test tries to connect to a remote address, which is down, using the SCTP protocol.
 * It uses SctpMultiChannel to initiate an implicit association by sending a packet to the remote side.
 *
 * On Linux, the packet "is sent" and I can use my NotificationHandler to receive CANT_START and SendFailed notifications.
 * On Solaris, the SctpMultiChannel.send() throws a java.net.ConnectException: Connection refused.
 *
 * It seems the size of this packet matters in newer Java versions than 1.8u11/1.7u55.
 * If I use 1.8u20/1.7u60, or later, I cannot use more than 100 bytes in the initial packet
 * without getting an InternalError instead of a SendFailed notification.
 *
 */
public class Main {

    static SocketAddress localSocketAddress  = new InetSocketAddress("127.0.0.1", 2000);
    static SocketAddress remoteSocketAddress = new InetSocketAddress("127.0.0.1", 3000);

    public static void main(final String[] args) {

        if(args.length == 4) {
            try {
                localSocketAddress = new InetSocketAddress(args[0], Integer.parseInt(args[1]));
                remoteSocketAddress = new InetSocketAddress(args[2], Integer.parseInt(args[3]));
            } catch(Exception e) {
                e.printStackTrace();
            }
        } else {
            System.out.println("Usage: java Main <localip> <localport> <remoteip> <remoteport>");
        }

        System.out.println("Using local address: " + localSocketAddress);
        System.out.println("Using remote address: " + remoteSocketAddress);
        System.out.println("Note, remote address should not be up");


        printSystemInfo();

        final Main main = new Main();

        // This will work
        main.send(100);


        // This will fail,
        // on Linux with newer Java than 1.8u11/1.7u55
        main.send(101);
    }



    public void send(final int sendBufferSize) {
        System.out.println("==== Trying with " + sendBufferSize + " byte send buffer ====");
        try (final SctpMultiChannel sctpMultiChannel = SctpMultiChannel.open()) {
            sctpMultiChannel.setOption(SctpStandardSocketOptions.SCTP_INIT_MAXSTREAMS, InitMaxStreams.create(1, 1), null);

            sctpMultiChannel.bind(localSocketAddress);

            final MessageInfo messageInfo = MessageInfo.createOutgoing(remoteSocketAddress, 0);

            System.out.println("Sending connection attempt to " + remoteSocketAddress.toString());
            final int sent = sctpMultiChannel.send(ByteBuffer.allocate(sendBufferSize), messageInfo);
            System.out.println("Sent " + sent);

            final NotificationHandler notificationHandler = new NotificationHandler<Object>() {
                @Override
                public HandlerResult handleNotification(Notification notification, Object o) {
                    System.out.println("Notification: " + notification);
                    return HandlerResult.RETURN;
                }
            };

            final ByteBuffer receiveByteBuffer = ByteBuffer.allocate(1024);

            receiveByteBuffer.clear();
            System.out.println("First receive on SctpMultiChannel");
            final MessageInfo receivedMessageInfo1 = sctpMultiChannel.receive(receiveByteBuffer, null, notificationHandler);


            receiveByteBuffer.clear();
            System.out.println("Second receive on SctpMultiChannel");
            final MessageInfo receivedMessageInfo2 = sctpMultiChannel.receive(receiveByteBuffer, null, notificationHandler);

            System.out.println("No InternalError, success!\n");

        } catch (final Exception e) {
            e.printStackTrace();
        }
    }



    private static void printSystemInfo() {
        final List<String> properties = Arrays.asList(
                "os.name", "os.arch", "os.version",
                "java.runtime.version",
                "java.vm.specification.vendor");

        for(final String property : properties) {
            System.out.println(property + ": " + System.getProperty(property));
        }
    }

}

---------- END SOURCE ----------


Comments
This is a regression as a result of JDK-8034181. The actual crux of the fix for JDK-8034181 is good, but it introduced an "assertion", throw InternalError, if the notification received from the OS SCTP stack returned a size greater than the native sctp_notification structure, as the native Java implementation needs to allocate internal memory to hold the bytes of the actual structure. This assertion is incorrect. SCTP send fail notifications are 48 bytes + user sent bytes. From the testcase in the description: sctp_notification: 148 bytes sctp_send_failed: 48 bytes users unsent data: 101 sftp_send_failed + user unsent data = 149 > sctp_notification => throw InternalError!
23-12-2014

Tested this on Linux 32-bit system and could reproduce the issue as claimed by the user. JDK 7u72, 8u20, 8u25 and 9 displayed error " Exception in thread "main" java.lang.InternalError: should not reach here" on run. No InternalError, success! while running this with JDK 8u11 though. Moving this up for further review.
18-12-2014