Description
CBMC version: develop
Operating system: macOs Majave
Exact command line resulting in the issue: cbmc main.c --malloc-may-fail --malloc-fail-null --pointer-overflow-check --pointer-check
What behaviour did you expect: Consistent behaviour using malloc for large allocations.
What happened instead:
Consider the following example.
1 #include <stdlib.h>
2 #include <stdint.h>
3 #include <assert.h>
4
5 int main () {
6 size_t size;
7 //size = SIZE_MAX;
8 uint8_t *ptr = malloc(size);
9 __CPROVER_assume(ptr != NULL);
10 uint8_t *ptr_end = ptr + size;
11 assert(ptr <= ptr_end);
12 }
Running this example with cbmc main.c --malloc-may-fail --malloc-fail-null --pointer-overflow-check --pointer-check
leads to verification failure.
...
[main.pointer_arithmetic.16] line 11 pointer relation: dead object in ptr_end: SUCCESS
[main.pointer_arithmetic.17] line 11 pointer relation: pointer outside object bounds in ptr_end: FAILURE
[main.pointer_arithmetic.18] line 11 pointer relation: invalid integer address in ptr_end: SUCCESS
** 1 of 22 failed (2 iterations)
VERIFICATION FAILED
Running this example with cbmc main.c --pointer-overflow-check --pointer-check
leads to another kind of verification failure.
...
[main.pointer_arithmetic.4] line 10 pointer arithmetic: dead object in ptr + (signed long int)size: SUCCESS
[main.pointer_arithmetic.5] line 10 pointer arithmetic: pointer outside object bounds in ptr + (signed long int)size: FAILURE
[main.pointer_arithmetic.6] line 10 pointer arithmetic: invalid integer address in ptr + (signed long int)size: SUCCESS
...
[main.pointer_arithmetic.16] line 11 pointer relation: dead object in ptr_end: SUCCESS
[main.pointer_arithmetic.17] line 11 pointer relation: pointer outside object bounds in ptr_end: FAILURE
[main.pointer_arithmetic.18] line 11 pointer relation: invalid integer address in ptr_end: SUCCESS
** 2 of 22 failed (3 iterations)
VERIFICATION FAILED
If we include line 7, we get different results too.
Using cbmc main.c --malloc-may-fail --malloc-fail-null --pointer-overflow-check --pointer-check
.
...
[main.pointer_arithmetic.14] line 11 pointer relation: pointer invalid in ptr_end: SUCCESS
[main.pointer_arithmetic.15] line 11 pointer relation: deallocated dynamic object in ptr_end: SUCCESS
[main.pointer_arithmetic.16] line 11 pointer relation: dead object in ptr_end: SUCCESS
[main.pointer_arithmetic.17] line 11 pointer relation: pointer outside object bounds in ptr_end: SUCCESS
[main.pointer_arithmetic.18] line 11 pointer relation: invalid integer address in ptr_end: SUCCESS
** 0 of 22 failed (1 iterations)
VERIFICATION SUCCESSFUL
Using cbmc main.c --pointer-overflow-check --pointer-check
.
CBMC version 5.30.1 (cbmc-5.21.0-1027-g64974b018-dirty) 64-bit x86_64 linux
Parsing main.c
Converting
Type-checking main
Generating GOTO Program
Adding CPROVER library (x86_64)
Removal of function pointers and virtual functions
Generic Property Instrumentation
Running with 8 object bits, 56 offset bits (default)
Starting Bounded Model Checking
Runtime Symex: 0.00208319s
size of program expression: 87 steps
simple slicing removed 5 assignments
Generated 20 VCC(s), 3 remaining after simplification
Runtime Postprocess Equation: 1.2301e-05s
Passing problem to propositional reduction
converting SSA
array too large for flattening
Why do we have completely different results here? Am I missing something? What is CBMC behaviour for allocation attempts where size
is larger than __CPROVER_max_malloc_size
? What is the definition of __CPROVER_max_malloc_size
?
Often, we need to use in our proofs the following trick
/**
* CBMC has an internal representation in which each object has an index and a (signed) offset
* A buffer cannot be larger than the max size of the offset
* The Makefile is expected to set CBMC_OBJECT_BITS to the value of --object-bits
*/
#define MAX_MALLOC (SIZE_MAX >> (CBMC_OBJECT_BITS + 1))
size_t size;
__CPROVER_assume(size < MAX_MALLOC);
void *ptr = malloc(size);
Why should this be even necessary here? Wouldn't malloc take care of such cases?
Activity
feliperodri commentedon May 19, 2021
The counterexample when running
cbmc main.c --malloc-may-fail --malloc-fail-null --pointer-overflow-check --pointer-check --trace
on this example also raise some questions.Why does CBMC have a negative number as offset in this case?
SaswatPadhi commentedon May 19, 2021
IMO, the most worrying issue here is the crash. CBMC should never crash.
This PR looks related: #5210.
When using
--malloc-fail-assert
, as used inregression/cbmc/malloc-too-large
introduced in this PR, CBMC doesn't crash:feliperodri commentedon May 19, 2021
Without the flag
--malloc-fail-assert
, I wouldn’t expect[malloc.assertion.1] line 26 max allocation size exceeded: FAILURE
.malloc
should fail to allocate a huge chunk of memory, and return NULL.tautschnig commentedon May 19, 2021
@SaswatPadhi Could you please elaborate on the following:
Which of the examples provided above results in some kind of "crash?"
tautschnig commentedon May 19, 2021
@feliperodri @SaswatPadhi Thank you for the detailed report. Apart from my above question: I'll try to figure out whether there are bugs hidden in here that aren't just down to the object:offset encoding. That I'm trying to get rid of in #1086, which I'm (very slowly) making progress on.
SaswatPadhi commentedon May 19, 2021
I meant when running CBMC without
--malloc-fail-assert
flag:cbmc ~/test.c
May be "crash" is the wrong word here, but this does look like an internal exception or a crash ...
An assertion failure on whatever property we expect
malloc
to always satisfy, may be some size bound in this case, would be a much better output.tautschnig commentedon May 19, 2021
Ah, understood. That would be fixed by #2108, which we should perhaps merge (after rebasing) as-is and not wait for #1874 to be fixed first.
SaswatPadhi commentedon May 19, 2021
Thanks for looking into this!
Yes, if #1874 is an optimization PR (which it looks like, from the title), then let's please merge #2108 first.
Enable and use the automatic limit for array-as-uninterpreted-function
5 remaining items