aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorTim Lange <mail@tim-lange.me>2022-09-04 21:07:14 +0200
committerTim Lange <mail@tim-lange.me>2022-09-05 16:22:57 +0200
commit0a9c0d4ae5519c404682425da9522c46c38712fd (patch)
tree86e3c426e4cdc483f544792e7ba90ee7a60144de /gcc
parent1cc7e31c41a555c53d6f0a88ecd71bbf09b9dd8d (diff)
downloadgcc-0a9c0d4ae5519c404682425da9522c46c38712fd.zip
gcc-0a9c0d4ae5519c404682425da9522c46c38712fd.tar.gz
gcc-0a9c0d4ae5519c404682425da9522c46c38712fd.tar.bz2
analyzer: strcpy semantics
This patch adds modelling for the semantics of strcpy in the simple case where the analyzer is able to infer a concrete string size. Regrtested on Linux x86_64. 2022-09-04 Tim Lange <mail@tim-lange.me> gcc/analyzer/ChangeLog: * region-model-impl-calls.cc (region_model::impl_call_strcpy): Handle the constant string case. * region-model.cc (region_model::get_string_size): New function to get the string size from a region or svalue. * region-model.h (class region_model): Add get_string_size. gcc/testsuite/ChangeLog: * gcc.dg/analyzer/out-of-bounds-4.c: New test. * gcc.dg/analyzer/strcpy-3.c: New test.
Diffstat (limited to 'gcc')
-rw-r--r--gcc/analyzer/region-model-impl-calls.cc16
-rw-r--r--gcc/analyzer/region-model.cc29
-rw-r--r--gcc/analyzer/region-model.h3
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/out-of-bounds-4.c65
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/strcpy-3.c23
5 files changed, 133 insertions, 3 deletions
diff --git a/gcc/analyzer/region-model-impl-calls.cc b/gcc/analyzer/region-model-impl-calls.cc
index 8eebd12..3790eaf 100644
--- a/gcc/analyzer/region-model-impl-calls.cc
+++ b/gcc/analyzer/region-model-impl-calls.cc
@@ -1019,13 +1019,23 @@ region_model::impl_call_strcpy (const call_details &cd)
const svalue *dest_sval = cd.get_arg_svalue (0);
const region *dest_reg = deref_rvalue (dest_sval, cd.get_arg_tree (0),
cd.get_ctxt ());
+ const svalue *src_sval = cd.get_arg_svalue (1);
+ const region *src_reg = deref_rvalue (src_sval, cd.get_arg_tree (1),
+ cd.get_ctxt ());
+ const svalue *src_contents_sval = get_store_value (src_reg,
+ cd.get_ctxt ());
cd.maybe_set_lhs (dest_sval);
- check_region_for_write (dest_reg, cd.get_ctxt ());
+ /* Try to get the string size if SRC_REG is a string_region. */
+ const svalue *copied_bytes_sval = get_string_size (src_reg);
+ /* Otherwise, check if the contents of SRC_REG is a string. */
+ if (copied_bytes_sval->get_kind () == SK_UNKNOWN)
+ copied_bytes_sval = get_string_size (src_contents_sval);
- /* For now, just mark region's contents as unknown. */
- mark_region_as_unknown (dest_reg, cd.get_uncertainty ());
+ const region *sized_dest_reg
+ = m_mgr->get_sized_region (dest_reg, NULL_TREE, copied_bytes_sval);
+ set_value (sized_dest_reg, src_contents_sval, cd.get_ctxt ());
}
/* Handle the on_call_pre part of "strlen". */
diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc
index 5a64c00..e84087a 100644
--- a/gcc/analyzer/region-model.cc
+++ b/gcc/analyzer/region-model.cc
@@ -3218,6 +3218,35 @@ region_model::get_capacity (const region *reg) const
return m_mgr->get_or_create_unknown_svalue (sizetype);
}
+/* Return the string size, including the 0-terminator, if SVAL is a
+ constant_svalue holding a string. Otherwise, return an unknown_svalue. */
+
+const svalue *
+region_model::get_string_size (const svalue *sval) const
+{
+ tree cst = sval->maybe_get_constant ();
+ if (!cst || TREE_CODE (cst) != STRING_CST)
+ return m_mgr->get_or_create_unknown_svalue (size_type_node);
+
+ tree out = build_int_cst (size_type_node, TREE_STRING_LENGTH (cst));
+ return m_mgr->get_or_create_constant_svalue (out);
+}
+
+/* Return the string size, including the 0-terminator, if REG is a
+ string_region. Otherwise, return an unknown_svalue. */
+
+const svalue *
+region_model::get_string_size (const region *reg) const
+{
+ const string_region *str_reg = dyn_cast <const string_region *> (reg);
+ if (!str_reg)
+ return m_mgr->get_or_create_unknown_svalue (size_type_node);
+
+ tree cst = str_reg->get_string_cst ();
+ tree out = build_int_cst (size_type_node, TREE_STRING_LENGTH (cst));
+ return m_mgr->get_or_create_constant_svalue (out);
+}
+
/* If CTXT is non-NULL, use it to warn about any problems accessing REG,
using DIR to determine if this access is a read or write. */
diff --git a/gcc/analyzer/region-model.h b/gcc/analyzer/region-model.h
index 7ce832f..a1f2165 100644
--- a/gcc/analyzer/region-model.h
+++ b/gcc/analyzer/region-model.h
@@ -793,6 +793,9 @@ class region_model
const svalue *get_capacity (const region *reg) const;
+ const svalue *get_string_size (const svalue *sval) const;
+ const svalue *get_string_size (const region *reg) const;
+
/* Implemented in sm-malloc.cc */
void on_realloc_with_move (const call_details &cd,
const svalue *old_ptr_sval,
diff --git a/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-4.c b/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-4.c
new file mode 100644
index 0000000..46f600d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-4.c
@@ -0,0 +1,65 @@
+/* { dg-additional-options "-Wno-stringop-overflow -Wno-stringop-truncation" } */
+#include <string.h>
+
+/* Wanalyzer-out-of-bounds tests for strpy-related overflows.
+
+ The intra-procedural tests are all caught by Wstringop-overflow.
+ The inter-procedural out-of-bounds are only found by the analyzer. */
+
+void test1 (void)
+{
+ char dst[5];
+ strcpy (dst, "Hello"); /* { dg-line test1 } */
+
+ /* { dg-warning "overflow" "warning" { target *-*-* } test1 } */
+ /* { dg-message "dst" "note" { target *-*-* } test1 } */
+}
+
+void test2 (void)
+{
+ char dst[6];
+ strcpy (dst, "Hello");
+}
+
+void test3 (void)
+{
+ char *src = "Hello";
+ char dst[5];
+ strcpy (dst, src); /* { dg-line test3 } */
+
+ /* { dg-warning "overflow" "warning" { target *-*-* } test3 } */
+ /* { dg-message "dst" "note" { target *-*-* } test3 } */
+}
+
+void test4 (void)
+{
+ char *src = "Hello";
+ char dst[6];
+ strcpy (dst, src);
+}
+
+const char *return_hello (void)
+{
+ return "hello";
+}
+
+void test5 (void)
+{
+ const char *str = return_hello ();
+ if (!str)
+ return;
+ char dst[5];
+ strcpy (dst, str); /* { dg-line test5 } */
+
+ /* { dg-warning "overflow" "warning" { target *-*-* } test5 } */
+ /* { dg-message "dst" "note" { target *-*-* } test5 } */
+}
+
+void test6 (void)
+{
+ const char *str = return_hello ();
+ if (!str)
+ return;
+ char dst[6];
+ strcpy (dst, str);
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/strcpy-3.c b/gcc/testsuite/gcc.dg/analyzer/strcpy-3.c
new file mode 100644
index 0000000..a38f9a7
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/strcpy-3.c
@@ -0,0 +1,23 @@
+#include <string.h>
+#include "analyzer-decls.h"
+
+void test_1 (void)
+{
+ char str[] = "Hello";
+ char buf[6];
+ char *result = strcpy (buf, str);
+ __analyzer_describe (1, result); /* { dg-warning "region_svalue.*?'buf'" } */
+ __analyzer_eval (result == buf); /* { dg-warning "TRUE" } */
+ __analyzer_eval (buf[0] == 'H'); /* { dg-warning "TRUE" } */
+ __analyzer_eval (buf[1] == 'e'); /* { dg-warning "TRUE" } */
+ __analyzer_eval (buf[2] == 'l'); /* { dg-warning "TRUE" } */
+ __analyzer_eval (buf[3] == 'l'); /* { dg-warning "TRUE" } */
+ __analyzer_eval (buf[4] == 'o'); /* { dg-warning "TRUE" } */
+ __analyzer_eval (buf[5] == 0); /* { dg-warning "TRUE" } */
+ __analyzer_eval (result[0] == 'H'); /* { dg-warning "TRUE" } */
+ __analyzer_eval (result[1] == 'e'); /* { dg-warning "TRUE" } */
+ __analyzer_eval (result[2] == 'l'); /* { dg-warning "TRUE" } */
+ __analyzer_eval (result[3] == 'l'); /* { dg-warning "TRUE" } */
+ __analyzer_eval (result[4] == 'o'); /* { dg-warning "TRUE" } */
+ __analyzer_eval (result[5] == 0); /* { dg-warning "TRUE" } */
+}