Description
CBMC Bug Report: Performance Cliff and Excessive Memory Usage at --unwind 6
Performance Degradation and Memory Explosion in CBMC 6.4.1 on macOS ARM64 with --unwind 6
When Verifying cJSON.c
.
Please advise if this is a known bug, and suggest patches or workarounds. I’m happy to provide more logs, test cases, or assist in debugging.
Description
I am experiencing a severe performance issue with CBMC 6.4.1 when increasing the --unwind
value from 5 to 6 while verifying a C program that uses the cJSON
library (cJSON.c
). For --unwind ≤ 5
, CBMC completes execution within seconds with reasonable memory usage. However, at --unwind 6
, CBMC runs for over 10 hours without producing a result, and memory usage exceeds 100GB, which is unexpected and appears to indicate a state space explosion or bug.
Steps to Reproduce
-
Environment:
- OS: macOS (Apple M3)
- CBMC Version: 6.4.1
-
Project Structure:
- Source files:
main.c
: Contains a harness function provided below.lib/cJSON/cJSON.c
: ThecJSON
JSON parsing library (version 1.7.99, linked from https://github.com/DaveGamble/cJSON/blob/master/cJSON.c). The file also contains manually inserted assertions.lib/cJSON/cJSON_Utils.c
: Utilities forcJSON
.lib/cJSON/cJSON.h
andlib/cJSON/cJSON_Utils.h
: Corresponding headers (assumed to be included).
- Source files:
-
Harness and Command:
-
Harness Code (in
main.c
):#include <assert.h> #include <string.h> #define MAX_INPUT_SIZE 512 // Forward declaration char *process_command(const char *command); void harness() { char buffer[3][MAX_INPUT_SIZE]; // Process up to 3 commands for (int i = 0; i < 3; i++) { __CPROVER_assume(buffer[i][MAX_INPUT_SIZE - 1] == '\0'); __CPROVER_assume(strncmp(buffer[i], "ADD", 3) == 0 || strncmp(buffer[i], "MOD", 3) == 0 || strncmp(buffer[i], "GET id", 6) == 0 || strncmp(buffer[i], "EXIT", 4) == 0); char *response = process_command(buffer[i]); if (response) { assert(response[strlen(response)] == '\0'); free(response); } } }
-
Command for Low Unwind (Works Fine):
cbmc -Ilib/cJSON main.c lib/cJSON/cJSON.c lib/cJSON/cJSON_Utils.c --function harness --trace --no-standard-checks --no-built-in-assertions --unwind 5
- Completes in seconds with normal memory usage.
-
Command for High Unwind (Fails):
cbmc -Ilib/cJSON app/main.c lib/cJSON/cJSON.c lib/cJSON/cJSON_Utils.c --function harness --trace --no-standard-checks --no-built-in-assertions --unwind 6
- Runs for >10 hours without completing and consumes >100GB of memory.
-
-
Verbose Output (With
--verbosity 9
):- When running with
--unwind 6
and--verbosity 9
, the following lines seem to repeat indefinitely, suggesting a potentially infinite loop or excessive unrolling:Unwinding loop cJSON_Delete.0 iteration 5 file lib/cJSON/cJSON.c line 260 function cJSON_Delete thread 0 Unwinding recursion cJSON_Delete iteration 5 Unwinding recursion cJSON_Delete iteration 6 Not unwinding recursion cJSON_Delete iteration 7 Unwinding loop cJSON_Delete.0 iteration 1 file lib/cJSON/cJSON.c line 260 function cJSON_Delete thread 0 Not unwinding recursion cJSON_Delete iteration 7 Unwinding loop cJSON_Delete.0 iteration 2 file lib/cJSON/cJSON.c line 260 function cJSON_Delete thread 0 Not unwinding recursion cJSON_Delete iteration 7 [And so on...]
- For a single-command harness (processing one
process_command
call), the output is concise and fast:This completes quickly with low memory usage.Unwinding recursion cJSON_Delete iteration 1 Unwinding loop cJSON_Delete.0 iteration 1 file lib/cJSON/cJSON.c line 260 function cJSON_Delete thread 0 Unwinding loop cJSON_Delete.0 iteration 1 file lib/cJSON/cJSON.c line 260 function cJSON_Delete thread 0 ....
- When running with
Expected Behavior
- I expected runtime and memory usage to increase gradually with each increment of
--unwind
(e.g., from 5 to 6), not exponentially. For--unwind 6
, CBMC should complete within minutes (or at most hours) with reasonable memory usage (e.g., <10GB), not >10 hours and >100GB.
Actual Behavior
- At
--unwind 6
, CBMC hangs for >10 hours, consumes >100GB of memory, and appears stuck in excessive unrolling ofcJSON_Delete
’s loop and recursion inlib/cJSON/cJSON.c
(line 260).
Additional Context
-
Assertions in
cJSON.c
:- I have manually injected three assertions in
cJSON.c
to test for vulnerabilities.
- I have manually injected three assertions in
-
Workarounds Tried:
- Constrained
buffer[i]
to specific commands (ADD
,MOD
,GET id
,EXIT
) using__CPROVER_assume
, but the issue persists. - Used a single-command harness, which works fast but doesn’t test the multi-command sequence needed for potential bugs.
- Constrained
Impact
This performance cliff prevents effective verification of multi-command sequences.
Possible Cause
- The
cJSON_Delete
function (line 260 incJSON.c
) contains awhile
loop and recursive calls (cJSON_Delete(item->child)
), which CBMC unrolls excessively at--unwind 6
. This likely causes a state space explosion.