@ 261102.1

250304.8 · a8ce2ff09af51b39be0d76091e6117caccd1b5c0

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.

reading declarations

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;

src/compile.c

@@ -1,5 +1,4 @@ #include <assert.h>-#include <math.h> #include <string.h> #include <stdlib.h> #include <unistd.h>@@ -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();

src/jv_alloc.c

@@ -1,7 +1,8 @@-#include <stdlib.h>+#include <assert.h> #include <stdio.h>+#include <stdlib.h> #include <string.h>-#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); } 

src/jv_alloc.h

@@ -2,7 +2,6 @@ #define JV_ALLOC_H  #include <stddef.h>-#include "jv.h"  void* jv_mem_alloc(size_t); void* jv_mem_alloc_unguarded(size_t);

src/jv_aux.c

@@ -3,6 +3,7 @@ #include <math.h> #include <string.h> #include <stdlib.h>+#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<n; i++) {     entries[i].object = jv_array_get(jv_copy(objects), i);

tests/base64.test

@@ -1,6 +1,10 @@ # Tests are groups of three lines: program, input, expected output # Blank lines and lines starting with # are ignored +@base64+""+""+ @base64 "<>&'\"\t" "PD4mJyIJ"@@ -15,6 +19,10 @@ "foóbar\n" "Zm/Ds2Jhcgo=" +@base64d+""+""+ @base64d "Zm/Ds2Jhcgo=" "foóbar\n"