Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Exception when printing JSON string in ESP-IDF project with cJSON library #874

Open
huamuyichun opened this issue Jul 17, 2024 · 1 comment

Comments

@huamuyichun
Copy link

Bug Report

Required Info:

  • Operating System: Debian GNU/Linux 12 (bookworm)
  • FreeRTOS Version: V10.5.1 (ESP-IDF SMP modified)
  • ESP-IDF Version: v5.4-dev-78-gd4cd437ede-dirty
  • Commit hash: d4cd437ede613fffacc06ac6d6c93a083829022f
  • Component: cJSON

Steps to reproduce issue:

Hi, while fuzz testing FreeRTOS using Syzkaller, I encountered a KASAN heap-out-of-bounds error during testing, within the cJSON module.
I use cJSON_CreateObject()to get a json object, then add a string field, then generate JSON string. When I executing here, some wrongs occur.
This is my SPEC which can trigger the bug:

static long syz_cjson_print(volatile long a0)
{
    // Create a JSON object
    cJSON *json = cJSON_CreateObject();
    if (json == NULL) {
        return -1; // Return error code
    }

    // Add a string field
    cJSON_AddStringToObject(json, "key", "value");

    // Generate JSON string
    char *json_string = cJSON_Print(json);
    if (json_string == NULL) {
        cJSON_Delete(json);
        return -1; // Return error code
    }

    // Free JSON string memory
    cJSON_free(json_string);

    // Delete cJSON object
    cJSON_Delete(json);

    return 0; // Success
}

Error Report

I received the following error report during the fuzzing process:

This is the stack of calls:

Level: 0: /path/to/panic_handler.c : panic_handler : 132 
Level: 1: /path/to/panic_handler.c : xt_unhandled_exception : 242 
Level: 2: /path/to/xtensa_vectors.S : ?? : 805 
Level: 3: /path/to/panic.c : panic_abort : 463 
Level: 4: /path/to/esp_system_chip.c : esp_system_abort : 92 
Level: 5: /path/to/kasan.c : __asan_load1 : 185 
Level: 6: /path/to/cJSON.c : print_string_ptr : 935 
Level: 7: /path/to/cJSON.c : print_object : 1764 
Level: 8: /path/to/cJSON.c : print_value : 1438 
Level: 9: /path/to/cJSON.c : print : 1211 
Level: 10: /path/to/cJSON.c : cJSON_Print : 1259 
Level: 11: /path/to/common_freertos.h : syz_cjson_print : 1305 
Level: 12: /path/to/executor.h : execute_syscall : 347 
Level: 13: /path/to/executor.h : fuzz_start_one : 543 
Level: 14: /path/to/executor.h : executor_main : 579 
Level: 15: /path/to/hello_world_main.c : app_main : 64 
Level: 16: /path/to/app_startup.c : main_task : 208 
Level: 17: /path/to/port.c : vPortTaskWrapper : 134 
BUG: KASAN: heap-out-of-bounds in 0x400e1676
Read of size 1 at addr 0x3ffcadc8

Backtrace:
0x40081ecd:0x3ffc0e10 
0x4008c685:0x3ffc0e30 
0x400ee115:0x3ffc0e50 
0x400e1673:0x3ffc0e70 
0x400e20e0:0x3ffc0e90 
0x400e1d09:0x3ffc0eb0 
0x400e1dc0:0x3ffc0ed0 
0x400e2cf7:0x3ffc0fa0 
0x400e5711:0x3ffc0fc0 
0x400e3511:0x3ffc0ff0 
0x400e6b08:0x3ffc1040 
0x400e6bd9:0x3ffc11f0 
0x400e6da7:0x3ffc1210 
0x40152aab:0x3ffc12d0 
0x4008d35d:0x3ffc1300

It seems there is an illegal access at /path/to/cJSON.c in the print_string_ptr function. In line 935, and it is the loop's for (input_pointer = input; *input_pointer; input_pointer++) logical fault, you can see the ptr input_pointer will not stop until read the '\0', so if the string does not match the specification, which means it doesn't end with '\0', there will be problems with the loop. It will visit the illegal memory.
Not only that, but it is also possible that an invalid character was entered, and when the pointer moved to the invalid character, the loop could not process that character.
So the loop should add a check on the range of the string and an exception for invalid characters.

/* Render the cstring provided to an escaped version that can be printed. */
static cJSON_bool print_string_ptr(const unsigned char * const input, printbuffer * const output_buffer)
{
    ...

    /* set "flag" to 1 if something needs to be escaped */
    for (input_pointer = input; *input_pointer; input_pointer++)
    {
        switch (*input_pointer)
        {
            case '\"':
            case '\\':
            case '\b':
            case '\f':
            case '\n':
            case '\r':
            case '\t':
                /* one character escape sequence */
                escape_characters++;
                break;
            default:
                if (*input_pointer < 32)
                {
                    /* UTF-16 escape sequence uXXXX */
                    escape_characters += 5;
                }
                break;
        }
    }
    output_length = (size_t)(input_pointer - input) + escape_characters;

    ...
}

Expected Behavior

The program should execute without any memory errors or crashes.

Actual Behavior

The program crashes with a KASAN report indicating a heap-out-of-bounds error.

Additional Information

It would be greatly appreciated if you could review this bug report. Any suggestions or feedback you can provide would be very helpful. Thank you for your time.

@PeterAlfredLee
Copy link
Contributor

I'm confused by the fuzz test. The input volatile long a0 is not used. The test only creates a json object and prints it, then it frees both. How does this fuzzer work?

Besides this, I can not reproduce this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants