From 9bfecad23d6f5e548223bf8d14ccfe51e1d8d981 Mon Sep 17 00:00:00 2001
From: Daniel Stenberg <daniel@haxx.se>
Date: Mon, 7 Nov 2016 10:55:25 +0100
Subject: [PATCH] realloc: use Curl_saferealloc to avoid common mistakes

---
 lib/content_encoding.c |  9 +++------
 lib/curl_ntlm_wb.c     |  8 ++++----
 lib/escape.c           |  7 +++----
 lib/http.c             |  4 ++--
 lib/http2.c            |  7 +++----
 lib/rtsp.c             |  5 +++--
 lib/security.c         |  4 ++--
 lib/ssh.c              |  7 ++++---
 lib/strdup.c           | 23 +++++++++++++++++++++++
 lib/strdup.h           |  1 +
 10 files changed, 48 insertions(+), 27 deletions(-)

diff --git a/lib/content_encoding.c b/lib/content_encoding.c
index fa36aca..5a5824d 100644
--- a/lib/content_encoding.c
+++ b/lib/content_encoding.c
@@ -3,11 +3,11 @@
  *  Project                     ___| | | |  _ \| |
  *                             / __| | | | |_) | |
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al.
  *
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
  * are also available at https://curl.haxx.se/docs/copyright.html.
  *
@@ -26,12 +26,12 @@
 
 #include "urldata.h"
 #include <curl/curl.h>
 #include "sendf.h"
 #include "content_encoding.h"
+#include "strdup.h"
 #include "curl_memory.h"
-
 #include "memdebug.h"
 
 /* Comment this out if zlib is always going to be at least ver. 1.2.0.4
    (doing so will reduce code size slightly). */
 #define OLD_ZLIB_SUPPORT 1
@@ -369,16 +369,13 @@ Curl_unencode_gzip_write(struct connectdata *conn,
 
   case ZLIB_GZIP_HEADER:
   {
     /* Need more gzip header data state */
     ssize_t hlen;
-    unsigned char *oldblock = z->next_in;
-
     z->avail_in += (uInt)nread;
-    z->next_in = realloc(z->next_in, z->avail_in);
+    z->next_in = Curl_saferealloc(z->next_in, z->avail_in);
     if(z->next_in == NULL) {
-      free(oldblock);
       return exit_zlib(z, &k->zlib_init, CURLE_OUT_OF_MEMORY);
     }
     /* Append the new block of data to the previous one */
     memcpy(z->next_in + z->avail_in - nread, k->str, nread);
 
diff --git a/lib/curl_ntlm_wb.c b/lib/curl_ntlm_wb.c
index afdea16..1d1b3a3 100644
--- a/lib/curl_ntlm_wb.c
+++ b/lib/curl_ntlm_wb.c
@@ -49,10 +49,11 @@
 #include "select.h"
 #include "vauth/ntlm.h"
 #include "curl_ntlm_wb.h"
 #include "url.h"
 #include "strerror.h"
+#include "strdup.h"
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
 #include "curl_memory.h"
 #include "memdebug.h"
 
@@ -291,15 +292,14 @@ static CURLcode ntlm_wb_response(struct connectdata *conn,
     len_out += size;
     if(buf[len_out - 1] == '\n') {
       buf[len_out - 1] = '\0';
       break;
     }
-    newbuf = realloc(buf, len_out + NTLM_BUFSIZE);
-    if(!newbuf) {
-      free(buf);
+    newbuf = Curl_saferealloc(buf, len_out + NTLM_BUFSIZE);
+    if(!newbuf)
       return CURLE_OUT_OF_MEMORY;
-    }
+
     buf = newbuf;
   }
 
   /* Samba/winbind installed but not configured */
   if(state == NTLMSTATE_TYPE1 &&
diff --git a/lib/escape.c b/lib/escape.c
index 6657007..9fb8d3e 100644
--- a/lib/escape.c
+++ b/lib/escape.c
@@ -29,10 +29,11 @@
 
 #include "urldata.h"
 #include "warnless.h"
 #include "non-ascii.h"
 #include "escape.h"
+#include "strdup.h"
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
 #include "curl_memory.h"
 #include "memdebug.h"
 
@@ -107,15 +108,13 @@ char *curl_easy_escape(struct Curl_easy *data, const char *string,
     else {
       /* encode it */
       newlen += 2; /* the size grows with two, since this'll become a %XX */
       if(newlen > alloc) {
         alloc *= 2;
-        testing_ptr = realloc(ns, alloc);
-        if(!testing_ptr) {
-          free(ns);
+        testing_ptr = Curl_saferealloc(ns, alloc);
+        if(!testing_ptr)
           return NULL;
-        }
         else {
           ns = testing_ptr;
         }
       }
 
diff --git a/lib/http.c b/lib/http.c
index e7788e7..999d6da 100644
--- a/lib/http.c
+++ b/lib/http.c
@@ -74,10 +74,11 @@
 #include "non-ascii.h"
 #include "conncache.h"
 #include "pipeline.h"
 #include "http2.h"
 #include "connect.h"
+#include "strdup.h"
 
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
 #include "curl_memory.h"
 #include "memdebug.h"
@@ -1253,18 +1254,17 @@ CURLcode Curl_add_buffer(Curl_send_buffer *in, const void *inptr, size_t size)
     else
       new_size = (in->size_used+size) * 2;
 
     if(in->buffer)
       /* we have a buffer, enlarge the existing one */
-      new_rb = realloc(in->buffer, new_size);
+      new_rb = Curl_saferealloc(in->buffer, new_size);
     else
       /* create a new buffer */
       new_rb = malloc(new_size);
 
     if(!new_rb) {
       /* If we failed, we cleanup the whole buffer and return error */
-      Curl_safefree(in->buffer);
       free(in);
       return CURLE_OUT_OF_MEMORY;
     }
 
     in->buffer = new_rb;
diff --git a/lib/http2.c b/lib/http2.c
index b0301da..1484e15 100644
--- a/lib/http2.c
+++ b/lib/http2.c
@@ -33,11 +33,11 @@
 #include "multiif.h"
 #include "conncache.h"
 #include "url.h"
 #include "connect.h"
 #include "strtoofft.h"
-
+#include "strdup.h"
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
 #include "curl_memory.h"
 #include "memdebug.h"
 
@@ -839,14 +839,13 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
     }
     else if(stream->push_headers_used ==
             stream->push_headers_alloc) {
       char **headp;
       stream->push_headers_alloc *= 2;
-      headp = realloc(stream->push_headers,
-                      stream->push_headers_alloc * sizeof(char *));
+      headp = Curl_saferealloc(stream->push_headers,
+                               stream->push_headers_alloc * sizeof(char *));
       if(!headp) {
-        free(stream->push_headers);
         stream->push_headers = NULL;
         return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
       }
       stream->push_headers = headp;
     }
diff --git a/lib/rtsp.c b/lib/rtsp.c
index d1bad19..5da33d4 100644
--- a/lib/rtsp.c
+++ b/lib/rtsp.c
@@ -34,10 +34,11 @@
 #include "progress.h"
 #include "rtsp.h"
 #include "strcase.h"
 #include "select.h"
 #include "connect.h"
+#include "strdup.h"
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
 #include "curl_memory.h"
 #include "memdebug.h"
 
@@ -612,13 +613,13 @@ static CURLcode rtsp_rtp_readwrite(struct Curl_easy *data,
   char *scratch;
   CURLcode result;
 
   if(rtspc->rtp_buf) {
     /* There was some leftover data the last time. Merge buffers */
-    char *newptr = realloc(rtspc->rtp_buf, rtspc->rtp_bufsize + *nread);
+    char *newptr = Curl_saferealloc(rtspc->rtp_buf,
+                                    rtspc->rtp_bufsize + *nread);
     if(!newptr) {
-      Curl_safefree(rtspc->rtp_buf);
       rtspc->rtp_buf = NULL;
       rtspc->rtp_bufsize = 0;
       return CURLE_OUT_OF_MEMORY;
     }
     rtspc->rtp_buf = newptr;
diff --git a/lib/security.c b/lib/security.c
index ff26066..87c22da 100644
--- a/lib/security.c
+++ b/lib/security.c
@@ -60,11 +60,11 @@
 #include "curl_sec.h"
 #include "ftp.h"
 #include "sendf.h"
 #include "strcase.h"
 #include "warnless.h"
-
+#include "strdup.h"
 /* The last #include file should be: */
 #include "memdebug.h"
 
 static const struct {
   enum protection_level level;
@@ -200,11 +200,11 @@ static CURLcode read_data(struct connectdata *conn,
     return result;
 
   if(len) {
     /* only realloc if there was a length */
     len = ntohl(len);
-    tmp = realloc(buf->data, len);
+    tmp = Curl_saferealloc(buf->data, len);
   }
   if(tmp == NULL)
     return CURLE_OUT_OF_MEMORY;
 
   buf->data = tmp;
diff --git a/lib/ssh.c b/lib/ssh.c
index 0df030d..420beb2 100644
--- a/lib/ssh.c
+++ b/lib/ssh.c
@@ -69,11 +69,11 @@
 #include "http.h" /* for HTTP proxy tunnel stuff */
 #include "ssh.h"
 #include "url.h"
 #include "speedcheck.h"
 #include "getinfo.h"
-
+#include "strdup.h"
 #include "strcase.h"
 #include "vtls/vtls.h"
 #include "connect.h"
 #include "strerror.h"
 #include "inet_ntop.h"
@@ -2110,13 +2110,14 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block)
       }
       Curl_safefree(sshc->readdir_linkPath);
 
       /* get room for the filename and extra output */
       sshc->readdir_totalLen += 4 + sshc->readdir_len;
-      new_readdir_line = realloc(sshc->readdir_line, sshc->readdir_totalLen);
+      new_readdir_line = Curl_saferealloc(sshc->readdir_line,
+                                          sshc->readdir_totalLen);
       if(!new_readdir_line) {
-        Curl_safefree(sshc->readdir_line);
+        sshc->readdir_line = NULL;
         Curl_safefree(sshc->readdir_filename);
         Curl_safefree(sshc->readdir_longentry);
         state(conn, SSH_SFTP_CLOSE);
         sshc->actualcode = CURLE_OUT_OF_MEMORY;
         break;
diff --git a/lib/strdup.c b/lib/strdup.c
index 5a15c2b..ec1b0df 100644
--- a/lib/strdup.c
+++ b/lib/strdup.c
@@ -73,5 +73,28 @@ void *Curl_memdup(const void *src, size_t length)
 
   memcpy(buffer, src, length);
 
   return buffer;
 }
+
+/***************************************************************************
+ *
+ * Curl_saferealloc(dataptr, newlength)
+ *
+ * Does a normal realloc(), but will free the data pointer if the realloc
+ * fails. If 'newlength' is zero, it will free the data and return a failure.
+ *
+ * This convenience function is provided and used to help us avoid a common
+ * mistake pattern when we could pass in a zero, catch the NULL return and end
+ * up free'ing the memory twice.
+ *
+ * Returns the new pointer or NULL on failure.
+ *
+ ***************************************************************************/
+void *Curl_saferealloc(void *ptr, size_t size)
+{
+  void *datap = realloc(ptr, size);
+  if(size && !datap)
+    /* only free 'ptr' if size was non-zero */
+    free(ptr);
+  return datap;
+}
diff --git a/lib/strdup.h b/lib/strdup.h
index c74a3b7..ae3d5d0 100644
--- a/lib/strdup.h
+++ b/lib/strdup.h
@@ -25,7 +25,8 @@
 
 #ifndef HAVE_STRDUP
 extern char *curlx_strdup(const char *str);
 #endif
 void *Curl_memdup(const void *src, size_t buffer_length);
+void *Curl_saferealloc(void *ptr, size_t size);
 
 #endif /* HEADER_CURL_STRDUP_H */
-- 
2.10.2

