-
Notifications
You must be signed in to change notification settings - Fork 3.5k
Undefined-Behavior (Float-Cast-Overflow) in cJSON_CreateNumber via NaN values #999
Description
Undefined-Behavior (Float-Cast-Overflow) in cJSON_CreateNumber via NaN values
Version
v1.7.15-52-gb2890c8
Description
An Undefined-Behavior bug exists in cJSON's cJSON_CreateNumber function located at cJSON.c:2524. This occurs when a NaN (Not-a-Number) or Infinity floating-point value is passed to the function (e.g. through array creation functions like cJSON_CreateFloatArray).
Vulnerability Type: Undefined-Behavior (Float-Cast-Overflow / Float out of range of int)
Root Cause: When a NaN floating-point value is passed into cJSON_CreateNumber(double num), it bypasses the two saturation checks (num >= INT_MAX and num <= (double)INT_MIN) because relational comparisons with NaN generally evaluate to false. It then falls through into the else branch, attempting to cast the NaN value into an int, which triggers undefined behavior in C/C++.
Vulnerable Code in cJSON.c:
CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num)
{
cJSON *item = cJSON_New_Item(&global_hooks);
if(item)
{
item->type = cJSON_Number;
item->valuedouble = num;
/* use saturation in case of overflow */
if (num >= INT_MAX) // comparisons with `NaN` always return false
{
item->valueint = INT_MAX;
}
else if (num <= (double)INT_MIN) // comparisons with `NaN` always return false
{
item->valueint = INT_MIN;
}
else
{
item->valueint = (int)num; // BUG: attempts to cast NaN/Inf to int triggering UB
}
}
return item;
}PoC Code
#include <cmath>
#include <vector>
#include <fuzzer/FuzzedDataProvider.h>
extern "C" {
#include "cjson/cJSON.h"
}
extern "C" int LLVMFuzzerTestOneInput(uint8_t *data, int size) {
FuzzedDataProvider fdp(data, size);
int array_size = 1;
std::vector<float> float_values = { NAN };
// This call triggers the Undefined Behavior (Float Cast Overflow)
// nested in cJSON_CreateNumber!
cJSON *float_array = cJSON_CreateFloatArray(float_values.data(), array_size);
return 0;
}
int main() {
uint8_t data[10];
LLVMFuzzerTestOneInput(data, 10);
return 0;
}Reproduction Steps
clang++ -g -O0 -fsanitize=address,undefined,fuzzer -fno-omit-frame-pointer \
-I<include> \
poc.cpp -o poc \
libcjson.a
export UBSAN_OPTIONS=print_stacktrace=1
./pocStack Trace
./poc
/root/src/cjson/cJSON.c:2524:30: runtime error: nan is outside the range of representable values of type 'int'
#0 0x55c6ee9ebc05 in cJSON_CreateNumber /root/src/cjson/cJSON.c:2524:30
#1 0x55c6ee9ef7d3 in cJSON_CreateFloatArray /root/src/cjson/cJSON.c:2677:13
#2 0x55c6ee9e0333 in LLVMFuzzerTestOneInput /root/FuzzAgent/output/cjson/crash_reports/crash_000/poc.cpp:18:24
#3 0x55c6ee9e056f in main /root/FuzzAgent/output/cjson/crash_reports/crash_000/poc.cpp:25:5
#4 0x7036a4be11c9 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
#5 0x7036a4be128a in __libc_start_main csu/../csu/libc-start.c:360:3
#6 0x55c6ee8f53f4 in _start (/root/FuzzAgent/output/cjson/crash_reports/crash_000/poc+0x3e3f4) (BuildId: b9bd84db748d3423672a09e5113f9577174c6452)
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior /root/src/cjson/cJSON.c:2524:30