Skip to content

Commit e8cd369

Browse files
committed
Avoid zero-length calloc
Using `calloc` function with a count of zero elements is implementation defined and it may cause allocation errors on some platforms (ref jqlang#3277). This commit fixes `jv_mem_calloc` callers to avoid undefined behavior.
1 parent 8ba03f7 commit e8cd369

File tree

6 files changed

+28
-7
lines changed

6 files changed

+28
-7
lines changed

src/builtin.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -757,9 +757,12 @@ static jv f_format(jq_state *jq, jv input, jv fmt) {
757757
input = f_tostring(jq, input);
758758
const unsigned char* data = (const unsigned char*)jv_string_value(input);
759759
int len = jv_string_length_bytes(jv_copy(input));
760+
if (len == 0) {
761+
jv_free(input);
762+
return jv_string("");
763+
}
760764
size_t decoded_len = (3 * (size_t)len) / 4; // 3 usable bytes for every 4 bytes of input
761765
char *result = jv_mem_calloc(decoded_len, sizeof(char));
762-
memset(result, 0, decoded_len * sizeof(char));
763766
uint32_t ri = 0;
764767
int input_bytes_read=0;
765768
uint32_t code = 0;

src/compile.c

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
#include <assert.h>
2-
#include <math.h>
32
#include <string.h>
43
#include <stdlib.h>
54
#include <unistd.h>
65
#include "compile.h"
76
#include "bytecode.h"
87
#include "locfile.h"
98
#include "jv_alloc.h"
10-
#include "linker.h"
119

1210
/*
1311
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) {
13741372
bc->globals = jv_mem_alloc(sizeof(struct symbol_table));
13751373
int ncfunc = count_cfunctions(b);
13761374
bc->globals->ncfunctions = 0;
1377-
bc->globals->cfunctions = jv_mem_calloc(ncfunc, sizeof(struct cfunction));
1375+
bc->globals->cfunctions = jv_mem_calloc(ncfunc ? ncfunc : 1, sizeof(struct cfunction));
13781376
bc->globals->cfunc_names = jv_array();
13791377
bc->debuginfo = jv_object_set(jv_object(), jv_string("name"), jv_null());
13801378
jv env = jv_invalid();

src/jv_alloc.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
#include <stdlib.h>
1+
#include <assert.h>
22
#include <stdio.h>
3+
#include <stdlib.h>
34
#include <string.h>
4-
#include "jv_alloc.h"
5+
#include "jv.h"
56

67
struct nomem_handler {
78
jv_nomem_handler_f handler;
@@ -150,6 +151,7 @@ void* jv_mem_alloc_unguarded(size_t sz) {
150151
}
151152

152153
void* jv_mem_calloc(size_t nemb, size_t sz) {
154+
assert(nemb > 0 && sz > 0);
153155
void* p = calloc(nemb, sz);
154156
if (!p) {
155157
memory_exhausted();
@@ -158,6 +160,7 @@ void* jv_mem_calloc(size_t nemb, size_t sz) {
158160
}
159161

160162
void* jv_mem_calloc_unguarded(size_t nemb, size_t sz) {
163+
assert(nemb > 0 && sz > 0);
161164
return calloc(nemb, sz);
162165
}
163166

src/jv_alloc.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
#define JV_ALLOC_H
33

44
#include <stddef.h>
5-
#include "jv.h"
65

76
void* jv_mem_alloc(size_t);
87
void* jv_mem_alloc_unguarded(size_t);

src/jv_aux.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include <math.h>
44
#include <string.h>
55
#include <stdlib.h>
6+
#include "jv.h"
67
#include "jv_alloc.h"
78
#include "jv_private.h"
89

@@ -552,6 +553,10 @@ jv jv_keys_unsorted(jv x) {
552553
jv jv_keys(jv x) {
553554
if (jv_get_kind(x) == JV_KIND_OBJECT) {
554555
int nkeys = jv_object_length(jv_copy(x));
556+
if (nkeys == 0) {
557+
jv_free(x);
558+
return jv_array();
559+
}
555560
jv* keys = jv_mem_calloc(nkeys, sizeof(jv));
556561
int kidx = 0;
557562
jv_object_foreach(x, key, value) {
@@ -673,6 +678,11 @@ static struct sort_entry* sort_items(jv objects, jv keys) {
673678
assert(jv_get_kind(keys) == JV_KIND_ARRAY);
674679
assert(jv_array_length(jv_copy(objects)) == jv_array_length(jv_copy(keys)));
675680
int n = jv_array_length(jv_copy(objects));
681+
if (n == 0) {
682+
jv_free(objects);
683+
jv_free(keys);
684+
return NULL;
685+
}
676686
struct sort_entry* entries = jv_mem_calloc(n, sizeof(struct sort_entry));
677687
for (int i=0; i<n; i++) {
678688
entries[i].object = jv_array_get(jv_copy(objects), i);

tests/base64.test

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
# Tests are groups of three lines: program, input, expected output
22
# Blank lines and lines starting with # are ignored
33

4+
@base64
5+
""
6+
""
7+
48
@base64
59
"<>&'\"\t"
610
"PD4mJyIJ"
@@ -15,6 +19,10 @@
1519
"foóbar\n"
1620
"Zm/Ds2Jhcgo="
1721

22+
@base64d
23+
""
24+
""
25+
1826
@base64d
1927
"Zm/Ds2Jhcgo="
2028
"foóbar\n"

0 commit comments

Comments
 (0)