What's happening
When you pass {"a":{"":false}} to json_scanf, it internally calls json_unescape with a null pointer, then does pointer arithmetic on it. That's UB and it crashes every time.
The call chain is: json_scanf → json_vscanf → json_scanf_cb → json_unescape (frozen.c:899, frozen.c:924)
Crash Input:
\"VVVVVVVVVVVVVVVVVVVVVVVVVVVV)"\u0041\u00:1},{"i""
Crashes immediately with a UBSan abort (SIGILL). Tested on clang 18 with -fsanitize=address,undefined.
Note
This is 18 bytes of totally normal JSON. Any application using json_scanf with %Q that might receive nested objects with empty string keys will hit this in production — no adversarial input required.
What's happening
When you pass
{"a":{"":false}}tojson_scanf, it internally callsjson_unescapewith a null pointer, then does pointer arithmetic on it. That's UB and it crashes every time.The call chain is:
json_scanf→json_vscanf→json_scanf_cb→json_unescape(frozen.c:899, frozen.c:924)Crashes immediately with a UBSan abort (SIGILL). Tested on clang 18 with
-fsanitize=address,undefined.Note
This is 18 bytes of totally normal JSON. Any application using
json_scanfwith%Qthat might receive nested objects with empty string keys will hit this in production — no adversarial input required.