analyzer: handle memmove like memcpy

gcc/analyzer/ChangeLog:
	* region-model-impl-calls.cc (class kf_memcpy): Rename to...
	(class kf_memcpy_memmove): ...this.
	(kf_memcpy::impl_call_pre): Rename to...
	(kf_memcpy_memmove::impl_call_pre): ...this, and check the src for
	poison.
	(register_known_functions): Update for above renaming, and
	register BUILT_IN_MEMMOVE and BUILT_IN_MEMMOVE_CHK.

gcc/testsuite/ChangeLog:
	* gcc.dg/analyzer/memcpy-1.c (test_8a, test_8b): New tests.
	* gcc.dg/analyzer/memmove-1.c: New test, based on memcpy-1.c
	* gcc.dg/analyzer/out-of-bounds-1.c (test7): Update expected
	result for uninit srcBuf.
	* gcc.dg/analyzer/out-of-bounds-5.c (test8, test9): Add
	dg-warnings for memcpy from uninit src vla.
	* gcc.dg/analyzer/pr104308.c (test_memmove_within_uninit):
	Expect creation point note to be missing on riscv*-*-*.

Signed-off-by: David Malcolm <dmalcolm@redhat.com>
This commit is contained in:
David Malcolm 2022-12-08 21:19:23 -05:00
parent 2996b5c053
commit cf80a23e19
6 changed files with 212 additions and 8 deletions

View File

@ -246,10 +246,12 @@ kf_malloc::impl_call_pre (const call_details &cd) const
}
}
/* Handler for "memcpy" and "__builtin_memcpy". */
// TODO: complain about overlapping src and dest.
/* Handler for "memcpy" and "__builtin_memcpy",
"memmove", and "__builtin_memmove". */
/* TODO: complain about overlapping src and dest for the memcpy
variants. */
class kf_memcpy : public known_function
class kf_memcpy_memmove : public known_function
{
public:
bool matches_call_types_p (const call_details &cd) const final override
@ -263,7 +265,7 @@ public:
};
void
kf_memcpy::impl_call_pre (const call_details &cd) const
kf_memcpy_memmove::impl_call_pre (const call_details &cd) const
{
const svalue *dest_ptr_sval = cd.get_arg_svalue (0);
const svalue *src_ptr_sval = cd.get_arg_svalue (1);
@ -285,6 +287,8 @@ kf_memcpy::impl_call_pre (const call_details &cd) const
= mgr->get_sized_region (dest_reg, NULL_TREE, num_bytes_sval);
const svalue *src_contents_sval
= model->get_store_value (sized_src_reg, cd.get_ctxt ());
model->check_for_poison (src_contents_sval, cd.get_arg_tree (1),
cd.get_ctxt ());
model->set_value (sized_dest_reg, src_contents_sval, cd.get_ctxt ());
}
@ -927,8 +931,10 @@ register_known_functions (known_function_manager &kfm)
kfm.add (BUILT_IN_EXPECT_WITH_PROBABILITY, make_unique<kf_expect> ());
kfm.add (BUILT_IN_FREE, make_unique<kf_free> ());
kfm.add (BUILT_IN_MALLOC, make_unique<kf_malloc> ());
kfm.add (BUILT_IN_MEMCPY, make_unique<kf_memcpy> ());
kfm.add (BUILT_IN_MEMCPY_CHK, make_unique<kf_memcpy> ());
kfm.add (BUILT_IN_MEMCPY, make_unique<kf_memcpy_memmove> ());
kfm.add (BUILT_IN_MEMCPY_CHK, make_unique<kf_memcpy_memmove> ());
kfm.add (BUILT_IN_MEMMOVE, make_unique<kf_memcpy_memmove> ());
kfm.add (BUILT_IN_MEMMOVE_CHK, make_unique<kf_memcpy_memmove> ());
kfm.add (BUILT_IN_MEMSET, make_unique<kf_memset> ());
kfm.add (BUILT_IN_MEMSET_CHK, make_unique<kf_memset> ());
kfm.add (BUILT_IN_REALLOC, make_unique<kf_realloc> ());

View File

@ -166,3 +166,17 @@ void test_7b (void *src, size_t sz)
{
memcpy ((void *)"hello world", src, sz); /* { dg-warning "write to string literal" } */
}
/* memcpy from uninitialized buffer. */
void test_8a (void *dst)
{
char src[16];
memcpy (dst, src, 16); /* { dg-warning "use of uninitialized value" } */
}
void test_8b (void *dst, size_t n)
{
char src[16];
memcpy (dst, src, n); /* { dg-warning "use of uninitialized value" } */
}

View File

@ -0,0 +1,182 @@
#include <string.h>
#include "analyzer-decls.h"
/* Function for thwarting expansion of memmove by optimizer. */
typedef void * (*memmove_t) (void *dst, const void *src, size_t n);
static memmove_t __attribute__((noinline))
get_memmove (void)
{
return memmove;
}
void *test_1 (void *dst, void *src, size_t n)
{
void *result = memmove (dst, src, n);
__analyzer_eval (result == dst); /* { dg-warning "TRUE" } */
return result;
}
void *test_1a (void *dst, void *src, size_t n)
{
void *result = __memmove_chk (dst, src, n, -1);
__analyzer_eval (result == dst); /* { dg-warning "TRUE" } */
return result;
}
void *test_1b (void *dst, void *src, size_t n)
{
memmove_t fn = get_memmove ();
void *result = fn (dst, src, n);
__analyzer_eval (result == dst); /* { dg-warning "TRUE" } */
return result;
}
void test_2 (int i)
{
int j;
memmove (&j, &i, sizeof (int));
__analyzer_eval (i == j); /* { dg-warning "TRUE" } */
}
void test_2a (int i)
{
int j;
__memmove_chk (&j, &i, sizeof (int), sizeof (int));
__analyzer_eval (i == j); /* { dg-warning "TRUE" } */
}
void test_2b (int i)
{
int j;
memmove_t fn = get_memmove ();
fn (&j, &i, sizeof (int));
__analyzer_eval (i == j); /* { dg-warning "TRUE" } */
}
void test_3 (void *src, size_t n)
{
char buf[40], other[40];
buf[0] = 'a';
other[0] = 'b';
__analyzer_eval (buf[0] == 'a'); /* { dg-warning "TRUE" } */
__analyzer_eval (other[0] == 'b'); /* { dg-warning "TRUE" } */
memmove (buf, src, n);
__analyzer_eval (buf[0] == 'a'); /* { dg-warning "UNKNOWN" } */
__analyzer_eval (other[0] == 'b'); /* { dg-warning "TRUE" } */
}
void test_3b (void *src, size_t n)
{
char buf[40], other[40];
memmove_t fn = get_memmove ();
buf[0] = 'a';
other[0] = 'b';
__analyzer_eval (buf[0] == 'a'); /* { dg-warning "TRUE" } */
__analyzer_eval (other[0] == 'b'); /* { dg-warning "TRUE" } */
fn (buf, src, n);
__analyzer_eval (buf[0] == 'a'); /* { dg-warning "UNKNOWN" } */
__analyzer_eval (other[0] == 'b'); /* { dg-warning "TRUE" } */
}
/* Overwriting a zeroed buffer, then memmove of the result. */
void test_4 (int a, int b)
{
int src[1024];
int dst[1024];
memset (src, 0, sizeof (src));
src[42] = a;
src[100] = b;
__analyzer_eval (src[0] == 0); /* { dg-warning "TRUE" } */
__analyzer_eval (src[42] == a); /* { dg-warning "TRUE" } */
__analyzer_eval (src[100] == b); /* { dg-warning "TRUE" } */
__analyzer_eval (src[1023] == 0); /* { dg-warning "TRUE" } */
memmove (dst, src, sizeof (src));
__analyzer_eval (dst[0] == 0); /* { dg-warning "TRUE" } */
__analyzer_eval (dst[42] == a); /* { dg-warning "TRUE" } */
__analyzer_eval (dst[100] == b); /* { dg-warning "TRUE" } */
__analyzer_eval (dst[1023] == 0); /* { dg-warning "TRUE" } */
}
void test_4b (int a, int b)
{
int src[1024];
int dst[1024];
memmove_t fn = get_memmove ();
memset (src, 0, sizeof (src));
src[42] = a;
src[100] = b;
__analyzer_eval (src[0] == 0); /* { dg-warning "TRUE" } */
__analyzer_eval (src[42] == a); /* { dg-warning "TRUE" } */
__analyzer_eval (src[100] == b); /* { dg-warning "TRUE" } */
__analyzer_eval (src[1023] == 0); /* { dg-warning "TRUE" } */
fn (dst, src, sizeof (src));
__analyzer_eval (dst[0] == 0); /* { dg-warning "TRUE" } */
__analyzer_eval (dst[42] == a); /* { dg-warning "TRUE" } */
__analyzer_eval (dst[100] == b); /* { dg-warning "TRUE" } */
__analyzer_eval (dst[1023] == 0); /* { dg-warning "TRUE" } */
}
/* Populating a buffer from an unknown buffer. */
void test_5 (void *src, size_t sz)
{
char dst[1024];
memmove (dst, src, sizeof (dst));
__analyzer_eval (dst[0] == 0); /* { dg-warning "UNKNOWN" } */
__analyzer_eval (dst[1023] == 0); /* { dg-warning "UNKNOWN" } */
}
void test_5b (void *src, size_t sz)
{
char dst[1024];
memmove_t fn = get_memmove ();
fn (dst, src, sizeof (dst));
__analyzer_eval (dst[0] == 0); /* { dg-warning "UNKNOWN" } */
__analyzer_eval (dst[1023] == 0); /* { dg-warning "UNKNOWN" } */
}
/* Zero-sized memmove. */
void test_6 (void *dst, void *src)
{
memmove (dst, src, 0);
}
void test_6b (void *dst, void *src)
{
memmove_t fn = get_memmove ();
fn (dst, src, 0);
}
/* memmove to string literal. */
void test_7 (void *src, size_t sz)
{
memmove ((void *)"hello world", src, sz); /* { dg-warning "write to string literal" } */
}
void test_7b (void *src, size_t sz)
{
memmove ((void *)"hello world", src, sz); /* { dg-warning "write to string literal" } */
}
/* memcpy from uninitialized buffer. */
void test_8a (void *dst)
{
char src[16];
memmove (dst, src, 16); /* { dg-warning "use of uninitialized value" } */
}
void test_8b (void *dst, size_t n)
{
char src[16];
memmove (dst, src, n); /* { dg-warning "use of uninitialized value" } */
}

View File

@ -117,6 +117,6 @@ void test7 (void)
// TODO: Should we handle widening_svalues as a follow-up?
/* { dg-warning "over-read" "warning" { xfail *-*-* } test7 } */
/* { dg-warning "use of uninitialized value" "uninit warning" { target *-*-* } test7 } */
/* { dg-warning "overflow" "warning" { xfail *-*-* } test7 } */
/* { dg-message "" "note" { xfail *-*-* } test7 } */
}

View File

@ -69,6 +69,7 @@ void test8 (size_t size, size_t offset)
char dst[size];
memcpy (dst, src, size + offset); /* { dg-line test8 } */
/* { dg-warning "over-read" "warning" { target *-*-* } test8 } */
/* { dg-warning "use of uninitialized value" "warning" { target *-*-* } test8 } */
/* { dg-warning "overflow" "warning" { target *-*-* } test8 } */
}
@ -78,6 +79,7 @@ void test9 (size_t size, size_t offset)
int32_t dst[size];
memcpy (dst, src, 4 * size + 1); /* { dg-line test9 } */
/* { dg-warning "over-read" "warning" { target *-*-* } test9 } */
/* { dg-warning "use of uninitialized value" "warning" { target *-*-* } test9 } */
/* { dg-warning "overflow" "warning" { target *-*-* } test9 } */
}

View File

@ -6,7 +6,7 @@
int test_memmove_within_uninit (void)
{
char s[5]; /* { dg-message "region created on stack here" } */
char s[5]; /* { dg-message "region created on stack here" "" { xfail riscv*-*-* } } */
memmove(s, s + 1, 2); /* { dg-warning "use of uninitialized value" } */
return 0;
}