If the input file is too large to fit into a printbuf then return an error value instead of silently truncating the parsed content. This introduces errno handling into printbuf to distinguish between an input file being too large and running out of memory.tags/json-c-0.17-20230812
@@ -101,15 +101,22 @@ struct json_object *json_object_from_fd_ex(int fd, int in_depth) | |||||
if (!tok) | if (!tok) | ||||
{ | { | ||||
_json_c_set_last_err( | _json_c_set_last_err( | ||||
"json_object_from_fd_ex: unable to allocate json_tokener(depth=%d): %s\n", depth, | |||||
strerror(errno)); | |||||
"json_object_from_fd_ex: unable to allocate json_tokener(depth=%d): %s\n", | |||||
depth, strerror(errno)); | |||||
printbuf_free(pb); | printbuf_free(pb); | ||||
return NULL; | return NULL; | ||||
} | } | ||||
while ((ret = read(fd, buf, JSON_FILE_BUF_SIZE)) > 0) | while ((ret = read(fd, buf, JSON_FILE_BUF_SIZE)) > 0) | ||||
{ | { | ||||
printbuf_memappend(pb, buf, ret); | |||||
if (printbuf_memappend(pb, buf, ret) < 0) | |||||
{ | |||||
_json_c_set_last_err("json_object_from_fd_ex: error reading fd %d: %s\n", | |||||
fd, strerror(errno)); | |||||
json_tokener_free(tok); | |||||
printbuf_free(pb); | |||||
return NULL; | |||||
} | |||||
} | } | ||||
if (ret < 0) | if (ret < 0) | ||||
{ | { | ||||
@@ -15,6 +15,7 @@ | |||||
#include "config.h" | #include "config.h" | ||||
#include <errno.h> | |||||
#include <limits.h> | #include <limits.h> | ||||
#include <stdio.h> | #include <stdio.h> | ||||
#include <stdlib.h> | #include <stdlib.h> | ||||
@@ -56,6 +57,8 @@ struct printbuf *printbuf_new(void) | |||||
* | * | ||||
* If the current size is large enough, nothing is changed. | * If the current size is large enough, nothing is changed. | ||||
* | * | ||||
* If extension failed, errno is set to indicate the error. | |||||
* | |||||
* Note: this does not check the available space! The caller | * Note: this does not check the available space! The caller | ||||
* is responsible for performing those calculations. | * is responsible for performing those calculations. | ||||
*/ | */ | ||||
@@ -68,7 +71,10 @@ static int printbuf_extend(struct printbuf *p, int min_size) | |||||
return 0; | return 0; | ||||
/* Prevent signed integer overflows with large buffers. */ | /* Prevent signed integer overflows with large buffers. */ | ||||
if (min_size > INT_MAX - 8) | if (min_size > INT_MAX - 8) | ||||
{ | |||||
errno = EFBIG; | |||||
return -1; | return -1; | ||||
} | |||||
if (p->size > INT_MAX / 2) | if (p->size > INT_MAX / 2) | ||||
new_size = min_size + 8; | new_size = min_size + 8; | ||||
else { | else { | ||||
@@ -77,7 +83,7 @@ static int printbuf_extend(struct printbuf *p, int min_size) | |||||
new_size = min_size + 8; | new_size = min_size + 8; | ||||
} | } | ||||
#ifdef PRINTBUF_DEBUG | #ifdef PRINTBUF_DEBUG | ||||
MC_DEBUG("printbuf_memappend: realloc " | |||||
MC_DEBUG("printbuf_extend: realloc " | |||||
"bpos=%d min_size=%d old_size=%d new_size=%d\n", | "bpos=%d min_size=%d old_size=%d new_size=%d\n", | ||||
p->bpos, min_size, p->size, new_size); | p->bpos, min_size, p->size, new_size); | ||||
#endif /* PRINTBUF_DEBUG */ | #endif /* PRINTBUF_DEBUG */ | ||||
@@ -92,7 +98,10 @@ int printbuf_memappend(struct printbuf *p, const char *buf, int size) | |||||
{ | { | ||||
/* Prevent signed integer overflows with large buffers. */ | /* Prevent signed integer overflows with large buffers. */ | ||||
if (size < 0 || size > INT_MAX - p->bpos - 1) | if (size < 0 || size > INT_MAX - p->bpos - 1) | ||||
{ | |||||
errno = EFBIG; | |||||
return -1; | return -1; | ||||
} | |||||
if (p->size <= p->bpos + size + 1) | if (p->size <= p->bpos + size + 1) | ||||
{ | { | ||||
if (printbuf_extend(p, p->bpos + size + 1) < 0) | if (printbuf_extend(p, p->bpos + size + 1) < 0) | ||||
@@ -112,7 +121,10 @@ int printbuf_memset(struct printbuf *pb, int offset, int charvalue, int len) | |||||
offset = pb->bpos; | offset = pb->bpos; | ||||
/* Prevent signed integer overflows with large buffers. */ | /* Prevent signed integer overflows with large buffers. */ | ||||
if (len < 0 || offset < -1 || len > INT_MAX - offset) | if (len < 0 || offset < -1 || len > INT_MAX - offset) | ||||
{ | |||||
errno = EFBIG; | |||||
return -1; | return -1; | ||||
} | |||||
size_needed = offset + len; | size_needed = offset + len; | ||||
if (pb->size < size_needed) | if (pb->size < size_needed) | ||||
{ | { | ||||
@@ -4,7 +4,7 @@ OK: correctly unable to parse contents of valid_nested.json with low max depth: | |||||
OK: json_object_from_file(./not_present.json) correctly returned NULL: json_object_from_file: error opening file ./not_present.json: ERRNO=ENOENT | OK: json_object_from_file(./not_present.json) correctly returned NULL: json_object_from_file: error opening file ./not_present.json: ERRNO=ENOENT | ||||
OK: json_object_from_fd(closed_fd), expecting NULL, EBADF, got:NULL, json_object_from_fd: error reading fd 10: ERRNO=EBADF | |||||
OK: json_object_from_fd(closed_fd), expecting NULL, EBADF, got:NULL, json_object_from_fd_ex: error reading fd 10: ERRNO=EBADF | |||||
OK: json_object_to_file(json.out, jso)=0 | OK: json_object_to_file(json.out, jso)=0 | ||||
file[json.out], size=336, contents={"foo":1234,"foo1":"abcdefghijklmnopqrstuvwxyz","foo2":"abcdefghijklmnopqrstuvwxyz","foo3":"abcdefghijklmnopqrstuvwxyz","foo4":"abcdefghijklmnopqrstuvwxyz","foo5":"abcdefghijklmnopqrstuvwxyz","foo6":"abcdefghijklmnopqrstuvwxyz","foo7":"abcdefghijklmnopqrstuvwxyz","foo8":"abcdefghijklmnopqrstuvwxyz","foo9":"abcdefghijklmnopqrstuvwxyz"} | file[json.out], size=336, contents={"foo":1234,"foo1":"abcdefghijklmnopqrstuvwxyz","foo2":"abcdefghijklmnopqrstuvwxyz","foo3":"abcdefghijklmnopqrstuvwxyz","foo4":"abcdefghijklmnopqrstuvwxyz","foo5":"abcdefghijklmnopqrstuvwxyz","foo6":"abcdefghijklmnopqrstuvwxyz","foo7":"abcdefghijklmnopqrstuvwxyz","foo8":"abcdefghijklmnopqrstuvwxyz","foo9":"abcdefghijklmnopqrstuvwxyz"} | ||||