Skip to content

Commit 1da804b

Browse files
tautschnigDaniel Kroening
authored and
Daniel Kroening
committed
Allocation size computation in calloc must not overflow
calloc guarantees that the requested number of bytes is allocated, or else NULL is returned. Since an overflowing multiplication would imply that the requested size can never be allocated, NULL must be returned (and we should not report an arithmetic overflow error). Fixes: #4606
1 parent 3037204 commit 1da804b

File tree

4 files changed

+32
-5
lines changed

4 files changed

+32
-5
lines changed
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#include <assert.h>
2+
#include <stdlib.h>
3+
4+
int main()
5+
{
6+
char *p = calloc(-1, -1);
7+
if(p)
8+
assert(p[0] == 0);
9+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
CORE
2+
main.c
3+
--unsigned-overflow-check --pointer-check
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
--
8+
^warning: ignoring

src/ansi-c/library/stdlib.c

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,20 @@ __CPROVER_bool __VERIFIER_nondet___CPROVER_bool();
6565

6666
inline void *calloc(__CPROVER_size_t nmemb, __CPROVER_size_t size)
6767
{
68+
__CPROVER_HIDE:;
69+
#pragma CPROVER check push
70+
#pragma CPROVER check disable "unsigned-overflow"
71+
if(__CPROVER_overflow_mult(nmemb, size))
72+
return (void *)0;
73+
// This is now safe; still do it within the scope of the pragma to avoid an
74+
// unnecessary assertion to be generated.
75+
__CPROVER_size_t alloc_size = nmemb * size;
76+
#pragma CPROVER check pop
77+
78+
void *malloc_res;
6879
// realistically, calloc may return NULL,
6980
// and __CPROVER_allocate doesn't, but no one cares
70-
__CPROVER_HIDE:;
71-
void *malloc_res;
72-
malloc_res = __CPROVER_allocate(nmemb * size, 1);
81+
malloc_res = __CPROVER_allocate(alloc_size, 1);
7382

7483
// make sure it's not recorded as deallocated
7584
__CPROVER_deallocated =
@@ -79,7 +88,7 @@ inline void *calloc(__CPROVER_size_t nmemb, __CPROVER_size_t size)
7988
__CPROVER_bool record_malloc = __VERIFIER_nondet___CPROVER_bool();
8089
__CPROVER_malloc_object =
8190
record_malloc ? malloc_res : __CPROVER_malloc_object;
82-
__CPROVER_malloc_size = record_malloc ? nmemb * size : __CPROVER_malloc_size;
91+
__CPROVER_malloc_size = record_malloc ? alloc_size : __CPROVER_malloc_size;
8392
__CPROVER_malloc_is_new_array =
8493
record_malloc ? 0 : __CPROVER_malloc_is_new_array;
8594

src/ansi-c/library_check.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ for f in "$@"; do
1111
perl -p -i -e 's/(__sync_)/s$1/' __libcheck.c
1212
perl -p -i -e 's/(__noop)/s$1/' __libcheck.c
1313
"$CC" -std=gnu99 -E -include library/cprover.h -D__CPROVER_bool=_Bool -D__CPROVER_thread_local=__thread -DLIBRARY_CHECK -o __libcheck.i __libcheck.c
14-
"$CC" -S -Wall -Werror -pedantic -Wextra -std=gnu99 __libcheck.i -o __libcheck.s -Wno-unused-label
14+
"$CC" -S -Wall -Werror -pedantic -Wextra -std=gnu99 __libcheck.i \
15+
-o __libcheck.s -Wno-unused-label -Wno-unknown-pragmas
1516
ec="${?}"
1617
rm __libcheck.s __libcheck.i __libcheck.c
1718
[ "${ec}" -eq 0 ] || exit "${ec}"

0 commit comments

Comments
 (0)