Undefined-Behavior (Float-Cast-Overflow) in cJSON_CreateNumber via NaN values
Nobody has claimed this yet.
Assessment
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Newbie friendliness
- 68/100
Research direction
Start at cJSON.c:2524 in cJSON_CreateNumber and trace the cJSON_CreateFloatArray path described by the issue. Reproduce with the supplied NaN example under the shown Clang AddressSanitizer/UndefinedBehaviorSanitizer command; done means NaN and Infinity inputs no longer trigger the reported float-cast-overflow diagnostic.
Written by the indexing model from the issue text.
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
./poc
Stack 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
- Dominant language
- C
- Stars
- 13k
- Forks
- 3.5k
- PR merge metrics
- No merged PRs in 30d
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from DaveGamble/cJSON
-
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
DaveGamble/cJSON#1082 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
DaveGamble/cJSON#1081 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
DaveGamble/cJSON#1074 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
DaveGamble/cJSON#1071 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
DaveGamble/cJSON#1067 ·
All issues in DaveGamble/cJSON
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
-
level/task module/gcp type/bug
Difficulty 2/5 1-3 hours Newbie friendliness 85/100
-
Difficulty 1/5 Under an hour Newbie friendliness 86/100
hapostgres/pg_auto_failover#1190 ·
-
docs
Difficulty 1/5 Under an hour Newbie friendliness 85/100
-
P3 sonic-vpp
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
sonic-net/sonic-buildimage#29662 ·