}
/* Handle the on_call_pre part of "memcpy" and "__builtin_memcpy". */
+// TODO: complain about overlapping src and dest.
void
region_model::impl_call_memcpy (const call_details &cd)
{
- const svalue *dest_sval = cd.get_arg_svalue (0);
+ const svalue *dest_ptr_sval = cd.get_arg_svalue (0);
+ const svalue *src_ptr_sval = cd.get_arg_svalue (1);
const svalue *num_bytes_sval = cd.get_arg_svalue (2);
- const region *dest_reg = deref_rvalue (dest_sval, cd.get_arg_tree (0),
+ const region *dest_reg = deref_rvalue (dest_ptr_sval, cd.get_arg_tree (0),
cd.get_ctxt ());
+ const region *src_reg = deref_rvalue (src_ptr_sval, cd.get_arg_tree (1),
+ cd.get_ctxt ());
- cd.maybe_set_lhs (dest_sval);
-
- if (tree num_bytes = num_bytes_sval->maybe_get_constant ())
- {
- /* "memcpy" of zero size is a no-op. */
- if (zerop (num_bytes))
- return;
- }
-
- check_region_for_write (dest_reg, cd.get_ctxt ());
+ cd.maybe_set_lhs (dest_ptr_sval);
- /* Otherwise, mark region's contents as unknown. */
- mark_region_as_unknown (dest_reg, cd.get_uncertainty ());
+ const region *sized_src_reg
+ = m_mgr->get_sized_region (src_reg, NULL_TREE, num_bytes_sval);
+ const region *sized_dest_reg
+ = m_mgr->get_sized_region (dest_reg, NULL_TREE, num_bytes_sval);
+ const svalue *src_contents_sval
+ = get_store_value (sized_src_reg, cd.get_ctxt ());
+ set_value (sized_dest_reg, src_contents_sval, cd.get_ctxt ());
}
/* Handle the on_call_pre part of "memset" and "__builtin_memset". */
#include <string.h>
#include "analyzer-decls.h"
+/* Function for thwarting expansion of memcpy by optimizer. */
+
+typedef void * (*memcpy_t) (void *dst, const void *src, size_t n);
+
+static memcpy_t __attribute__((noinline))
+get_memcpy (void)
+{
+ return memcpy;
+}
+
void *test_1 (void *dst, void *src, size_t n)
{
void *result = memcpy (dst, src, n);
return result;
}
+void *test_1b (void *dst, void *src, size_t n)
+{
+ memcpy_t fn = get_memcpy ();
+ void *result = fn (dst, src, n);
+ __analyzer_eval (result == dst); /* { dg-warning "TRUE" } */
+ return result;
+}
+
void test_2 (int i)
{
int j;
__analyzer_eval (i == j); /* { dg-warning "TRUE" } */
}
+void test_2b (int i)
+{
+ int j;
+ memcpy_t fn = get_memcpy ();
+ 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];
__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];
+ memcpy_t fn = get_memcpy ();
+ 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 memcpy 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" } */
+
+ memcpy (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];
+ memcpy_t fn = get_memcpy ();
+ 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];
+ memcpy (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];
+ memcpy_t fn = get_memcpy ();
+ fn (dst, src, sizeof (dst));
+ __analyzer_eval (dst[0] == 0); /* { dg-warning "UNKNOWN" } */
+ __analyzer_eval (dst[1023] == 0); /* { dg-warning "UNKNOWN" } */
+}
+
+/* Zero-sized memcpy. */
+
+void test_6 (void *dst, void *src)
+{
+ memcpy (dst, src, 0);
+}
+
+void test_6b (void *dst, void *src)
+{
+ memcpy_t fn = get_memcpy ();
+ fn (dst, src, 0);
+}
+
+/* memcpy to string literal. */
+
+void test_7 (void *src, size_t sz)
+{
+ memcpy ((void *)"hello world", src, sz); /* { dg-warning "write to string literal" } */
+}
+
+void test_7b (void *src, size_t sz)
+{
+ memcpy ((void *)"hello world", src, sz); /* { dg-warning "write to string literal" } */
+}