diff options
author | David Malcolm <dmalcolm@redhat.com> | 2022-11-30 21:26:42 -0500 |
---|---|---|
committer | David Malcolm <dmalcolm@redhat.com> | 2022-11-30 21:26:42 -0500 |
commit | 7c655699ed51b0c987e5472767db48b19044ae05 (patch) | |
tree | 1576cbce7ffb4aefba1d1b637d885b826ccbe204 /gcc/analyzer | |
parent | d69a95c12cc91ec10d6a8c78f401bf6720b08fce (diff) | |
download | gcc-7c655699ed51b0c987e5472767db48b19044ae05.zip gcc-7c655699ed51b0c987e5472767db48b19044ae05.tar.gz gcc-7c655699ed51b0c987e5472767db48b19044ae05.tar.bz2 |
analyzer: add note about valid subscripts [PR106626]
Consider -fanalyzer on:
#include <stdint.h>
int32_t arr[10];
void int_arr_write_element_after_end_off_by_one(int32_t x)
{
arr[10] = x;
}
Trunk x86_64: https://godbolt.org/z/17zn3qYY4
Currently we emit:
<source>: In function 'int_arr_write_element_after_end_off_by_one':
<source>:7:11: warning: buffer overflow [CWE-787] [-Wanalyzer-out-of-bounds]
7 | arr[10] = x;
| ~~~~~~~~^~~
event 1
|
| 3 | int32_t arr[10];
| | ^~~
| | |
| | (1) capacity is 40 bytes
|
+--> 'int_arr_write_element_after_end_off_by_one': events 2-3
|
| 5 | void int_arr_write_element_after_end_off_by_one(int32_t x)
| | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| | |
| | (2) entry to 'int_arr_write_element_after_end_off_by_one'
| 6 | {
| 7 | arr[10] = x;
| | ~~~~~~~~~~~
| | |
| | (3) out-of-bounds write from byte 40 till byte 43 but 'arr' ends at byte 40
|
<source>:7:11: note: write of 4 bytes to beyond the end of 'arr'
7 | arr[10] = x;
| ~~~~~~~~^~~
This is worded in terms of bytes, due to the way -Wanalyzer-out-of-bounds
is implemented, but this isn't what the user wrote.
This patch tries to get closer to the user's code by adding a note about
array bounds when we're referring to an array. In the above example it
adds this trailing note:
note: valid subscripts for 'arr' are '[0]' to '[9]'
gcc/analyzer/ChangeLog:
PR analyzer/106626
* bounds-checking.cc (out_of_bounds::maybe_describe_array_bounds):
New.
(buffer_overflow::emit): Call maybe_describe_array_bounds.
(buffer_overread::emit): Likewise.
(buffer_underflow::emit): Likewise.
(buffer_underread::emit): Likewise.
gcc/testsuite/ChangeLog:
PR analyzer/106626
* gcc.dg/analyzer/call-summaries-2.c: Add dg-message for expected
note about valid indexes.
* gcc.dg/analyzer/out-of-bounds-1.c: Likewise, fixing up existing
dg-message directives.
* gcc.dg/analyzer/out-of-bounds-write-char-arr.c: Likewise.
* gcc.dg/analyzer/out-of-bounds-write-int-arr.c: Likewise.
Signed-off-by: David Malcolm <dmalcolm@redhat.com>
Diffstat (limited to 'gcc/analyzer')
-rw-r--r-- | gcc/analyzer/bounds-checking.cc | 46 |
1 files changed, 42 insertions, 4 deletions
diff --git a/gcc/analyzer/bounds-checking.cc b/gcc/analyzer/bounds-checking.cc index ad7f431..b02bc79 100644 --- a/gcc/analyzer/bounds-checking.cc +++ b/gcc/analyzer/bounds-checking.cc @@ -71,6 +71,34 @@ public: } protected: + /* Potentially add a note about valid ways to index this array, such + as (given "int arr[10];"): + note: valid subscripts for 'arr' are '[0]' to '[9]' + We print the '[' and ']' characters so as to express the valid + subscripts using C syntax, rather than just as byte ranges, + which hopefully is more clear to the user. */ + void + maybe_describe_array_bounds (location_t loc) const + { + if (!m_diag_arg) + return; + tree t = TREE_TYPE (m_diag_arg); + if (!t) + return; + if (TREE_CODE (t) != ARRAY_TYPE) + return; + tree domain = TYPE_DOMAIN (t); + if (!domain) + return; + tree max_idx = TYPE_MAX_VALUE (domain); + if (!max_idx) + return; + tree min_idx = TYPE_MIN_VALUE (domain); + inform (loc, + "valid subscripts for %qE are %<[%E]%> to %<[%E]%>", + m_diag_arg, min_idx, max_idx); + } + const region *m_reg; tree m_diag_arg; byte_range m_out_of_bounds_range; @@ -165,6 +193,8 @@ public: inform (rich_loc->get_loc (), "write to beyond the end of %qE", m_diag_arg); + + maybe_describe_array_bounds (rich_loc->get_loc ()); } return warned; @@ -245,6 +275,8 @@ public: inform (rich_loc->get_loc (), "read from after the end of %qE", m_diag_arg); + + maybe_describe_array_bounds (rich_loc->get_loc ()); } return warned; @@ -297,8 +329,11 @@ public: { diagnostic_metadata m; m.add_cwe (124); - return warning_meta (rich_loc, m, get_controlling_option (), - "buffer underflow"); + bool warned = warning_meta (rich_loc, m, get_controlling_option (), + "buffer underflow"); + if (warned) + maybe_describe_array_bounds (rich_loc->get_loc ()); + return warned; } label_text describe_final_event (const evdesc::final_event &ev) @@ -346,8 +381,11 @@ public: { diagnostic_metadata m; m.add_cwe (127); - return warning_meta (rich_loc, m, get_controlling_option (), - "buffer underread"); + bool warned = warning_meta (rich_loc, m, get_controlling_option (), + "buffer underread"); + if (warned) + maybe_describe_array_bounds (rich_loc->get_loc ()); + return warned; } label_text describe_final_event (const evdesc::final_event &ev) |