Skip to content

Undefined-Behavior (Float-Cast-Overflow) in cJSON_CreateNumber via NaN values #999

@FuzzAnything-ORG

Description

@FuzzAnything-ORG

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions