diff --git a/build/openresty/patches/lua-cjson-2.1.0.10_02-handle-large-string-correctly.patch b/build/openresty/patches/lua-cjson-2.1.0.10_02-handle-large-string-correctly.patch new file mode 100644 index 000000000000..2458c4e186a3 --- /dev/null +++ b/build/openresty/patches/lua-cjson-2.1.0.10_02-handle-large-string-correctly.patch @@ -0,0 +1,387 @@ +diff --git a/bundle/lua-cjson-2.1.0.10/lua_cjson.c b/bundle/lua-cjson-2.1.0.10/lua_cjson.c +index ff61c47..3b055c4 100644 +--- a/bundle/lua-cjson-2.1.0.10/lua_cjson.c ++++ b/bundle/lua-cjson-2.1.0.10/lua_cjson.c +@@ -40,6 +40,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -177,13 +178,13 @@ typedef struct { + + typedef struct { + json_token_type_t type; +- int index; ++ size_t index; + union { + const char *string; + double number; + int boolean; + } value; +- int string_len; ++ size_t string_len; + } json_token_t; + + static const char *char2escape[256] = { +@@ -544,6 +545,8 @@ static void json_append_string(lua_State *l, strbuf_t *json, int lindex) + * This buffer is reused constantly for small strings + * If there are any excess pages, they won't be hit anyway. + * This gains ~5% speedup. */ ++ if (len > SIZE_MAX / 6 - 3) ++ abort(); /* Overflow check */ + strbuf_ensure_empty_length(json, len * 6 + 2); + + strbuf_append_char_unsafe(json, '\"'); +@@ -818,7 +821,7 @@ static int json_encode(lua_State *l) + strbuf_t local_encode_buf; + strbuf_t *encode_buf; + char *json; +- int len; ++ size_t len; + + luaL_argcheck(l, lua_gettop(l) == 1, 1, "expected 1 argument"); + +diff --git a/bundle/lua-cjson-2.1.0.10/strbuf.c b/bundle/lua-cjson-2.1.0.10/strbuf.c +index ed13367..2dc30be 100644 +--- a/bundle/lua-cjson-2.1.0.10/strbuf.c ++++ b/bundle/lua-cjson-2.1.0.10/strbuf.c +@@ -26,6 +26,7 @@ + #include + #include + #include ++#include + + #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: + */ +diff --git a/bundle/lua-cjson-2.1.0.10/strbuf.h b/bundle/lua-cjson-2.1.0.10/strbuf.h +index 5df0b7b..d77e0f4 100644 +--- a/bundle/lua-cjson-2.1.0.10/strbuf.h ++++ b/bundle/lua-cjson-2.1.0.10/strbuf.h +@@ -32,15 +32,13 @@ + + /* Size: Total bytes allocated to *buf + * Length: String length, excluding optional NULL terminator. +- * Increment: Allocation increments when resizing the string buffer. + * Dynamic: True if created via strbuf_new() + */ + + typedef struct { + char *buf; +- int size; +- int length; +- int increment; ++ size_t size; ++ size_t length; + int dynamic; + int reallocs; + int debug; +@@ -49,32 +47,27 @@ typedef struct { + #ifndef STRBUF_DEFAULT_SIZE + #define STRBUF_DEFAULT_SIZE 1023 + #endif +-#ifndef STRBUF_DEFAULT_INCREMENT +-#define STRBUF_DEFAULT_INCREMENT -2 +-#endif + + /* Initialise */ +-extern strbuf_t *strbuf_new(int len); +-extern void strbuf_init(strbuf_t *s, int len); +-extern void strbuf_set_increment(strbuf_t *s, int increment); ++extern strbuf_t *strbuf_new(size_t len); ++extern void strbuf_init(strbuf_t *s, size_t len); + + /* Release */ + extern void strbuf_free(strbuf_t *s); +-extern char *strbuf_free_to_string(strbuf_t *s, int *len); ++extern char *strbuf_free_to_string(strbuf_t *s, size_t *len); + + /* Management */ +-extern void strbuf_resize(strbuf_t *s, int len); +-static int strbuf_empty_length(strbuf_t *s); +-static int strbuf_length(strbuf_t *s); +-static char *strbuf_string(strbuf_t *s, int *len); +-static void strbuf_ensure_empty_length(strbuf_t *s, int len); ++extern void strbuf_resize(strbuf_t *s, size_t len); ++static size_t strbuf_empty_length(strbuf_t *s); ++static size_t strbuf_length(strbuf_t *s); ++static char *strbuf_string(strbuf_t *s, size_t *len); ++static void strbuf_ensure_empty_length(strbuf_t *s, size_t len); + static char *strbuf_empty_ptr(strbuf_t *s); +-static void strbuf_extend_length(strbuf_t *s, int len); ++static void strbuf_extend_length(strbuf_t *s, size_t len); ++static void strbuf_set_length(strbuf_t *s, int len); + + /* Update */ +-extern void strbuf_append_fmt(strbuf_t *s, int len, const char *fmt, ...); +-extern void strbuf_append_fmt_retry(strbuf_t *s, const char *format, ...); +-static void strbuf_append_mem(strbuf_t *s, const char *c, int len); ++static void strbuf_append_mem(strbuf_t *s, const char *c, size_t len); + extern void strbuf_append_string(strbuf_t *s, const char *str); + static void strbuf_append_char(strbuf_t *s, const char c); + static void strbuf_ensure_null(strbuf_t *s); +@@ -92,12 +85,12 @@ static inline int strbuf_allocated(strbuf_t *s) + + /* Return bytes remaining in the string buffer + * Ensure there is space for a NULL terminator. */ +-static inline int strbuf_empty_length(strbuf_t *s) ++static inline size_t strbuf_empty_length(strbuf_t *s) + { + return s->size - s->length - 1; + } + +-static inline void strbuf_ensure_empty_length(strbuf_t *s, int len) ++static inline void strbuf_ensure_empty_length(strbuf_t *s, size_t len) + { + if (len > strbuf_empty_length(s)) + strbuf_resize(s, s->length + len); +@@ -108,12 +101,17 @@ static inline char *strbuf_empty_ptr(strbuf_t *s) + return s->buf + s->length; + } + +-static inline void strbuf_extend_length(strbuf_t *s, int len) ++static inline void strbuf_set_length(strbuf_t *s, int len) ++{ ++ s->length = len; ++} ++ ++static inline void strbuf_extend_length(strbuf_t *s, size_t len) + { + s->length += len; + } + +-static inline int strbuf_length(strbuf_t *s) ++static inline size_t strbuf_length(strbuf_t *s) + { + return s->length; + } +@@ -129,14 +127,14 @@ static inline void strbuf_append_char_unsafe(strbuf_t *s, const char c) + s->buf[s->length++] = c; + } + +-static inline void strbuf_append_mem(strbuf_t *s, const char *c, int len) ++static inline void strbuf_append_mem(strbuf_t *s, const char *c, size_t len) + { + strbuf_ensure_empty_length(s, len); + memcpy(s->buf + s->length, c, len); + s->length += len; + } + +-static inline void strbuf_append_mem_unsafe(strbuf_t *s, const char *c, int len) ++static inline void strbuf_append_mem_unsafe(strbuf_t *s, const char *c, size_t len) + { + memcpy(s->buf + s->length, c, len); + s->length += len; +@@ -147,7 +145,7 @@ static inline void strbuf_ensure_null(strbuf_t *s) + s->buf[s->length] = 0; + } + +-static inline char *strbuf_string(strbuf_t *s, int *len) ++static inline char *strbuf_string(strbuf_t *s, size_t *len) + { + if (len) + *len = s->length; \ No newline at end of file diff --git a/spec/01-unit/29-lua_cjson_large_str_spec.lua b/spec/01-unit/29-lua_cjson_large_str_spec.lua new file mode 100644 index 000000000000..d7cd7de6e13a --- /dev/null +++ b/spec/01-unit/29-lua_cjson_large_str_spec.lua @@ -0,0 +1,21 @@ +local cjson = require("cjson.safe") + +describe("cjson", function() + it("should encode large JSON string correctly", function() + --[[ + This test is to ensure that `cjson.encode()` + can encode large JSON strings correctly, + the JSON string is the string element in JSON representation, + not the JSON string serialized from a Object. + + The bug is caused by the overflow of the `int`, + and it will casue the signal 11 when writing the string to buffer. + --]] + local large_string = string.rep("a", 1024 * 1024 * 1024) -- 1GB + + -- if bug exists, test will exit with 139 code (signal 11) + local json = assert(cjson.encode({large_string = large_string})) + assert(string.find(json, large_string, 1, true), + "JSON string should contain the large string") + end) +end)