8061228: Allow JDWP socket connector to accept connections from certain ip addresses only
authordsamersoff
Thu, 31 Aug 2017 21:31:51 -0700
changeset 47121 3aceb4fc0e84
parent 47120 ae1d29b6937f
child 47123 865462469c37
8061228: Allow JDWP socket connector to accept connections from certain ip addresses only Summary: Introduce new parameter for JDWP agent, that allows to restrict connection to certain ip addresses only Reviewed-by: dcubed, clanger, rehn, sspitsyn
jdk/src/jdk.jdwp.agent/share/native/include/jdwpTransport.h
jdk/src/jdk.jdwp.agent/share/native/libdt_socket/socketTransport.c
jdk/src/jdk.jdwp.agent/share/native/libjdwp/debugInit.c
jdk/src/jdk.jdwp.agent/share/native/libjdwp/transport.c
jdk/src/jdk.jdwp.agent/share/native/libjdwp/transport.h
jdk/test/com/sun/jdi/BasicJDWPConnectionTest.java
--- a/jdk/src/jdk.jdwp.agent/share/native/include/jdwpTransport.h	Tue Aug 29 22:15:40 2017 +0200
+++ b/jdk/src/jdk.jdwp.agent/share/native/include/jdwpTransport.h	Thu Aug 31 21:31:51 2017 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -33,7 +33,8 @@
 #include "jni.h"
 
 enum {
-    JDWPTRANSPORT_VERSION_1_0 = 0x00010000
+    JDWPTRANSPORT_VERSION_1_0 = 0x00010000,
+    JDWPTRANSPORT_VERSION_1_1 = 0x00010001
 };
 
 #ifdef __cplusplus
@@ -142,6 +143,13 @@
                                                jint version,
                                                jdwpTransportEnv** env);
 
+/*
+ * JDWP transport configuration from the agent.
+ */
+typedef struct jdwpTransportConfiguration {
+    /* Field added in JDWPTRANSPORT_VERSION_1_1: */
+    const char* allowed_peers;       /* Peers allowed for connection */
+} jdwpTransportConfiguration;
 
 
 /* Function Interface */
@@ -191,6 +199,9 @@
     jdwpTransportError (JNICALL *GetLastError)(jdwpTransportEnv* env,
         char** error);
 
+    /*  12: SetTransportConfiguration added in JDWPTRANSPORT_VERSION_1_1 */
+    jdwpTransportError (JNICALL *SetTransportConfiguration)(jdwpTransportEnv* env,
+        jdwpTransportConfiguration *config);
 };
 
 
@@ -248,6 +259,10 @@
         return functions->GetLastError(this, error);
     }
 
+    /*  SetTransportConfiguration added in JDWPTRANSPORT_VERSION_1_1 */
+    jdwpTransportError SetTransportConfiguration(jdwpTransportEnv* env,
+        return functions->SetTransportConfiguration(this, config);
+    }
 
 #endif /* __cplusplus */
 };
--- a/jdk/src/jdk.jdwp.agent/share/native/libdt_socket/socketTransport.c	Tue Aug 29 22:15:40 2017 +0200
+++ b/jdk/src/jdk.jdwp.agent/share/native/libdt_socket/socketTransport.c	Thu Aug 31 21:31:51 2017 -0700
@@ -34,6 +34,9 @@
 #ifdef _WIN32
  #include <winsock2.h>
  #include <ws2tcpip.h>
+#else
+ #include <arpa/inet.h>
+ #include <sys/socket.h>
 #endif
 
 /*
@@ -73,6 +76,19 @@
 static jint recv_fully(int, char *, int);
 static jint send_fully(int, char *, int);
 
+/* version >= JDWPTRANSPORT_VERSION_1_1 */
+typedef struct {
+    uint32_t subnet;
+    uint32_t netmask;
+} AllowedPeerInfo;
+
+#define STR(x) #x
+#define MAX_PEER_ENTRIES 32
+#define MAX_PEERS_STR STR(MAX_PEER_ENTRIES)
+static AllowedPeerInfo _peers[MAX_PEER_ENTRIES];
+static int _peers_cnt = 0;
+
+
 /*
  * Record the last error for this thread.
  */
@@ -260,7 +276,7 @@
     char *colon;
     int port;
 
-    memset((void *)sa,0,sizeof(struct sockaddr_in));
+    memset((void *)sa, 0, sizeof(struct sockaddr_in));
     sa->sin_family = AF_INET;
 
     /* check for host:port or port */
@@ -274,7 +290,7 @@
     if (colon == NULL) {
         // bind to localhost only if no address specified
         sa->sin_addr.s_addr = getLocalHostAddress();
-    } else if (strncmp(address,"localhost:",10) == 0) {
+    } else if (strncmp(address, "localhost:", 10) == 0) {
         // optimize for common case
         sa->sin_addr.s_addr = getLocalHostAddress();
     } else if (*address == '*' && *(address+1) == ':') {
@@ -286,7 +302,7 @@
         char *hostname;
         uint32_t addr;
 
-        buf = (*callback->alloc)((int)strlen(address)+1);
+        buf = (*callback->alloc)((int)strlen(address) + 1);
         if (buf == NULL) {
             RETURN_ERROR(JDWPTRANSPORT_ERROR_OUT_OF_MEMORY, "out of memory");
         }
@@ -320,6 +336,131 @@
     return JDWPTRANSPORT_ERROR_NONE;
 }
 
+static const char *
+ip_s2u(const char *instr, uint32_t *ip) {
+    // Convert string representation of ip to integer
+    // in network byte order (big-endian)
+    char t[4] = { 0, 0, 0, 0 };
+    const char *s = instr;
+    int i = 0;
+
+    while (1) {
+        if (*s == '.') {
+            ++i;
+            ++s;
+            continue;
+        }
+        if (*s == 0 || *s == '+' || *s == '/') {
+            break;
+        }
+        if (*s < '0' || *s > '9') {
+            return instr;
+        }
+        t[i] = (t[i] * 10) + (*s - '0');
+        ++s;
+    }
+
+    *ip = *(uint32_t*)(t);
+    return s;
+}
+
+static const char *
+mask_s2u(const char *instr, uint32_t *mask) {
+    // Convert the number of bits to a netmask
+    // in network byte order (big-endian)
+    unsigned char m = 0;
+    const char *s = instr;
+
+    while (1) {
+        if (*s == 0 || *s == '+') {
+            break;
+        }
+        if (*s < '0' || *s > '9') {
+            return instr;
+        }
+        m = (m * 10) + (*s - '0');
+        ++s;
+    }
+
+    if (m == 0 || m > 32) {
+       // Drop invalid input
+       return instr;
+    }
+
+    *mask = htonl(-1 << (32 - m));
+    return s;
+}
+
+static int
+ip_in_subnet(uint32_t subnet, uint32_t mask, uint32_t ipaddr) {
+    return (ipaddr & mask) == subnet;
+}
+
+static jdwpTransportError
+parseAllowedPeers(const char *allowed_peers) {
+    // Build a list of allowed peers from char string
+    // of format 192.168.0.10+192.168.0.0/24
+    const char *s = NULL;
+    const char *p = allowed_peers;
+    uint32_t   ip = 0;
+    uint32_t mask = 0xFFFFFFFF;
+
+    while (1) {
+        s = ip_s2u(p, &ip);
+        if (s == p) {
+            _peers_cnt = 0;
+            fprintf(stderr, "Error in allow option: '%s'\n", s);
+            RETURN_ERROR(JDWPTRANSPORT_ERROR_ILLEGAL_ARGUMENT,
+                         "invalid IP address in allow option");
+        }
+
+        if (*s == '/') {
+            // netmask specified
+            s = mask_s2u(s + 1, &mask);
+            if (*(s - 1) == '/') {
+                // Input is not consumed, something bad happened
+                _peers_cnt = 0;
+                fprintf(stderr, "Error in allow option: '%s'\n", s);
+                RETURN_ERROR(JDWPTRANSPORT_ERROR_ILLEGAL_ARGUMENT,
+                             "invalid netmask in allow option");
+            }
+        } else {
+            // reset netmask
+            mask = 0xFFFFFFFF;
+        }
+
+        if (*s == '+' || *s == 0) {
+            if (_peers_cnt >= MAX_PEER_ENTRIES) {
+                fprintf(stderr, "Error in allow option: '%s'\n", allowed_peers);
+                RETURN_ERROR(JDWPTRANSPORT_ERROR_ILLEGAL_ARGUMENT,
+                             "exceeded max number of allowed peers: " MAX_PEERS_STR);
+            }
+            _peers[_peers_cnt].subnet = ip;
+            _peers[_peers_cnt].netmask = mask;
+            _peers_cnt++;
+            if (*s == 0) {
+                // end of options
+                break;
+            }
+            // advance to next IP block
+            p = s + 1;
+        }
+    }
+    return JDWPTRANSPORT_ERROR_NONE;
+}
+
+static int
+isPeerAllowed(struct sockaddr_in *peer) {
+    int i;
+    for (i = 0; i < _peers_cnt; ++i) {
+        int peer_ip = peer->sin_addr.s_addr;
+        if (ip_in_subnet(_peers[i].subnet, _peers[i].netmask, peer_ip)) {
+            return 1;
+        }
+    }
+
+    return 0;
+}
 
 static jdwpTransportError JNICALL
 socketTransport_getCapabilities(jdwpTransportEnv* env,
@@ -412,7 +553,7 @@
 socketTransport_accept(jdwpTransportEnv* env, jlong acceptTimeout, jlong handshakeTimeout)
 {
     socklen_t socketLen;
-    int err;
+    int err = JDWPTRANSPORT_ERROR_NONE;
     struct sockaddr_in socket;
     jlong startTime = (jlong)0;
 
@@ -474,14 +615,34 @@
             return JDWPTRANSPORT_ERROR_IO_ERROR;
         }
 
-        /* handshake with the debugger */
-        err = handshake(socketFD, handshakeTimeout);
+        /*
+         * version >= JDWPTRANSPORT_VERSION_1_1:
+         * Verify that peer is allowed to connect.
+         */
+        if (_peers_cnt > 0) {
+            if (!isPeerAllowed(&socket)) {
+                char ebuf[64] = { 0 };
+                char buf[INET_ADDRSTRLEN] = { 0 };
+                const char* addr_str = inet_ntop(AF_INET, &(socket.sin_addr), buf, INET_ADDRSTRLEN);
+                sprintf(ebuf, "ERROR: Peer not allowed to connect: %s\n",
+                        (addr_str == NULL) ? "<bad address>" : addr_str);
+                dbgsysSocketClose(socketFD);
+                socketFD = -1;
+                err = JDWPTRANSPORT_ERROR_ILLEGAL_ARGUMENT;
+                setLastError(err, ebuf);
+            }
+        }
+
+        if (socketFD > 0) {
+          /* handshake with the debugger */
+          err = handshake(socketFD, handshakeTimeout);
+        }
 
         /*
          * If the handshake fails then close the connection. If there if an accept
          * timeout then we must adjust the timeout for the next poll.
          */
-        if (err) {
+        if (err != JDWPTRANSPORT_ERROR_NONE) {
             fprintf(stderr, "Debugger failed to attach: %s\n", getLastError());
             dbgsysSocketClose(socketFD);
             socketFD = -1;
@@ -743,20 +904,20 @@
     packet->type.cmd.len = length;
 
 
-    n = recv_fully(socketFD,(char *)&(packet->type.cmd.id),sizeof(jint));
+    n = recv_fully(socketFD,(char *)&(packet->type.cmd.id), sizeof(jint));
     if (n < (int)sizeof(jint)) {
         RETURN_RECV_ERROR(n);
     }
 
     packet->type.cmd.id = (jint)dbgsysNetworkToHostLong(packet->type.cmd.id);
 
-    n = recv_fully(socketFD,(char *)&(packet->type.cmd.flags),sizeof(jbyte));
+    n = recv_fully(socketFD,(char *)&(packet->type.cmd.flags), sizeof(jbyte));
     if (n < (int)sizeof(jbyte)) {
         RETURN_RECV_ERROR(n);
     }
 
     if (packet->type.cmd.flags & JDWPTRANSPORT_FLAGS_REPLY) {
-        n = recv_fully(socketFD,(char *)&(packet->type.reply.errorCode),sizeof(jbyte));
+        n = recv_fully(socketFD,(char *)&(packet->type.reply.errorCode), sizeof(jbyte));
         if (n < (int)sizeof(jshort)) {
             RETURN_RECV_ERROR(n);
         }
@@ -765,12 +926,12 @@
 
 
     } else {
-        n = recv_fully(socketFD,(char *)&(packet->type.cmd.cmdSet),sizeof(jbyte));
+        n = recv_fully(socketFD,(char *)&(packet->type.cmd.cmdSet), sizeof(jbyte));
         if (n < (int)sizeof(jbyte)) {
             RETURN_RECV_ERROR(n);
         }
 
-        n = recv_fully(socketFD,(char *)&(packet->type.cmd.cmd),sizeof(jbyte));
+        n = recv_fully(socketFD,(char *)&(packet->type.cmd.cmd), sizeof(jbyte));
         if (n < (int)sizeof(jbyte)) {
             RETURN_RECV_ERROR(n);
         }
@@ -814,11 +975,44 @@
     return JDWPTRANSPORT_ERROR_NONE;
 }
 
+static jdwpTransportError JNICALL
+socketTransport_setConfiguration(jdwpTransportEnv* env, jdwpTransportConfiguration* cfg) {
+    const char* allowed_peers = NULL;
+
+    if (cfg == NULL) {
+        RETURN_ERROR(JDWPTRANSPORT_ERROR_ILLEGAL_ARGUMENT,
+                     "NULL pointer to transport configuration is invalid");
+    }
+    allowed_peers = cfg->allowed_peers;
+    _peers_cnt = 0;
+    if (allowed_peers != NULL) {
+        size_t len = strlen(allowed_peers);
+        if (len == 0) { /* Impossible: parseOptions() would reject it */
+            fprintf(stderr, "Error in allow option: '%s'\n", allowed_peers);
+            RETURN_ERROR(JDWPTRANSPORT_ERROR_ILLEGAL_ARGUMENT,
+                         "allow option should not be empty");
+        } else if (*allowed_peers == '*') {
+            if (len != 1) {
+                fprintf(stderr, "Error in allow option: '%s'\n", allowed_peers);
+                RETURN_ERROR(JDWPTRANSPORT_ERROR_ILLEGAL_ARGUMENT,
+                             "allow option '*' cannot be expanded");
+            }
+        } else {
+            int err = parseAllowedPeers(allowed_peers);
+            if (err != JDWPTRANSPORT_ERROR_NONE) {
+                return err;
+            }
+        }
+    }
+    return JDWPTRANSPORT_ERROR_NONE;
+}
+
 jint JNICALL
 jdwpTransport_OnLoad(JavaVM *vm, jdwpTransportCallback* cbTablePtr,
-                     jint version, jdwpTransportEnv** result)
+                     jint version, jdwpTransportEnv** env)
 {
-    if (version != JDWPTRANSPORT_VERSION_1_0) {
+    if (version < JDWPTRANSPORT_VERSION_1_0 ||
+        version > JDWPTRANSPORT_VERSION_1_1) {
         return JNI_EVERSION;
     }
     if (initialized) {
@@ -842,7 +1036,10 @@
     interface.ReadPacket = &socketTransport_readPacket;
     interface.WritePacket = &socketTransport_writePacket;
     interface.GetLastError = &socketTransport_getLastError;
-    *result = &single_env;
+    if (version >= JDWPTRANSPORT_VERSION_1_1) {
+        interface.SetTransportConfiguration = &socketTransport_setConfiguration;
+    }
+    *env = &single_env;
 
     /* initialized TLS */
     tlsIndex = dbgsysTlsAlloc();
--- a/jdk/src/jdk.jdwp.agent/share/native/libjdwp/debugInit.c	Tue Aug 29 22:15:40 2017 +0200
+++ b/jdk/src/jdk.jdwp.agent/share/native/libjdwp/debugInit.c	Thu Aug 31 21:31:51 2017 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1998, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -89,6 +89,7 @@
     char *name;
     char *address;
     long timeout;
+    char *allow;
 } TransportSpec;
 
 /*
@@ -564,7 +565,8 @@
 
     LOG_MISC(("Begin startTransport"));
     serror = transport_startTransport(enumArg->isServer, transport->name,
-                                     transport->address, transport->timeout);
+                                      transport->address, transport->timeout,
+                                      transport->allow);
     if (serror != JDWP_ERROR(NONE)) {
         ERROR_MESSAGE(("JDWP Transport %s failed to initialize, %s(%d)",
                 transport->name, jdwpErrorText(serror), serror));
@@ -1060,7 +1062,6 @@
         if (transports == NULL) {
             EXIT_ERROR(AGENT_ERROR_OUT_OF_MEMORY,"transports");
         }
-
     }
 
     current = names;
@@ -1080,6 +1081,9 @@
                 goto syntax_error;
             }
             currentTransport->name = current;
+            currentTransport->address = NULL;
+            currentTransport->allow = NULL;
+            currentTransport->timeout = 0L;
             current += strlen(current) + 1;
         } else if (strcmp(buf, "address") == 0) {
             if (currentTransport == NULL) {
@@ -1092,7 +1096,18 @@
             }
             currentTransport->address = current;
             current += strlen(current) + 1;
-        } else if (strcmp(buf, "timeout") == 0) {
+        } else if (strcmp(buf, "allow") == 0) {
+            if (currentTransport == NULL) {
+                errmsg = "allow specified without transport";
+                goto bad_option_with_errmsg;
+            }
+            /*LINTED*/
+            if (!get_tok(&str, current, (int)(end - current), ',')) {
+                goto syntax_error;
+            }
+            currentTransport->allow = current;
+            current += strlen(current) + 1;
+         } else if (strcmp(buf, "timeout") == 0) {
             if (currentTransport == NULL) {
                 errmsg = "timeout specified without transport";
                 goto bad_option_with_errmsg;
--- a/jdk/src/jdk.jdwp.agent/share/native/libjdwp/transport.c	Tue Aug 29 22:15:40 2017 +0200
+++ b/jdk/src/jdk.jdwp.agent/share/native/libjdwp/transport.c	Thu Aug 31 21:31:51 2017 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1998, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -29,7 +29,9 @@
 #include "debugLoop.h"
 #include "sys.h"
 
-static jdwpTransportEnv *transport;
+static jdwpTransportEnv *transport = NULL;
+static unsigned transportVersion = JDWPTRANSPORT_VERSION_1_0;
+
 static jrawMonitorID listenerLock;
 static jrawMonitorID sendLock;
 
@@ -41,6 +43,8 @@
     jdwpTransportEnv *transport;
     char *address;
     long timeout;
+    char *allowed_peers;
+    unsigned transportVersion;
 } TransportInfo;
 
 static struct jdwpTransportCallback callback = {jvmtiAllocate, jvmtiDeallocate};
@@ -135,7 +139,7 @@
  * JDK 1.2 javai.c v1.61
  */
 static jdwpError
-loadTransport(const char *name, jdwpTransportEnv **transportPtr)
+loadTransport(const char *name, TransportInfo *info)
 {
     JNIEnv                 *env;
     jdwpTransport_OnLoad_t  onLoad;
@@ -147,6 +151,10 @@
         ERROR_MESSAGE(("library name is empty"));
         return JDWP_ERROR(TRANSPORT_LOAD);
     }
+    if (info == NULL) {
+        ERROR_MESSAGE(("internal error: info should not be NULL"));
+        return JDWP_ERROR(TRANSPORT_LOAD);
+    }
 
     /* First, look in sun.boot.library.path. This should find the standard
      *  dt_socket and dt_shmem transport libraries, or any library
@@ -192,22 +200,34 @@
 
     /* Get transport interface */
     env = getEnv();
-    if ( env != NULL ) {
-        jdwpTransportEnv *t;
-        JavaVM           *jvm;
-        jint              ver;
+    if (env != NULL) {
+        jdwpTransportEnv *t = NULL;
+        JavaVM           *jvm = NULL;
+        jint              rc;
+        size_t            i;
+        /* If a new version is added here, update 'case JNI_EVERSION' below. */
+        jint supported_versions[2] = {JDWPTRANSPORT_VERSION_1_1, JDWPTRANSPORT_VERSION_1_0};
 
         JNI_FUNC_PTR(env,GetJavaVM)(env, &jvm);
-        ver = (*onLoad)(jvm, &callback, JDWPTRANSPORT_VERSION_1_0, &t);
-        if (ver != JNI_OK) {
-            switch (ver) {
+
+        /* Try version 1.1 first, fallback to 1.0 on error */
+        for (i = 0; i < sizeof(supported_versions); ++i) {
+            rc = (*onLoad)(jvm, &callback, supported_versions[i], &t);
+            if (rc != JNI_EVERSION) {
+                info->transportVersion = supported_versions[i];
+                break;
+            }
+        }
+
+        if (rc != JNI_OK) {
+            switch (rc) {
                 case JNI_ENOMEM :
                     ERROR_MESSAGE(("insufficient memory to complete initialization"));
                     break;
 
                 case JNI_EVERSION :
-                    ERROR_MESSAGE(("transport doesn't recognize version %x",
-                        JDWPTRANSPORT_VERSION_1_0));
+                    ERROR_MESSAGE(("transport doesn't recognize all supported versions: "
+                                   "{ 1_1, 1_0 }"));
                     break;
 
                 case JNI_EEXIST :
@@ -215,13 +235,19 @@
                     break;
 
                 default:
-                    ERROR_MESSAGE(("unrecognized error %d from transport", ver));
+                    ERROR_MESSAGE(("unrecognized error %d from transport", rc));
                     break;
             }
 
             return JDWP_ERROR(TRANSPORT_INIT);
         }
-        *transportPtr = t;
+
+        /* Store transport version to global variable to be able to
+         * set correct transport version for subsequent connect,
+         * even if info is already deallocated.
+         */
+        transportVersion = info->transportVersion;
+        info->transport = t;
     } else {
         return JDWP_ERROR(TRANSPORT_LOAD);
     }
@@ -314,7 +340,6 @@
 
     info = (TransportInfo*)(void*)arg;
     t = info->transport;
-
     rc = (*t)->Accept(t, info->timeout, 0);
 
     /* System property no longer needed */
@@ -339,8 +364,10 @@
 static void JNICALL
 attachThread(jvmtiEnv* jvmti_env, JNIEnv* jni_env, void* arg)
 {
+    TransportInfo *info = (TransportInfo*)(void*)arg;
+
     LOG_MISC(("Begin attach thread"));
-    connectionInitiated((jdwpTransportEnv *)(void*)arg);
+    connectionInitiated(info->transport);
     LOG_MISC(("End attach thread"));
 }
 
@@ -418,13 +445,26 @@
 
 jdwpError
 transport_startTransport(jboolean isServer, char *name, char *address,
-                         long timeout)
+                         long timeout, char *allowed_peers)
 {
     jvmtiStartFunction func;
-    jdwpTransportEnv *trans;
     char threadName[MAXPATHLEN + 100];
     jint err;
     jdwpError serror;
+    jdwpTransportConfiguration cfg = {0};
+    TransportInfo *info;
+    jdwpTransportEnv *trans;
+
+    info = jvmtiAllocate(sizeof(*info));
+    if (info == NULL) {
+        return JDWP_ERROR(OUT_OF_MEMORY);
+    }
+
+    info->transport = transport;
+    info->transportVersion = transportVersion;
+    info->name = NULL;
+    info->address = NULL;
+    info->allowed_peers = NULL;
 
     /*
      * If the transport is already loaded then use it
@@ -434,28 +474,24 @@
      * That probably means we have a bag a transport environments
      * to correspond to the transports bag.
      */
-    if (transport != NULL) {
-        trans = transport;
-    } else {
-        serror = loadTransport(name, &trans);
+    if (info->transport == NULL) {
+        serror = loadTransport(name, info);
         if (serror != JDWP_ERROR(NONE)) {
+            jvmtiDeallocate(info);
             return serror;
         }
     }
 
+    // Cache the value
+    trans = info->transport;
+
     if (isServer) {
-
         char *retAddress;
         char *launchCommand;
-        TransportInfo *info;
         jvmtiError error;
         int len;
         char* prop_value;
 
-        info = jvmtiAllocate(sizeof(*info));
-        if (info == NULL) {
-            return JDWP_ERROR(OUT_OF_MEMORY);
-        }
         info->timeout = timeout;
 
         info->name = jvmtiAllocate((int)strlen(name)+1);
@@ -465,7 +501,6 @@
         }
         (void)strcpy(info->name, name);
 
-        info->address = NULL;
         if (address != NULL) {
             info->address = jvmtiAllocate((int)strlen(address)+1);
             if (info->address == NULL) {
@@ -475,7 +510,32 @@
             (void)strcpy(info->address, address);
         }
 
-        info->transport = trans;
+        if (info->transportVersion == JDWPTRANSPORT_VERSION_1_0) {
+            if (allowed_peers != NULL) {
+                ERROR_MESSAGE(("Allow parameter is specified but transport doesn't support it"));
+                serror = JDWP_ERROR(TRANSPORT_INIT);
+                goto handleError;
+            }
+        } else {
+            /* Memory is allocated only for transport versions > 1.0
+             * as the version 1.0 does not support the 'allow' option.
+             */
+            if (allowed_peers != NULL) {
+                info->allowed_peers = jvmtiAllocate((int)strlen(allowed_peers) + 1);
+                if (info->allowed_peers == NULL) {
+                    serror = JDWP_ERROR(OUT_OF_MEMORY);
+                    goto handleError;
+                }
+                (void)strcpy(info->allowed_peers, allowed_peers);
+            }
+            cfg.allowed_peers = info->allowed_peers;
+            err = (*trans)->SetTransportConfiguration(trans, &cfg);
+            if (err != JDWPTRANSPORT_ERROR_NONE) {
+                printLastError(trans, err);
+                serror = JDWP_ERROR(TRANSPORT_INIT);
+                goto handleError;
+            }
+        }
 
         err = (*trans)->StartListening(trans, address, &retAddress);
         if (err != JDWPTRANSPORT_ERROR_NONE) {
@@ -527,6 +587,7 @@
 handleError:
         jvmtiDeallocate(info->name);
         jvmtiDeallocate(info->address);
+        jvmtiDeallocate(info->allowed_peers);
         jvmtiDeallocate(info);
     } else {
         /*
@@ -543,6 +604,10 @@
          if (err != JDWPTRANSPORT_ERROR_NONE) {
              printLastError(trans, err);
              serror = JDWP_ERROR(TRANSPORT_INIT);
+             /* The name, address and allowed_peers fields in 'info'
+              * are not allocated in the non-server case so
+              * they do not need to be freed. */
+             jvmtiDeallocate(info);
              return serror;
          }
 
@@ -553,7 +618,7 @@
          (void)strcat(threadName, name);
 
          func = &attachThread;
-         err = spawnNewThread(func, (void*)trans, threadName);
+         err = spawnNewThread(func, (void*)info, threadName);
          serror = map2jdwpError(err);
     }
     return serror;
--- a/jdk/src/jdk.jdwp.agent/share/native/libjdwp/transport.h	Tue Aug 29 22:15:40 2017 +0200
+++ b/jdk/src/jdk.jdwp.agent/share/native/libjdwp/transport.h	Thu Aug 31 21:31:51 2017 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1998, 2003, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -30,7 +30,8 @@
 
 void transport_initialize(void);
 void transport_reset(void);
-jdwpError transport_startTransport(jboolean isServer, char *name, char *address, long timeout);
+jdwpError transport_startTransport(jboolean isServer, char *name, char *address,
+                                   long timeout, char *allowed_peers);
 
 jint transport_receivePacket(jdwpPacket *);
 jint transport_sendPacket(jdwpPacket *);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/com/sun/jdi/BasicJDWPConnectionTest.java	Thu Aug 31 21:31:51 2017 -0700
@@ -0,0 +1,200 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @summary Smoke test for JDWP hardening
+ * @library /lib/testlibrary
+ * @library /test/lib
+ * @run driver BasicJDWPConnectionTest
+ */
+
+import java.io.IOException;
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+
+import java.net.Socket;
+import java.net.SocketException;
+
+import jdk.test.lib.apps.LingeredApp;
+import jdk.testlibrary.Utils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+public class BasicJDWPConnectionTest {
+
+    public static int handshake(int port) throws IOException {
+        // Connect to the debuggee and handshake
+        int res = -1;
+        Socket s = null;
+        try {
+            s = new Socket("localhost", port);
+            s.getOutputStream().write("JDWP-Handshake".getBytes("UTF-8"));
+            byte[] buffer = new byte[24];
+            res = s.getInputStream().read(buffer);
+        }
+        catch (SocketException ex) {
+            // pass
+        } finally {
+            if (s != null) {
+                s.close();
+            }
+        }
+        return res;
+    }
+
+    public static ArrayList<String> prepareCmd(int port, String allowOpt) {
+         String address = "*:" + String.valueOf(port);
+         ArrayList<String> cmd = new ArrayList<>();
+
+         String jdwpArgs = "-agentlib:jdwp=transport=dt_socket,server=y," +
+                           "suspend=n,address=" + address + allowOpt;
+         cmd.add(jdwpArgs);
+         return cmd;
+    }
+
+    public static void positiveTest(String testName, String allowOpt)
+        throws InterruptedException, IOException {
+        System.err.println("\nStarting " + testName);
+        int port = Utils.getFreePort();
+        ArrayList<String> cmd = prepareCmd(port, allowOpt);
+
+        LingeredApp a = LingeredApp.startApp(cmd);
+        int res = handshake(port);
+        a.stopApp();
+        if (res < 0) {
+            throw new RuntimeException(testName + " FAILED");
+        }
+        System.err.println(testName + " PASSED");
+    }
+
+    public static void negativeTest(String testName, String allowOpt)
+        throws InterruptedException, IOException {
+        System.err.println("\nStarting " + testName);
+        int port = Utils.getFreePort();
+        ArrayList<String> cmd = prepareCmd(port, allowOpt);
+
+        LingeredApp a = LingeredApp.startApp(cmd);
+        int res = handshake(port);
+        a.stopApp();
+        if (res > 0) {
+            System.err.println(testName + ": res=" + res);
+            throw new RuntimeException(testName + " FAILED");
+        }
+        System.err.println(testName + ": returned a negative code as expected: " + res);
+        System.err.println(testName + " PASSED");
+    }
+
+    public static void badAllowOptionTest(String testName, String allowOpt)
+        throws InterruptedException, IOException {
+        System.err.println("\nStarting " + testName);
+        int port = Utils.getFreePort();
+        ArrayList<String> cmd = prepareCmd(port, allowOpt);
+
+        try {
+            LingeredApp a = LingeredApp.startApp(cmd);
+        } catch (IOException ex) {
+            System.err.println(testName + ": caught expected IOException");
+            System.err.println(testName + " PASSED");
+            return;
+        }
+        throw new RuntimeException(testName + " FAILED");
+    }
+
+    public static void DefaultTest() throws InterruptedException, IOException {
+        // No allow option is the same as the allow option ',allow=*' is passed
+        String allowOpt = "";
+        positiveTest("DefaultTest", allowOpt);
+    }
+
+    static void ExplicitDefaultTest() throws InterruptedException, IOException {
+        // Explicit permission for connections from everywhere
+        String allowOpt = ",allow=*";
+        positiveTest("ExplicitDefaultTest" ,allowOpt);
+    }
+
+    public static void AllowTest() throws InterruptedException, IOException {
+        String allowOpt = ",allow=127.0.0.1";
+        positiveTest("AllowTest", allowOpt);
+    }
+
+    public static void MultiAllowTest() throws InterruptedException, IOException {
+        String allowOpt = ",allow=127.0.0.1+10.0.0.0/8+172.16.0.0/12+192.168.0.0/24";
+        positiveTest("MultiAllowTest", allowOpt);
+    }
+
+    public static void DenyTest() throws InterruptedException, IOException {
+        // Bad allow address
+        String allowOpt = ",allow=0.0.0.0";
+        negativeTest("DenyTest", allowOpt);
+    }
+
+    public static void MultiDenyTest() throws InterruptedException, IOException {
+        // Wrong separator ';' is used for allow option
+        String allowOpt = ",allow=127.0.0.1;192.168.0.0/24";
+        badAllowOptionTest("MultiDenyTest", allowOpt);
+    }
+
+    public static void EmptyAllowOptionTest() throws InterruptedException, IOException {
+        // Empty allow option
+        String allowOpt = ",allow=";
+        badAllowOptionTest("EmptyAllowOptionTest", allowOpt);
+    }
+
+    public static void ExplicitMultiDefault1Test() throws InterruptedException, IOException {
+        // Bad mix of allow option '*' with address value
+        String allowOpt = ",allow=*+allow=127.0.0.1";
+        badAllowOptionTest("ExplicitMultiDefault1Test", allowOpt);
+    }
+
+    public static void ExplicitMultiDefault2Test() throws InterruptedException, IOException {
+        // Bad mix of allow address value with '*'
+        String allowOpt = ",allow=allow=127.0.0.1+*";
+        badAllowOptionTest("ExplicitMultiDefault2Test", allowOpt);
+    }
+
+    public static void main(String[] args) {
+        try {
+            DefaultTest();
+            ExplicitDefaultTest();
+            AllowTest();
+            MultiAllowTest();
+            DenyTest();
+            MultiDenyTest();
+            EmptyAllowOptionTest();
+            ExplicitMultiDefault1Test();
+            ExplicitMultiDefault2Test();
+            System.err.println("\nTest PASSED");
+        } catch (InterruptedException ex) {
+            System.err.println("\nTest ERROR, getFreePort");
+            ex.printStackTrace();
+            System.exit(3);
+        } catch (IOException ex) {
+            System.err.println("\nTest ERROR");
+            ex.printStackTrace();
+            System.exit(3);
+        }
+    }
+}