Skip to content

Commit cce4e8e

Browse files
committed
Malloc should fail on too large assertions
1: introduce `pointer-width` and `object-width` as new `cprover-start` variables 2: assert-then-assume about the size limit in our `stdlib.c` implementation of `malloc` 3: add some tests, and assume about the alloc size in others Note: this PR removes the C90-style structhack in test `cbmc/Pointer_byte_extract5`. This may need to be amended if SV-Comp decides they want to keep it.
1 parent be84cd1 commit cce4e8e

File tree

19 files changed

+133
-24
lines changed

19 files changed

+133
-24
lines changed

regression/cbmc-library/memcpy-04/main.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@ void publish(info_t *info)
1111
{
1212
size_t size;
1313
__CPROVER_assume(size >= info->len);
14+
// alloc size is arbitrary but no larger than what CBMC can represent
15+
size_t pointer_width = sizeof(void *) * 8;
16+
size_t object_bits = 8; // by default
17+
size_t cbmc_max_alloc_size = ((size_t)1 << (pointer_width - object_bits)) - 1;
18+
__CPROVER_assume(size <= cbmc_max_alloc_size);
1419
unsigned char *buffer = malloc(size);
1520
memcpy(buffer, info->name, info->len);
1621
if(info->len > 42)
@@ -24,6 +29,13 @@ void main()
2429
{
2530
info_t info;
2631
size_t name_len;
32+
33+
// alloc size is arbitrary but no larger than what CBMC can represent
34+
size_t pointer_width = sizeof(void *) * 8;
35+
size_t object_bits = 8; // by default
36+
size_t cbmc_max_alloc_size = ((size_t)1 << (pointer_width - object_bits)) - 1;
37+
__CPROVER_assume(name_len <= cbmc_max_alloc_size);
38+
2739
info.name = malloc(name_len);
2840
info.len = name_len;
2941
if(name_len > 42)

regression/cbmc-library/memcpy-04/test.desc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
CORE
22
main.c
33

4-
^\[publish.assertion.1\] line 18 should pass: SUCCESS$
5-
^\[publish.assertion.2\] line 19 should fail: FAILURE$
4+
^\[publish.assertion.\d+\] line \d+ should pass: SUCCESS$
5+
^\[publish.assertion.\d+\] line \d+ should fail: FAILURE$
66
^\*\* 1 of \d+ failed
77
^VERIFICATION FAILED$
88
^EXIT=10$

regression/cbmc/Malloc25/main.c

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@
22

33
int main(int argc, char *argv[])
44
{
5-
int *p = malloc((size_t)argc * (size_t)argc * sizeof(int));
5+
size_t alloc_size = (size_t)argc * (size_t)argc * sizeof(int);
6+
// alloc size is arbitrary but no larger than what CBMC can represent
7+
size_t pointer_width = sizeof(void *) * 8;
8+
size_t object_bits = 8; // by default
9+
size_t cbmc_max_alloc_size = ((size_t)1 << (pointer_width - object_bits)) - 1;
10+
__CPROVER_assume(alloc_size <= cbmc_max_alloc_size);
11+
12+
int *p = malloc(alloc_size);
613
return 0;
714
}

regression/cbmc/Pointer_byte_extract5/main.i

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,13 @@ typedef union
1111
typedef struct
1212
{
1313
int Count;
14-
Union List[1];
14+
Union List[0];
1515
} Struct3;
1616
#pragma pack(pop)
1717

1818
int main()
1919
{
20-
Struct3 *p = malloc(sizeof(Struct3) + sizeof(Union));
20+
Struct3 *p = malloc(sizeof(Struct3) + 2 * sizeof(Union));
2121
p->Count = 3;
2222
int po=0;
2323

regression/cbmc/Pointer_byte_extract5/no-simplify.desc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ main.i
44
^EXIT=10$
55
^SIGNAL=0$
66
array\.List dynamic object upper bound in p->List\[2\]: FAILURE
7-
\*\* 1 of 14 failed
7+
\*\* 1 of 15 failed
88
--
99
^warning: ignoring
1010
--

regression/cbmc/Pointer_byte_extract5/test.desc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ main.i
44
^EXIT=10$
55
^SIGNAL=0$
66
array\.List dynamic object upper bound in p->List\[2\]: FAILURE
7-
\*\* 1 of 11 failed
7+
\*\* 1 of 13 failed
88
--
99
^warning: ignoring
1010
--

regression/cbmc/array_constraints1/test.desc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@ main.c
44
^EXIT=10$
55
^SIGNAL=0$
66
^VERIFICATION FAILED$
7-
^\*\* 2 of 14
7+
^\*\* 2 of 15
88
--
99
^warning: ignoring
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#include <assert.h>
2+
#include <stdint.h>
3+
#include <stdlib.h>
4+
5+
int main()
6+
{
7+
size_t pointer_width = sizeof(void *) * 8;
8+
size_t object_bits = 8; // by default
9+
size_t cbmc_max_alloc_size = ((size_t)1 << (pointer_width - object_bits)) - 1;
10+
int *p = malloc(cbmc_max_alloc_size); // try to allocate exactly the max
11+
12+
return 0;
13+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
CORE
2+
largest_representable.c
3+
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^\[malloc.assertion.\d+\] line \d+ max allocation size exceeded: SUCCESS$
7+
^VERIFICATION SUCCESSFUL$
8+
--
9+
^warning: ignoring
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#include <assert.h>
2+
#include <stdint.h>
3+
#include <stdlib.h>
4+
5+
int main()
6+
{
7+
int *p = malloc(SIZE_MAX);
8+
9+
return 0;
10+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
CORE
2+
max_size.c
3+
4+
^EXIT=10$
5+
^SIGNAL=0$
6+
^\[malloc.assertion.\d+\] line \d+ max allocation size exceeded: FAILURE$
7+
^VERIFICATION FAILED$
8+
--
9+
^warning: ignoring
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#include <assert.h>
2+
#include <stdint.h>
3+
#include <stdlib.h>
4+
5+
int main()
6+
{
7+
size_t pointer_width = sizeof(void *) * 8;
8+
size_t object_bits = 8; // by default
9+
size_t cbmc_max_alloc_size = ((size_t)1 << (pointer_width - object_bits)) - 1;
10+
// try to allocate the smallest violating amount
11+
int *p = malloc(cbmc_max_alloc_size + 1);
12+
13+
return 0;
14+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
CORE
2+
one_byte_too_large.c
3+
4+
^EXIT=10$
5+
^SIGNAL=0$
6+
^\[malloc.assertion.\d+\] line \d+ max allocation size exceeded: FAILURE$
7+
^VERIFICATION FAILED$
8+
--
9+
^warning: ignoring

regression/cbmc/pointer-overflow1/test.desc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ main.c
55
^SIGNAL=0$
66
^\[main\.overflow\.\d+\] line \d+ (pointer )?arithmetic overflow on .*sizeof\(signed int\) .* : SUCCESS
77
^VERIFICATION FAILED$
8-
^\*\* 8 of 11 failed
8+
^\*\* 8 of 13 failed
99
--
1010
^\[main\.overflow\.\d+\] line \d+ (pointer )?arithmetic overflow on .*sizeof\(signed int\) .* : FAILURE
1111
^warning: ignoring

regression/cbmc/r_w_ok1/main.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,13 @@ int main()
1616
assert(__CPROVER_w_ok(p, sizeof(int)));
1717

1818
size_t n;
19+
20+
// n is arbitrary but no larger than what CBMC can represent
21+
size_t pointer_width = sizeof(void *) * 8;
22+
size_t object_bits = 8; // by default
23+
size_t cbmc_max_alloc_size = ((size_t)1 << (pointer_width - object_bits)) - 1;
24+
__CPROVER_assume(n <= cbmc_max_alloc_size);
25+
1926
char *arbitrary_size = malloc(n);
2027

2128
assert(__CPROVER_r_ok(arbitrary_size, n));

regression/cbmc/r_w_ok1/test.desc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ CORE
22
main.c
33

44
__CPROVER_[rw]_ok\(arbitrary_size, n \+ 1\): FAILURE$
5-
^\*\* 2 of 10 failed
5+
^\*\* 2 of 11 failed
66
^VERIFICATION FAILED$
77
^EXIT=10$
88
^SIGNAL=0$

src/ansi-c/ansi_c_internal_additions.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,10 @@ void ansi_c_internal_additions(std::string &code)
155155
"void *" CPROVER_PREFIX "allocate("
156156
CPROVER_PREFIX "size_t size, " CPROVER_PREFIX "bool zero);\n"
157157
"const void *" CPROVER_PREFIX "alloca_object = 0;\n"
158+
CPROVER_PREFIX "size_t " CPROVER_PREFIX "pointer_width="+
159+
std::to_string(config.ansi_c.pointer_width)+";\n"
160+
CPROVER_PREFIX "size_t " CPROVER_PREFIX "object_bits="+
161+
std::to_string(config.bv_encoding.object_bits)+";\n"
158162

159163
// this is ANSI-C
160164
"extern " CPROVER_PREFIX "thread_local const char __func__["

src/ansi-c/library/cprover.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ extern const void *__CPROVER_malloc_object;
1616
extern __CPROVER_size_t __CPROVER_malloc_size;
1717
extern _Bool __CPROVER_malloc_is_new_array;
1818
extern const void *__CPROVER_memory_leak;
19+
extern __CPROVER_size_t __CPROVER_pointer_width;
20+
extern __CPROVER_size_t __CPROVER_object_bits;
1921

2022
void __CPROVER_assume(__CPROVER_bool assumption) __attribute__((__noreturn__));
2123
void __CPROVER_assert(__CPROVER_bool assertion, const char *description);

src/ansi-c/library/stdlib.c

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -115,23 +115,36 @@ inline void *malloc(__CPROVER_size_t malloc_size)
115115
// realistically, malloc may return NULL,
116116
// and __CPROVER_allocate doesn't, but no one cares
117117
__CPROVER_HIDE:;
118-
void *malloc_res;
119-
malloc_res = __CPROVER_allocate(malloc_size, 0);
120118

121-
// make sure it's not recorded as deallocated
122-
__CPROVER_deallocated=(malloc_res==__CPROVER_deallocated)?0:__CPROVER_deallocated;
119+
__CPROVER_assert(
120+
malloc_size < ((__CPROVER_size_t)1
121+
<< (__CPROVER_pointer_width - __CPROVER_object_bits)),
122+
"max allocation size exceeded");
123+
__CPROVER_assume(
124+
malloc_size < ((__CPROVER_size_t)1
125+
<< (__CPROVER_pointer_width - __CPROVER_object_bits)));
126+
127+
void *malloc_res;
128+
malloc_res = __CPROVER_allocate(malloc_size, 0);
129+
130+
// make sure it's not recorded as deallocated
131+
__CPROVER_deallocated =
132+
(malloc_res == __CPROVER_deallocated) ? 0 : __CPROVER_deallocated;
133+
134+
// record the object size for non-determistic bounds checking
135+
__CPROVER_bool record_malloc = __VERIFIER_nondet___CPROVER_bool();
136+
__CPROVER_malloc_object =
137+
record_malloc ? malloc_res : __CPROVER_malloc_object;
138+
__CPROVER_malloc_size = record_malloc ? malloc_size : __CPROVER_malloc_size;
139+
__CPROVER_malloc_is_new_array =
140+
record_malloc ? 0 : __CPROVER_malloc_is_new_array;
123141

124-
// record the object size for non-determistic bounds checking
125-
__CPROVER_bool record_malloc=__VERIFIER_nondet___CPROVER_bool();
126-
__CPROVER_malloc_object=record_malloc?malloc_res:__CPROVER_malloc_object;
127-
__CPROVER_malloc_size=record_malloc?malloc_size:__CPROVER_malloc_size;
128-
__CPROVER_malloc_is_new_array=record_malloc?0:__CPROVER_malloc_is_new_array;
129-
130-
// detect memory leaks
131-
__CPROVER_bool record_may_leak=__VERIFIER_nondet___CPROVER_bool();
132-
__CPROVER_memory_leak=record_may_leak?malloc_res:__CPROVER_memory_leak;
142+
// detect memory leaks
143+
__CPROVER_bool record_may_leak = __VERIFIER_nondet___CPROVER_bool();
144+
__CPROVER_memory_leak =
145+
record_may_leak ? malloc_res : __CPROVER_memory_leak;
133146

134-
return malloc_res;
147+
return malloc_res;
135148
}
136149

137150
/* FUNCTION: __builtin_alloca */

0 commit comments

Comments
 (0)