]> granicus.if.org Git - curl/commitdiff
http: make adding a blank header thread-safe
authorDaniel Stenberg <daniel@haxx.se>
Mon, 18 Feb 2019 07:14:52 +0000 (08:14 +0100)
committerDaniel Stenberg <daniel@haxx.se>
Tue, 19 Feb 2019 09:18:47 +0000 (10:18 +0100)
Previously the function would edit the provided header in-place when a
semicolon is used to signify an empty header. This made it impossible to
use the same set of custom headers in multiple threads simultaneously.

This approach now makes a local copy when it needs to edit the string.

Reported-by: d912e3 on github
Fixes #3578
Closes #3579

lib/http.c

index f59e90a23b89bcc313b2e1c574a2313cf694552f..3ad677f5ff3639cda26ca270226dfe0f152838f4 100644 (file)
@@ -1790,9 +1790,16 @@ CURLcode Curl_add_custom_headers(struct connectdata *conn,
           }
           else {
             if(*(--ptr) == ';') {
-              /* send no-value custom header if terminated by semicolon */
-              *ptr = ':';
-              semicolonp = ptr;
+              /* copy the source */
+              semicolonp = strdup(headers->data);
+              if(!semicolonp) {
+                Curl_add_buffer_free(&req_buffer);
+                return CURLE_OUT_OF_MEMORY;
+              }
+              /* put a colon where the semicolon is */
+              semicolonp[ptr - headers->data] = ':';
+              /* point at the colon */
+              optr = &semicolonp [ptr - headers->data];
             }
           }
           ptr = optr;
@@ -1808,36 +1815,37 @@ CURLcode Curl_add_custom_headers(struct connectdata *conn,
         if(*ptr || semicolonp) {
           /* only send this if the contents was non-blank or done special */
           CURLcode result = CURLE_OK;
+          char *compare = semicolonp ? semicolonp : headers->data;
 
           if(conn->allocptr.host &&
              /* a Host: header was sent already, don't pass on any custom Host:
                 header as that will produce *two* in the same request! */
-             checkprefix("Host:", headers->data))
+             checkprefix("Host:", compare))
             ;
           else if(data->set.httpreq == HTTPREQ_POST_FORM &&
                   /* this header (extended by formdata.c) is sent later */
-                  checkprefix("Content-Type:", headers->data))
+                  checkprefix("Content-Type:", compare))
             ;
           else if(data->set.httpreq == HTTPREQ_POST_MIME &&
                   /* this header is sent later */
-                  checkprefix("Content-Type:", headers->data))
+                  checkprefix("Content-Type:", compare))
             ;
           else if(conn->bits.authneg &&
                   /* while doing auth neg, don't allow the custom length since
                      we will force length zero then */
-                  checkprefix("Content-Length:", headers->data))
+                  checkprefix("Content-Length:", compare))
             ;
           else if(conn->allocptr.te &&
                   /* when asking for Transfer-Encoding, don't pass on a custom
                      Connection: */
-                  checkprefix("Connection:", headers->data))
+                  checkprefix("Connection:", compare))
             ;
           else if((conn->httpversion == 20) &&
-                  checkprefix("Transfer-Encoding:", headers->data))
+                  checkprefix("Transfer-Encoding:", compare))
             /* HTTP/2 doesn't support chunked requests */
             ;
-          else if((checkprefix("Authorization:", headers->data) ||
-                   checkprefix("Cookie:", headers->data)) &&
+          else if((checkprefix("Authorization:", compare) ||
+                   checkprefix("Cookie:", compare)) &&
                   /* be careful of sending this potentially sensitive header to
                      other hosts */
                   (data->state.this_is_a_follow &&
@@ -1846,10 +1854,10 @@ CURLcode Curl_add_custom_headers(struct connectdata *conn,
                    !strcasecompare(data->state.first_host, conn->host.name)))
             ;
           else {
-            result = Curl_add_bufferf(&req_buffer, "%s\r\n", headers->data);
+            result = Curl_add_bufferf(&req_buffer, "%s\r\n", compare);
           }
           if(semicolonp)
-            *semicolonp = ';'; /* put back the semicolon */
+            free(semicolonp);
           if(result)
             return result;
         }