Bug 1460617 - land NSS 328d235fc7ee UPGRADE_NSS_RELEASE, r=me
authorJ.C. Jones <jjones@mozilla.com>
Thu, 24 May 2018 08:08:55 -0700
changeset 419708 4816f8eef0ab
parent 419707 c106211c6ed5
child 419709 607663065b17
push id103590
push userjjones@mozilla.com
push dateThu, 24 May 2018 18:03:12 +0000
treeherdermozilla-inbound@4816f8eef0ab [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersme
bugs1460617
milestone62.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1460617 - land NSS 328d235fc7ee UPGRADE_NSS_RELEASE, r=me
security/nss/TAG-INFO
security/nss/cmd/tstclnt/tstclnt.c
security/nss/coreconf/coreconf.dep
security/nss/lib/ssl/sslsecur.c
security/nss/lib/ssl/sslsock.c
security/nss/lib/util/secasn1d.c
--- a/security/nss/TAG-INFO
+++ b/security/nss/TAG-INFO
@@ -1,1 +1,1 @@
-c8ee333b84a0
+328d235fc7ee
--- a/security/nss/cmd/tstclnt/tstclnt.c
+++ b/security/nss/cmd/tstclnt/tstclnt.c
@@ -46,16 +46,17 @@
     if (verbose) \
     printf
 #define FPRINTF  \
     if (verbose) \
     fprintf
 
 #define MAX_WAIT_FOR_SERVER 600
 #define WAIT_INTERVAL 100
+#define ZERO_RTT_MAX (2 << 16)
 
 #define EXIT_CODE_HANDSHAKE_FAILED 254
 
 #define EXIT_CODE_SIDECHANNELTEST_GOOD 0
 #define EXIT_CODE_SIDECHANNELTEST_BADCERT 1
 #define EXIT_CODE_SIDECHANNELTEST_NODATA 2
 #define EXIT_CODE_SIDECHANNELTEST_REVOKED 3
 
@@ -94,16 +95,17 @@ int ssl3CipherSuites[] = {
 unsigned long __cmp_umuls;
 PRBool verbose;
 int dumpServerChain = 0;
 int renegotiationsToDo = 0;
 int renegotiationsDone = 0;
 PRBool initializedServerSessionCache = PR_FALSE;
 
 static char *progName;
+static const char *requestFile;
 
 secuPWData pwdata = { PW_NONE, 0 };
 
 SSLNamedGroup *enabledGroups = NULL;
 unsigned int enabledGroupsCount = 0;
 
 void
 printSecurityInfo(PRFileDesc *fd)
@@ -706,22 +708,28 @@ own_GetClientAuthData(void *arg,
     return NSS_GetClientAuthData(arg, socket, caNames, pRetCert, pRetKey);
 }
 
 #if defined(WIN32) || defined(OS2)
 void
 thread_main(void *arg)
 {
     PRFileDesc *ps = (PRFileDesc *)arg;
-    PRFileDesc *std_in = PR_GetSpecialFD(PR_StandardInput);
+    PRFileDesc *std_in;
     int wc, rc;
     char buf[256];
 
+    if (requestFile) {
+        std_in = PR_Open(requestFile, PR_RDONLY, 0);
+    } else {
+        std_in = PR_GetSpecialFD(PR_StandardInput);
+    }
+
 #ifdef WIN32
-    {
+    if (!requestFile) {
         /* Put stdin into O_BINARY mode
         ** or else incoming \r\n's will become \n's.
         */
         int smrv = _setmode(_fileno(stdin), _O_BINARY);
         if (smrv == -1) {
             fprintf(stderr,
                     "%s: Cannot change stdin to binary mode. Use -i option instead.\n",
                     progName);
@@ -732,16 +740,19 @@ thread_main(void *arg)
 
     do {
         rc = PR_Read(std_in, buf, sizeof buf);
         if (rc <= 0)
             break;
         wc = PR_Send(ps, buf, rc, 0, maxInterval);
     } while (wc == rc);
     PR_Close(ps);
+    if (requestFile) {
+        PR_Close(std_in);
+    }
 }
 
 #endif
 
 static void
 printHostNameAndAddr(const char *host, const PRNetAddr *addr)
 {
     PRUint16 port = PR_NetAddrInetPort(addr);
@@ -910,51 +921,59 @@ PRBool pingServerFirst = PR_FALSE;
 int pingTimeoutSeconds = -1;
 PRBool clientSpeaksFirst = PR_FALSE;
 PRBool skipProtoHeader = PR_FALSE;
 ServerCertAuth serverCertAuth;
 char *hs1SniHostName = NULL;
 char *hs2SniHostName = NULL;
 PRUint16 portno = 443;
 int override = 0;
-char *requestString = NULL;
-PRInt32 requestStringLen = 0;
-PRBool requestSent = PR_FALSE;
 PRBool enableZeroRtt = PR_FALSE;
+PRUint8 *zeroRttData;
+unsigned int zeroRttLen = 0;
 PRBool enableAltServerHello = PR_FALSE;
 PRBool useDTLS = PR_FALSE;
 PRBool actAsServer = PR_FALSE;
 PRBool stopAfterHandshake = PR_FALSE;
 PRBool requestToExit = PR_FALSE;
 char *versionString = NULL;
+PRBool handshakeComplete = PR_FALSE;
 
 static int
-writeBytesToServer(PRFileDesc *s, const char *buf, int nb)
+writeBytesToServer(PRFileDesc *s, const PRUint8 *buf, int nb)
 {
     SECStatus rv;
-    const char *bufp = buf;
+    const PRUint8 *bufp = buf;
     PRPollDesc pollDesc;
 
     pollDesc.in_flags = PR_POLL_WRITE | PR_POLL_EXCEPT;
     pollDesc.out_flags = 0;
     pollDesc.fd = s;
 
     FPRINTF(stderr, "%s: Writing %d bytes to server\n",
             progName, nb);
     do {
         PRInt32 cc = PR_Send(s, bufp, nb, 0, maxInterval);
         if (cc < 0) {
             PRErrorCode err = PR_GetError();
             if (err != PR_WOULD_BLOCK_ERROR) {
-                SECU_PrintError(progName,
-                                "write to SSL socket failed");
+                SECU_PrintError(progName, "write to SSL socket failed");
                 return 254;
             }
             cc = 0;
         }
+        FPRINTF(stderr, "%s: %d bytes written\n", progName, cc);
+        if (enableZeroRtt && !handshakeComplete) {
+            if (zeroRttLen + cc > ZERO_RTT_MAX) {
+                SECU_PrintError(progName, "too much early data to save");
+                return -1;
+            }
+            PORT_Memcpy(zeroRttData + zeroRttLen, bufp, cc);
+            zeroRttLen += cc;
+        }
         bufp += cc;
         nb -= cc;
         if (nb <= 0)
             break;
 
         rv = restartHandshakeAfterServerCertIfNeeded(s,
                                                      &serverCertAuth, override);
         if (rv != SECSuccess) {
@@ -964,18 +983,17 @@ writeBytesToServer(PRFileDesc *s, const 
 
         pollDesc.in_flags = PR_POLL_WRITE | PR_POLL_EXCEPT;
         pollDesc.out_flags = 0;
         FPRINTF(stderr,
                 "%s: about to call PR_Poll on writable socket !\n",
                 progName);
         cc = PR_Poll(&pollDesc, 1, PR_INTERVAL_NO_TIMEOUT);
         if (cc < 0) {
-            SECU_PrintError(progName,
-                            "PR_Poll failed");
+            SECU_PrintError(progName, "PR_Poll failed");
             return -1;
         }
         FPRINTF(stderr,
                 "%s: PR_Poll returned with writable socket !\n",
                 progName);
     } while (1);
 
     return 0;
@@ -988,37 +1006,38 @@ handshakeCallback(PRFileDesc *fd, void *
     if (secondHandshakeName) {
         SSL_SetURL(fd, secondHandshakeName);
     }
     printSecurityInfo(fd);
     if (renegotiationsDone < renegotiationsToDo) {
         SSL_ReHandshake(fd, (renegotiationsToDo < 2));
         ++renegotiationsDone;
     }
-    if (requestString && requestSent) {
+    if (zeroRttLen) {
         /* This data was sent in 0-RTT. */
         SSLChannelInfo info;
         SECStatus rv;
 
         rv = SSL_GetChannelInfo(fd, &info, sizeof(info));
         if (rv != SECSuccess)
             return;
 
         if (!info.earlyDataAccepted) {
-            FPRINTF(stderr, "Early data rejected. Re-sending\n");
-            writeBytesToServer(fd, requestString, requestStringLen);
+            FPRINTF(stderr, "Early data rejected. Re-sending %d bytes\n",
+                    zeroRttLen);
+            writeBytesToServer(fd, zeroRttData, zeroRttLen);
+            zeroRttLen = 0;
         }
     }
     if (stopAfterHandshake) {
         requestToExit = PR_TRUE;
     }
+    handshakeComplete = PR_TRUE;
 }
 
-#define REQUEST_WAITING (requestString && !requestSent)
-
 static SECStatus
 installServerCertificate(PRFileDesc *s, char *nick)
 {
     CERTCertificate *cert;
     SECKEYPrivateKey *privKey = NULL;
 
     if (!nick) {
         PORT_SetError(SEC_ERROR_INVALID_ARGS);
@@ -1131,23 +1150,22 @@ connectToServer(PRFileDesc *s, PRPollDes
 static int
 run()
 {
     int headerSeparatorPtrnId = 0;
     int error = 0;
     SECStatus rv;
     PRStatus status;
     PRInt32 filesReady;
-    int npds;
     PRFileDesc *s = NULL;
     PRFileDesc *std_out;
     PRPollDesc pollset[2];
     PRBool wrStarted = PR_FALSE;
 
-    requestSent = PR_FALSE;
+    handshakeComplete = PR_FALSE;
 
     /* Create socket */
     if (useDTLS) {
         s = PR_OpenUDPSocket(addr.raw.family);
     } else {
         s = PR_OpenTCPSocket(addr.raw.family);
     }
 
@@ -1388,35 +1406,39 @@ run()
         rv = SSL_ResetHandshake(s, PR_TRUE /* server */);
         if (rv != SECSuccess) {
             return 1;
         }
     } else {
         /* Try to connect to the server */
         rv = connectToServer(s, pollset);
         if (rv != SECSuccess) {
-            ;
             error = 1;
             goto done;
         }
     }
 
     pollset[SSOCK_FD].fd = s;
     pollset[SSOCK_FD].in_flags = PR_POLL_EXCEPT;
     if (!actAsServer)
         pollset[SSOCK_FD].in_flags |= (clientSpeaksFirst ? 0 : PR_POLL_READ);
     else
         pollset[SSOCK_FD].in_flags |= PR_POLL_READ;
-    pollset[STDIN_FD].fd = PR_GetSpecialFD(PR_StandardInput);
-    if (!REQUEST_WAITING) {
-        pollset[STDIN_FD].in_flags = PR_POLL_READ;
-        npds = 2;
+    if (requestFile) {
+        pollset[STDIN_FD].fd = PR_Open(requestFile, PR_RDONLY, 0);
+        if (!pollset[STDIN_FD].fd) {
+            fprintf(stderr, "%s: unable to open input file: %s\n",
+                    progName, requestFile);
+            error = 1;
+            goto done;
+        }
     } else {
-        npds = 1;
+        pollset[STDIN_FD].fd = PR_GetSpecialFD(PR_StandardInput);
     }
+    pollset[STDIN_FD].in_flags = PR_POLL_READ;
     std_out = PR_GetSpecialFD(PR_StandardOutput);
 
 #if defined(WIN32) || defined(OS2)
     /* PR_Poll cannot be used with stdin on Windows or OS/2.  (sigh).
     ** But use of PR_Poll and non-blocking sockets is a major feature
     ** of this program.  So, we simulate a pollable stdin with a
     ** TCP socket pair and a  thread that reads stdin and writes to
     ** that socket pair.
@@ -1452,34 +1474,34 @@ run()
 
     /*
     ** Select on stdin and on the socket. Write data from stdin to
     ** socket, read data from socket and write to stdout.
     */
     requestToExit = PR_FALSE;
     FPRINTF(stderr, "%s: ready...\n", progName);
     while (!requestToExit &&
-           ((pollset[SSOCK_FD].in_flags | pollset[STDIN_FD].in_flags) ||
-            REQUEST_WAITING)) {
-        char buf[4000]; /* buffer for stdin */
-        int nb;         /* num bytes read from stdin. */
+           (pollset[SSOCK_FD].in_flags || pollset[STDIN_FD].in_flags)) {
+        PRUint8 buf[4000]; /* buffer for stdin */
+        int nb;            /* num bytes read from stdin. */
 
         rv = restartHandshakeAfterServerCertIfNeeded(s, &serverCertAuth,
                                                      override);
         if (rv != SECSuccess) {
             error = EXIT_CODE_HANDSHAKE_FAILED;
             SECU_PrintError(progName, "authentication of server cert failed");
             goto done;
         }
 
         pollset[SSOCK_FD].out_flags = 0;
         pollset[STDIN_FD].out_flags = 0;
 
         FPRINTF(stderr, "%s: about to call PR_Poll !\n", progName);
-        filesReady = PR_Poll(pollset, npds, PR_INTERVAL_NO_TIMEOUT);
+        filesReady = PR_Poll(pollset, PR_ARRAY_SIZE(pollset),
+                             PR_INTERVAL_NO_TIMEOUT);
         if (filesReady < 0) {
             SECU_PrintError(progName, "select failed");
             error = 1;
             goto done;
         }
         if (filesReady == 0) { /* shouldn't happen! */
             FPRINTF(stderr, "%s: PR_Poll returned zero!\n", progName);
             error = 1;
@@ -1491,125 +1513,87 @@ run()
                     "%s: PR_Poll returned 0x%02x for stdin out_flags.\n",
                     progName, pollset[STDIN_FD].out_flags);
         }
         if (pollset[SSOCK_FD].in_flags) {
             FPRINTF(stderr,
                     "%s: PR_Poll returned 0x%02x for socket out_flags.\n",
                     progName, pollset[SSOCK_FD].out_flags);
         }
-        if (REQUEST_WAITING) {
-            error = writeBytesToServer(s, requestString, requestStringLen);
-            if (error) {
-                goto done;
-            }
-            requestSent = PR_TRUE;
-            pollset[SSOCK_FD].in_flags = PR_POLL_READ;
-        }
         if (pollset[STDIN_FD].out_flags & PR_POLL_READ) {
             /* Read from stdin and write to socket */
             nb = PR_Read(pollset[STDIN_FD].fd, buf, sizeof(buf));
             FPRINTF(stderr, "%s: stdin read %d bytes\n", progName, nb);
             if (nb < 0) {
                 if (PR_GetError() != PR_WOULD_BLOCK_ERROR) {
                     SECU_PrintError(progName, "read from stdin failed");
                     error = 1;
                     break;
                 }
             } else if (nb == 0) {
                 /* EOF on stdin, stop polling stdin for read. */
                 pollset[STDIN_FD].in_flags = 0;
+                if (actAsServer)
+                    requestToExit = PR_TRUE;
             } else {
                 error = writeBytesToServer(s, buf, nb);
                 if (error) {
                     goto done;
                 }
                 pollset[SSOCK_FD].in_flags = PR_POLL_READ;
             }
         }
 
         if (pollset[SSOCK_FD].in_flags) {
             FPRINTF(stderr,
                     "%s: PR_Poll returned 0x%02x for socket out_flags.\n",
                     progName, pollset[SSOCK_FD].out_flags);
         }
-        if ((pollset[SSOCK_FD].out_flags & PR_POLL_READ) ||
-            (pollset[SSOCK_FD].out_flags & PR_POLL_ERR)
 #ifdef PR_POLL_HUP
-            || (pollset[SSOCK_FD].out_flags & PR_POLL_HUP)
+#define POLL_RECV_FLAGS (PR_POLL_READ | PR_POLL_ERR | PR_POLL_HUP)
+#else
+#define POLL_RECV_FLAGS (PR_POLL_READ | PR_POLL_ERR)
 #endif
-                ) {
+        if (pollset[SSOCK_FD].out_flags & POLL_RECV_FLAGS) {
             /* Read from socket and write to stdout */
             nb = PR_Recv(pollset[SSOCK_FD].fd, buf, sizeof buf, 0, maxInterval);
             FPRINTF(stderr, "%s: Read from server %d bytes\n", progName, nb);
             if (nb < 0) {
                 if (PR_GetError() != PR_WOULD_BLOCK_ERROR) {
                     SECU_PrintError(progName, "read from socket failed");
                     error = 1;
                     goto done;
                 }
             } else if (nb == 0) {
                 /* EOF from socket... stop polling socket for read */
                 pollset[SSOCK_FD].in_flags = 0;
             } else {
                 if (skipProtoHeader != PR_TRUE || wrStarted == PR_TRUE) {
                     PR_Write(std_out, buf, nb);
                 } else {
-                    separateReqHeader(std_out, buf, nb, &wrStarted,
+                    separateReqHeader(std_out, (char *)buf, nb, &wrStarted,
                                       &headerSeparatorPtrnId);
                 }
                 if (verbose)
                     fputs("\n\n", stderr);
             }
         }
         milliPause(50 * multiplier);
     }
 
 done:
     if (s) {
         PR_Close(s);
     }
-
+    if (requestFile) {
+        PR_Close(pollset[STDIN_FD].fd);
+    }
     return error;
 }
 
-PRInt32
-ReadFile(const char *filename, char **data)
-{
-    char *ret = NULL;
-    char buf[8192];
-    unsigned int len = 0;
-    PRStatus rv;
-
-    PRFileDesc *fd = PR_Open(filename, PR_RDONLY, 0);
-    if (!fd)
-        return -1;
-
-    for (;;) {
-        rv = PR_Read(fd, buf, sizeof(buf));
-        if (rv < 0) {
-            PR_Free(ret);
-            return rv;
-        }
-
-        if (!rv)
-            break;
-
-        ret = PR_Realloc(ret, len + rv);
-        if (!ret) {
-            return -1;
-        }
-        PORT_Memcpy(ret + len, buf, rv);
-        len += rv;
-    }
-
-    *data = ret;
-    return len;
-}
-
 int
 main(int argc, char **argv)
 {
     PLOptState *optstate;
     PLOptStatus optstatus;
     PRStatus status;
     PRStatus prStatus;
     int error = 0;
@@ -1662,21 +1646,17 @@ main(int argc, char **argv)
                 break;
             case '6':
                 allowIPv4 = PR_FALSE;
                 if (!allowIPv6)
                     Usage();
                 break;
 
             case 'A':
-                requestStringLen = ReadFile(optstate->value, &requestString);
-                if (requestStringLen < 0) {
-                    fprintf(stderr, "Couldn't read file %s\n", optstate->value);
-                    exit(1);
-                }
+                requestFile = PORT_Strdup(optstate->value);
                 break;
 
             case 'C':
                 ++dumpServerChain;
                 break;
 
             case 'D':
                 openDB = PR_FALSE;
@@ -1772,16 +1752,21 @@ main(int argc, char **argv)
                 break;
             case 'Y':
                 PrintCipherUsage();
                 exit(0);
                 break;
 
             case 'Z':
                 enableZeroRtt = PR_TRUE;
+                zeroRttData = PORT_ZAlloc(ZERO_RTT_MAX);
+                if (!zeroRttData) {
+                    fprintf(stderr, "Unable to allocate buffer for 0-RTT\n");
+                    exit(1);
+                }
                 break;
 
             case 'a':
                 if (!hs1SniHostName) {
                     hs1SniHostName = PORT_Strdup(optstate->value);
                 } else if (!hs2SniHostName) {
                     hs2SniHostName = PORT_Strdup(optstate->value);
                 } else {
@@ -2054,30 +2039,23 @@ main(int argc, char **argv)
         }
     }
 
 done:
     if (s) {
         PR_Close(s);
     }
 
-    if (hs1SniHostName) {
-        PORT_Free(hs1SniHostName);
-    }
-    if (hs2SniHostName) {
-        PORT_Free(hs2SniHostName);
-    }
-    if (nickname) {
-        PORT_Free(nickname);
-    }
-    if (pwdata.data) {
-        PORT_Free(pwdata.data);
-    }
+    PORT_Free((void *)requestFile);
+    PORT_Free(hs1SniHostName);
+    PORT_Free(hs2SniHostName);
+    PORT_Free(nickname);
+    PORT_Free(pwdata.data);
     PORT_Free(host);
-    PORT_Free(requestString);
+    PORT_Free(zeroRttData);
 
     if (enabledGroups) {
         PORT_Free(enabledGroups);
     }
     if (NSS_IsInitialized()) {
         SSL_ClearSessionCache();
         if (initializedServerSessionCache) {
             if (SSL_ShutdownServerSessionIDCache() != SECSuccess) {
--- a/security/nss/coreconf/coreconf.dep
+++ b/security/nss/coreconf/coreconf.dep
@@ -5,8 +5,9 @@
 
 /*
  * A dummy header file that is a dependency for all the object files.
  * Used to force a full recompilation of NSS in Mozilla's Tinderbox
  * depend builds.  See comments in rules.mk.
  */
 
 #error "Do not include this header file."
+
--- a/security/nss/lib/ssl/sslsecur.c
+++ b/security/nss/lib/ssl/sslsecur.c
@@ -917,31 +917,40 @@ ssl_SecureSend(sslSocket *ss, const unsi
 
     /* Check to see if we can write even though we're not finished.
      *
      * Case 1: False start
      * Case 2: TLS 1.3 0-RTT
      */
     if (!ss->firstHsDone) {
         PRBool allowEarlySend = PR_FALSE;
+        PRBool firstClientWrite = PR_FALSE;
 
         ssl_Get1stHandshakeLock(ss);
-        if (ss->opt.enableFalseStart ||
-            (ss->opt.enable0RttData && !ss->sec.isServer)) {
+        /* The client can sometimes send before the handshake is fully
+         * complete. In TLS 1.2: false start; in TLS 1.3: 0-RTT. */
+        if (!ss->sec.isServer &&
+            (ss->opt.enableFalseStart || ss->opt.enable0RttData)) {
             ssl_GetSSL3HandshakeLock(ss);
-            /* The client can sometimes send before the handshake is fully
-             * complete. In TLS 1.2: false start; in TLS 1.3: 0-RTT. */
             zeroRtt = ss->ssl3.hs.zeroRttState == ssl_0rtt_sent ||
                       ss->ssl3.hs.zeroRttState == ssl_0rtt_accepted;
             allowEarlySend = ss->ssl3.hs.canFalseStart || zeroRtt;
+            firstClientWrite = ss->ssl3.hs.ws == idle_handshake;
             ssl_ReleaseSSL3HandshakeLock(ss);
         }
         if (!allowEarlySend && ss->handshake) {
             rv = ssl_Do1stHandshake(ss);
         }
+        if (firstClientWrite) {
+            /* Wait until after sending ClientHello and double-check 0-RTT. */
+            ssl_GetSSL3HandshakeLock(ss);
+            zeroRtt = ss->ssl3.hs.zeroRttState == ssl_0rtt_sent ||
+                      ss->ssl3.hs.zeroRttState == ssl_0rtt_accepted;
+            ssl_ReleaseSSL3HandshakeLock(ss);
+        }
         ssl_Release1stHandshakeLock(ss);
     }
 
     if (rv < 0) {
         ss->writerThread = NULL;
         goto done;
     }
 
--- a/security/nss/lib/ssl/sslsock.c
+++ b/security/nss/lib/ssl/sslsock.c
@@ -3034,36 +3034,37 @@ ssl_Poll(PRFileDesc *fd, PRInt16 how_fla
                 ** not based on what the application requested.
                 */
                 new_flags &= ~PR_POLL_RW;
                 if (ss->handshaking == sslHandshakingAsClient) {
                     new_flags |= PR_POLL_WRITE;
                 } else { /* handshaking as server */
                     new_flags |= PR_POLL_READ;
                 }
-            } else
+            } else if (ss->lastWriteBlocked) {
                 /* First handshake is in progress */
-                if (ss->lastWriteBlocked) {
                 if (new_flags & PR_POLL_READ) {
                     /* The caller is waiting for data to be received,
                     ** but the initial handshake is blocked on write, or the
                     ** client's first handshake record has not been written.
                     ** The code should select on write, not read.
                     */
-                    new_flags ^= PR_POLL_READ;  /* don't select on read. */
+                    new_flags &= ~PR_POLL_READ; /* don't select on read. */
                     new_flags |= PR_POLL_WRITE; /* do    select on write. */
                 }
             } else if (new_flags & PR_POLL_WRITE) {
                 /* The caller is trying to write, but the handshake is
                 ** blocked waiting for data to read, and the first
                 ** handshake has been sent.  So do NOT to poll on write
-                ** unless we did false start.
+                ** unless we did false start or we are doing 0-RTT.
                 */
-                if (!ss->ssl3.hs.canFalseStart) {
-                    new_flags ^= PR_POLL_WRITE; /* don't select on write. */
+                if (!(ss->ssl3.hs.canFalseStart ||
+                      ss->ssl3.hs.zeroRttState == ssl_0rtt_sent ||
+                      ss->ssl3.hs.zeroRttState == ssl_0rtt_accepted)) {
+                    new_flags &= ~PR_POLL_WRITE; /* don't select on write. */
                 }
                 new_flags |= PR_POLL_READ; /* do    select on read. */
             }
         }
     } else if ((new_flags & PR_POLL_READ) && (SSL_DataPending(fd) > 0)) {
         *p_out_flags = PR_POLL_READ; /* it's ready already. */
         return new_flags;
     } else if ((ss->lastWriteBlocked) && (how_flags & PR_POLL_READ) &&
@@ -3093,16 +3094,19 @@ ssl_Poll(PRFileDesc *fd, PRInt16 how_fla
              * calling PR_Poll that would return PR_POLL_EXCEPT, and send/recv
              * which won't actually report the I/O error while we are waiting
              * for the asynchronous callback to complete).
              */
             new_flags = 0;
         }
     }
 
+    SSL_TRC(20, ("%d: SSL[%d]: ssl_Poll flags %x -> %x",
+                 SSL_GETPID(), fd, how_flags, new_flags));
+
     if (new_flags && (fd->lower->methods->poll != NULL)) {
         PRInt16 lower_out_flags = 0;
         PRInt16 lower_new_flags;
         lower_new_flags = fd->lower->methods->poll(fd->lower, new_flags,
                                                    &lower_out_flags);
         if ((lower_new_flags & lower_out_flags) && (how_flags != new_flags)) {
             PRInt16 out_flags = lower_out_flags & ~PR_POLL_RW;
             if (lower_out_flags & PR_POLL_READ)
--- a/security/nss/lib/util/secasn1d.c
+++ b/security/nss/lib/util/secasn1d.c
@@ -2982,17 +2982,19 @@ SEC_ASN1DecoderFinish(SEC_ASN1DecoderCon
     } else {
         rv = SECSuccess;
     }
 
     /*
      * XXX anything else that needs to be finished?
      */
 
-    PORT_FreeArena(cx->our_pool, PR_TRUE);
+    if (cx) {
+        PORT_FreeArena(cx->our_pool, PR_TRUE);
+    }
 
     return rv;
 }
 
 SEC_ASN1DecoderContext *
 SEC_ASN1DecoderStart(PLArenaPool *their_pool, void *dest,
                      const SEC_ASN1Template *theTemplate)
 {