1 /* 2 * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 /* @test 25 * @bug 8034181 26 * @summary SIGBUS in SctpChannelImpl receive 27 * @author chegar 28 */ 29 30 import java.net.InetSocketAddress; 31 import java.net.SocketAddress; 32 import java.io.IOException; 33 import java.nio.ByteBuffer; 34 import com.sun.nio.sctp.AbstractNotificationHandler; 35 import com.sun.nio.sctp.AssociationChangeNotification; 36 import com.sun.nio.sctp.AssociationChangeNotification.AssocChangeEvent; 37 import com.sun.nio.sctp.HandlerResult; 38 import com.sun.nio.sctp.MessageInfo; 39 import com.sun.nio.sctp.Notification; 40 import com.sun.nio.sctp.PeerAddressChangeNotification; 41 import com.sun.nio.sctp.SctpChannel; 42 import com.sun.nio.sctp.SctpServerChannel; 43 import com.sun.nio.sctp.ShutdownNotification; 44 import static java.lang.System.out; 45 import static java.lang.System.err; 46 import static java.nio.charset.StandardCharsets.US_ASCII; 47 48 public class ReceiveIntoDirect { 49 /* suitably small message to NOT overrun small buffers */ 50 final byte[] msgBytes = "Hello".getBytes(US_ASCII); 51 52 /* number of client connections/combinations (accepted by the server) */ 53 final int NUM_CONNECTIONS = 75; 54 test(String[] args)55 void test(String[] args) throws IOException { 56 SocketAddress address = null; 57 Server server; 58 59 if (!Util.isSCTPSupported()) { 60 out.println("SCTP protocol is not supported"); 61 out.println("Test cannot be run"); 62 return; 63 } 64 65 if (args.length == 2) { 66 /* requested to connecct to a specific address */ 67 try { 68 int port = Integer.valueOf(args[1]); 69 address = new InetSocketAddress(args[0], port); 70 } catch (NumberFormatException nfe) { 71 err.println(nfe); 72 } 73 } else { 74 /* start server on local machine, default */ 75 server = new Server(); 76 server.start(); 77 address = server.address(); 78 debug("Server started and listening on " + address); 79 } 80 81 /* many combinations with varing buffer sizes, and offsets */ 82 runWithManyOffsets(address, 20); 83 runWithManyOffsets(address, 49); 84 runWithManyOffsets(address, 50); 85 runWithManyOffsets(address, 51); 86 runWithManyOffsets(address, 1024); 87 } 88 runWithManyOffsets(SocketAddress addr, int bufferSize)89 void runWithManyOffsets(SocketAddress addr, int bufferSize) 90 throws IOException 91 { 92 doTest(addr, bufferSize, 1); 93 doTest(addr, bufferSize, 2); 94 doTest(addr, bufferSize, 3); 95 doTest(addr, bufferSize, 4); 96 doTest(addr, bufferSize, 5); 97 doTest(addr, bufferSize, 6); 98 doTest(addr, bufferSize, 7); 99 doTest(addr, bufferSize, 8); 100 doTest(addr, bufferSize, 9); 101 doTest(addr, bufferSize, 10); 102 doTest(addr, bufferSize, 11); 103 doTest(addr, bufferSize, 12); 104 doTest(addr, bufferSize, 13); 105 doTest(addr, bufferSize, 14); 106 doTest(addr, bufferSize, 15); 107 } 108 doTest(SocketAddress peerAddress, int bufferSize, int bufferOffset)109 void doTest(SocketAddress peerAddress, int bufferSize, int bufferOffset) 110 throws IOException 111 { 112 debug("\n\nTesting with bufferSize " + bufferSize + " and offset " + bufferOffset); 113 assert bufferOffset + msgBytes.length <= bufferSize : 114 "buffer offset + message length greater than buffer size "; 115 116 ByteBuffer buffer = ByteBuffer.allocateDirect(bufferSize); 117 MessageInfo info; 118 119 try (SctpChannel channel = SctpChannel.open()) { 120 channel.connect(peerAddress); 121 122 ReceiveNotificationHandler handler = 123 new ReceiveNotificationHandler(); 124 125 /* TEST 1: Assoc/peer change notif into direct buffer with offest */ 126 do { 127 debug("Test 1: Assoc/peer change with offset " + bufferOffset); 128 buffer.position(bufferOffset); 129 info = channel.receive(buffer, null, handler); 130 if (info == null) { 131 fail("unexpected null from receive"); 132 return; 133 } 134 } while (!info.isComplete()); 135 136 buffer.flip().position(bufferOffset); 137 check(handler.receivedCommUp(), "SCTP_COMM_UP not received"); 138 check(info != null, "info is null"); 139 check(info.address() != null, "address is null"); 140 check(info.association() != null, "association is null"); 141 check(info.isComplete(), "message is not complete"); 142 check(info.isUnordered() != true, 143 "message should not be unordered"); 144 check(info.streamNumber() >= 0, "invalid stream number"); 145 check(info.bytes() == msgBytes.length, 146 "bytes received not equal to message length"); 147 check(info.bytes() == buffer.remaining(), "bytes != remaining"); 148 check(Util.compare(buffer, msgBytes), 149 "received message not the same as sent message"); 150 151 /* TEST 2: shutdown notification with offset */ 152 debug("Test 2: shutdown notif with offset " + bufferOffset); 153 buffer.clear().position(bufferOffset); 154 while ((info = channel.receive(buffer, null, handler )) != null && 155 info.bytes() != -1 ); 156 } 157 } 158 159 class Server implements Runnable 160 { 161 private final InetSocketAddress serverAddr; 162 private final SctpServerChannel ssc; 163 Server()164 public Server() throws IOException { 165 ssc = SctpServerChannel.open().bind(null); 166 java.util.Set<SocketAddress> addrs = ssc.getAllLocalAddresses(); 167 if (addrs.isEmpty()) 168 debug("addrs should not be empty"); 169 170 serverAddr = (InetSocketAddress) addrs.iterator().next(); 171 } 172 start()173 public void start() { 174 (new Thread(this, "Server-" + serverAddr.getPort())).start(); 175 } 176 address()177 public InetSocketAddress address() { 178 return serverAddr; 179 } 180 181 @Override run()182 public void run() { 183 try { 184 for (int i=0; i<NUM_CONNECTIONS; i++) { 185 SctpChannel sc = ssc.accept(); 186 187 /* send a small message */ 188 MessageInfo info = MessageInfo.createOutgoing(null, 0); 189 ByteBuffer buf = ByteBuffer.allocateDirect(Util.SMALL_BUFFER); 190 buf.put(msgBytes); 191 buf.flip(); 192 193 debug("sending small message: " + buf); 194 sc.send(buf, info); 195 196 sc.shutdown(); 197 sc.close(); 198 } 199 } catch (IOException x) { 200 unexpected(x); 201 } finally { 202 try { ssc.close(); } 203 catch (IOException x) { unexpected(x); } 204 } 205 } 206 } 207 208 class ReceiveNotificationHandler extends AbstractNotificationHandler<Object> 209 { 210 boolean receivedCommUp; // false 211 ReceiveNotificationHandler()212 public ReceiveNotificationHandler() { } 213 receivedCommUp()214 public boolean receivedCommUp() { 215 return receivedCommUp; 216 } 217 218 @Override handleNotification( Notification notification, Object attachment)219 public HandlerResult handleNotification( 220 Notification notification, Object attachment) { 221 fail("Unknown notification type"); 222 return HandlerResult.CONTINUE; 223 } 224 225 @Override handleNotification( AssociationChangeNotification notification, Object attachment)226 public HandlerResult handleNotification( 227 AssociationChangeNotification notification, Object attachment) { 228 AssocChangeEvent event = notification.event(); 229 debug("AssociationChangeNotification"); 230 debug(" Association: " + notification.association()); 231 debug(" Event: " + event); 232 233 if (event.equals(AssocChangeEvent.COMM_UP)) 234 receivedCommUp = true; 235 236 return HandlerResult.CONTINUE; 237 } 238 239 @Override handleNotification( PeerAddressChangeNotification pacn, Object unused)240 public HandlerResult handleNotification( 241 PeerAddressChangeNotification pacn, Object unused) 242 { 243 debug("PeerAddressChangeNotification: " + pacn); 244 return HandlerResult.CONTINUE; 245 } 246 247 @Override handleNotification( ShutdownNotification notification, Object attachment)248 public HandlerResult handleNotification( 249 ShutdownNotification notification, Object attachment) { 250 debug("ShutdownNotification"); 251 debug(" Association: " + notification.association()); 252 return HandlerResult.CONTINUE; 253 } 254 } 255 //--------------------- Infrastructure --------------------------- 256 boolean debug = true; 257 volatile int passed = 0, failed = 0; pass()258 void pass() {passed++;} fail()259 void fail() {failed++; Thread.dumpStack();} fail(String msg)260 void fail(String msg) {System.err.println(msg); fail();} unexpected(Throwable t)261 void unexpected(Throwable t) {failed++; t.printStackTrace();} check(boolean cond)262 void check(boolean cond) {if (cond) pass(); else fail();} check(boolean cond, String failMessage)263 void check(boolean cond, String failMessage) {if (cond) pass(); else fail(failMessage);} debug(String message)264 void debug(String message) {if(debug) { 265 System.out.println(Thread.currentThread() + " " + message); } } main(String[] args)266 public static void main(String[] args) throws Throwable { 267 Class<?> k = new Object(){}.getClass().getEnclosingClass(); 268 try {k.getMethod("instanceMain",String[].class) 269 .invoke( k.newInstance(), (Object) args);} 270 catch (Throwable e) {throw e.getCause();}} instanceMain(String[] args)271 public void instanceMain(String[] args) throws Throwable { 272 try {test(args);} catch (Throwable t) {unexpected(t);} 273 System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed); 274 if (failed > 0) throw new AssertionError("Some tests failed");} 275 276 } 277