From f3a7ad42104e0d25493a06ed3d1d66c7b4fd58bb Mon Sep 17 00:00:00 2001
From: Oran Agra <oran@redislabs.com>
Date: Tue, 18 Jul 2023 16:50:53 +0800
Subject: bugfix: Lua cjson and cmsgpack integer overflow issues
 (CVE-2022-24834)

* Fix integer overflows due to using wrong integer size.
* Add assertions / panic when overflow still happens.

Co-authored-by: Yossi Gottlieb <yossigo@gmail.com>
---
 strbuf.c | 110 +++++++++++++++++----------------------------------------------
 1 file changed, 29 insertions(+), 81 deletions(-)

(limited to 'strbuf.c')

diff --git a/strbuf.c b/strbuf.c
index ed13367..2dc30be 100644
--- a/strbuf.c
+++ b/strbuf.c
@@ -26,6 +26,7 @@
 #include <stdlib.h>
 #include <stdarg.h>
 #include <string.h>
+#include <stdint.h>
 
 #include "strbuf.h"
 
@@ -38,22 +39,22 @@ static void die(const char *fmt, ...)
     va_end(arg);
     fprintf(stderr, "\n");
 
-    exit(-1);
+    abort();
 }
 
-void strbuf_init(strbuf_t *s, int len)
+void strbuf_init(strbuf_t *s, size_t len)
 {
-    int size;
+    size_t size;
 
-    if (len <= 0)
+    if (!len)
         size = STRBUF_DEFAULT_SIZE;
     else
-        size = len + 1;         /* \0 terminator */
-
+        size = len + 1;
+    if (size < len)
+        die("Overflow, len: %zu", len);
     s->buf = NULL;
     s->size = size;
     s->length = 0;
-    s->increment = STRBUF_DEFAULT_INCREMENT;
     s->dynamic = 0;
     s->reallocs = 0;
     s->debug = 0;
@@ -65,7 +66,7 @@ void strbuf_init(strbuf_t *s, int len)
     strbuf_ensure_null(s);
 }
 
-strbuf_t *strbuf_new(int len)
+strbuf_t *strbuf_new(size_t len)
 {
     strbuf_t *s;
 
@@ -81,20 +82,10 @@ strbuf_t *strbuf_new(int len)
     return s;
 }
 
-void strbuf_set_increment(strbuf_t *s, int increment)
-{
-    /* Increment > 0:  Linear buffer growth rate
-     * Increment < -1: Exponential buffer growth rate */
-    if (increment == 0 || increment == -1)
-        die("BUG: Invalid string increment");
-
-    s->increment = increment;
-}
-
 static inline void debug_stats(strbuf_t *s)
 {
     if (s->debug) {
-        fprintf(stderr, "strbuf(%lx) reallocs: %d, length: %d, size: %d\n",
+        fprintf(stderr, "strbuf(%lx) reallocs: %d, length: %zd, size: %zd\n",
                 (long)s, s->reallocs, s->length, s->size);
     }
 }
@@ -113,7 +104,7 @@ void strbuf_free(strbuf_t *s)
         free(s);
 }
 
-char *strbuf_free_to_string(strbuf_t *s, int *len)
+char *strbuf_free_to_string(strbuf_t *s, size_t *len)
 {
     char *buf;
 
@@ -131,57 +122,63 @@ char *strbuf_free_to_string(strbuf_t *s, int *len)
     return buf;
 }
 
-static int calculate_new_size(strbuf_t *s, int len)
+static size_t calculate_new_size(strbuf_t *s, size_t len)
 {
-    int reqsize, newsize;
+    size_t reqsize, newsize;
 
     if (len <= 0)
         die("BUG: Invalid strbuf length requested");
 
     /* Ensure there is room for optional NULL termination */
     reqsize = len + 1;
+    if (reqsize < len)
+        die("Overflow, len: %zu", len);
 
     /* If the user has requested to shrink the buffer, do it exactly */
     if (s->size > reqsize)
         return reqsize;
 
     newsize = s->size;
-    if (s->increment < 0) {
+    if (reqsize >= SIZE_MAX / 2) {
+        newsize = reqsize;
+    } else {
         /* Exponential sizing */
         while (newsize < reqsize)
-            newsize *= -s->increment;
-    } else if (s->increment != 0)  {
-        /* Linear sizing */
-        newsize = ((newsize + s->increment - 1) / s->increment) * s->increment;
+            newsize *= 2;
     }
 
+    if (newsize < reqsize)
+        die("BUG: strbuf length would overflow, len: %zu", len);
+
+
     return newsize;
 }
 
 
 /* Ensure strbuf can handle a string length bytes long (ignoring NULL
  * optional termination). */
-void strbuf_resize(strbuf_t *s, int len)
+void strbuf_resize(strbuf_t *s, size_t len)
 {
-    int newsize;
+    size_t newsize;
 
     newsize = calculate_new_size(s, len);
 
     if (s->debug > 1) {
-        fprintf(stderr, "strbuf(%lx) resize: %d => %d\n",
+        fprintf(stderr, "strbuf(%lx) resize: %zd => %zd\n",
                 (long)s, s->size, newsize);
     }
 
     s->size = newsize;
     s->buf = realloc(s->buf, s->size);
     if (!s->buf)
-        die("Out of memory");
+        die("Out of memory, len: %zu", len);
     s->reallocs++;
 }
 
 void strbuf_append_string(strbuf_t *s, const char *str)
 {
-    int space, i;
+    int i;
+    size_t space;
 
     space = strbuf_empty_length(s);
 
@@ -197,55 +194,6 @@ void strbuf_append_string(strbuf_t *s, const char *str)
     }
 }
 
-/* strbuf_append_fmt() should only be used when an upper bound
- * is known for the output string. */
-void strbuf_append_fmt(strbuf_t *s, int len, const char *fmt, ...)
-{
-    va_list arg;
-    int fmt_len;
-
-    strbuf_ensure_empty_length(s, len);
-
-    va_start(arg, fmt);
-    fmt_len = vsnprintf(s->buf + s->length, len, fmt, arg);
-    va_end(arg);
-
-    if (fmt_len < 0)
-        die("BUG: Unable to convert number");  /* This should never happen.. */
-
-    s->length += fmt_len;
-}
-
-/* strbuf_append_fmt_retry() can be used when the there is no known
- * upper bound for the output string. */
-void strbuf_append_fmt_retry(strbuf_t *s, const char *fmt, ...)
-{
-    va_list arg;
-    int fmt_len, try;
-    int empty_len;
-
-    /* If the first attempt to append fails, resize the buffer appropriately
-     * and try again */
-    for (try = 0; ; try++) {
-        va_start(arg, fmt);
-        /* Append the new formatted string */
-        /* fmt_len is the length of the string required, excluding the
-         * trailing NULL */
-        empty_len = strbuf_empty_length(s);
-        /* Add 1 since there is also space to store the terminating NULL. */
-        fmt_len = vsnprintf(s->buf + s->length, empty_len + 1, fmt, arg);
-        va_end(arg);
-
-        if (fmt_len <= empty_len)
-            break;  /* SUCCESS */
-        if (try > 0)
-            die("BUG: length of formatted string changed");
-
-        strbuf_resize(s, s->length + fmt_len);
-    }
-
-    s->length += fmt_len;
-}
 
 /* vi:ai et sw=4 ts=4:
  */
-- 
cgit v1.2.3-55-g6feb