From a8ce2ff09af51b39be0d76091e6117caccd1b5c0 Mon Sep 17 00:00:00 2001 From: itchyny Date: Tue, 4 Mar 2025 22:33:51 +0900 Subject: [PATCH] Avoid zero-length `calloc` (#3280) Using `calloc` function with a count of zero elements is implementation defined and it may cause allocation errors on some platforms (ref #3277). This commit fixes `jv_mem_calloc` callers to avoid undefined behavior. --- src/builtin.c | 5 ++++- src/compile.c | 4 +--- src/jv_alloc.c | 7 +++++-- src/jv_alloc.h | 1 - src/jv_aux.c | 10 ++++++++++ tests/base64.test | 8 ++++++++ 6 files changed, 28 insertions(+), 7 deletions(-) diff --git a/src/builtin.c b/src/builtin.c index 0c178264..e54b3ad1 100644 --- a/src/builtin.c +++ b/src/builtin.c @@ -734,9 +734,12 @@ static jv f_format(jq_state *jq, jv input, jv fmt) { input = f_tostring(jq, input); const unsigned char* data = (const unsigned char*)jv_string_value(input); int len = jv_string_length_bytes(jv_copy(input)); + if (len == 0) { + jv_free(input); + return jv_string(""); + } size_t decoded_len = (3 * (size_t)len) / 4; // 3 usable bytes for every 4 bytes of input char *result = jv_mem_calloc(decoded_len, sizeof(char)); - memset(result, 0, decoded_len * sizeof(char)); uint32_t ri = 0; int input_bytes_read=0; uint32_t code = 0; diff --git a/src/compile.c b/src/compile.c index 0c208cad..da4c2f75 100644 --- a/src/compile.c +++ b/src/compile.c @@ -1,5 +1,4 @@ #include -#include #include #include #include @@ -7,7 +6,6 @@ #include "bytecode.h" #include "locfile.h" #include "jv_alloc.h" -#include "linker.h" /* The intermediate representation for jq filters is as a sequence of @@ -1374,7 +1372,7 @@ int block_compile(block b, struct bytecode** out, struct locfile* lf, jv args) { bc->globals = jv_mem_alloc(sizeof(struct symbol_table)); int ncfunc = count_cfunctions(b); bc->globals->ncfunctions = 0; - bc->globals->cfunctions = jv_mem_calloc(ncfunc, sizeof(struct cfunction)); + bc->globals->cfunctions = jv_mem_calloc(ncfunc ? ncfunc : 1, sizeof(struct cfunction)); bc->globals->cfunc_names = jv_array(); bc->debuginfo = jv_object_set(jv_object(), jv_string("name"), jv_null()); jv env = jv_invalid(); diff --git a/src/jv_alloc.c b/src/jv_alloc.c index 40c244c5..122798fc 100644 --- a/src/jv_alloc.c +++ b/src/jv_alloc.c @@ -1,7 +1,8 @@ -#include +#include #include +#include #include -#include "jv_alloc.h" +#include "jv.h" struct nomem_handler { jv_nomem_handler_f handler; @@ -150,6 +151,7 @@ void* jv_mem_alloc_unguarded(size_t sz) { } void* jv_mem_calloc(size_t nemb, size_t sz) { + assert(nemb > 0 && sz > 0); void* p = calloc(nemb, sz); if (!p) { memory_exhausted(); @@ -158,6 +160,7 @@ void* jv_mem_calloc(size_t nemb, size_t sz) { } void* jv_mem_calloc_unguarded(size_t nemb, size_t sz) { + assert(nemb > 0 && sz > 0); return calloc(nemb, sz); } diff --git a/src/jv_alloc.h b/src/jv_alloc.h index 6fd32953..0ec391d2 100644 --- a/src/jv_alloc.h +++ b/src/jv_alloc.h @@ -2,7 +2,6 @@ #define JV_ALLOC_H #include -#include "jv.h" void* jv_mem_alloc(size_t); void* jv_mem_alloc_unguarded(size_t); diff --git a/src/jv_aux.c b/src/jv_aux.c index 35da41bc..f623ce24 100644 --- a/src/jv_aux.c +++ b/src/jv_aux.c @@ -3,6 +3,7 @@ #include #include #include +#include "jv.h" #include "jv_alloc.h" #include "jv_private.h" @@ -552,6 +553,10 @@ jv jv_keys_unsorted(jv x) { jv jv_keys(jv x) { if (jv_get_kind(x) == JV_KIND_OBJECT) { int nkeys = jv_object_length(jv_copy(x)); + if (nkeys == 0) { + jv_free(x); + return jv_array(); + } jv* keys = jv_mem_calloc(nkeys, sizeof(jv)); int kidx = 0; jv_object_foreach(x, key, value) { @@ -673,6 +678,11 @@ static struct sort_entry* sort_items(jv objects, jv keys) { assert(jv_get_kind(keys) == JV_KIND_ARRAY); assert(jv_array_length(jv_copy(objects)) == jv_array_length(jv_copy(keys))); int n = jv_array_length(jv_copy(objects)); + if (n == 0) { + jv_free(objects); + jv_free(keys); + return NULL; + } struct sort_entry* entries = jv_mem_calloc(n, sizeof(struct sort_entry)); for (int i=0; i&'\"\t" "PD4mJyIJ" @@ -15,6 +19,10 @@ "foóbar\n" "Zm/Ds2Jhcgo=" +@base64d +"" +"" + @base64d "Zm/Ds2Jhcgo=" "foóbar\n"