Skip to content

Commit 0a6a5f8

Browse files
committed
analyzer: handle array-initialization from a string_cst [PR113999]
gcc/analyzer/ChangeLog: PR analyzer/113999 * analyzer.h (get_string_cst_size): New decl. * region-model-manager.cc (get_string_cst_size): New. (region_model_manager::maybe_get_char_from_string_cst): Treat single-byte accesses within string_cst but beyond TREE_STRING_LENGTH as being 0. * region-model.cc (string_cst_has_null_terminator): Likewise. gcc/testsuite/ChangeLog: PR analyzer/113999 * c-c++-common/analyzer/strlen-pr113999.c: New test. * gcc.dg/analyzer/strlen-1.c: More test coverage. Signed-off-by: David Malcolm <[email protected]>
1 parent 79d4c7d commit 0a6a5f8

File tree

5 files changed

+109
-6
lines changed

5 files changed

+109
-6
lines changed

gcc/analyzer/analyzer.h

+3
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,9 @@ byte_offset_to_json (const byte_offset_t &offset);
430430
extern tristate
431431
compare_constants (tree lhs_const, enum tree_code op, tree rhs_const);
432432

433+
extern tree
434+
get_string_cst_size (const_tree string_cst);
435+
433436
} // namespace ana
434437

435438
extern bool is_special_named_call_p (const gcall *call, const char *funcname,

gcc/analyzer/region-model-manager.cc

+30-5
Original file line numberDiff line numberDiff line change
@@ -1407,6 +1407,20 @@ get_or_create_const_fn_result_svalue (tree type,
14071407
return const_fn_result_sval;
14081408
}
14091409

1410+
/* Get a tree for the size of STRING_CST, or NULL_TREE.
1411+
Note that this may be larger than TREE_STRING_LENGTH (implying
1412+
a run of trailing zero bytes from TREE_STRING_LENGTH up to this
1413+
higher limit). */
1414+
1415+
tree
1416+
get_string_cst_size (const_tree string_cst)
1417+
{
1418+
gcc_assert (TREE_CODE (string_cst) == STRING_CST);
1419+
gcc_assert (TREE_CODE (TREE_TYPE (string_cst)) == ARRAY_TYPE);
1420+
1421+
return TYPE_SIZE_UNIT (TREE_TYPE (string_cst));
1422+
}
1423+
14101424
/* Given STRING_CST, a STRING_CST and BYTE_OFFSET_CST a constant,
14111425
attempt to get the character at that offset, returning either
14121426
the svalue for the character constant, or NULL if unsuccessful. */
@@ -1420,16 +1434,27 @@ region_model_manager::maybe_get_char_from_string_cst (tree string_cst,
14201434
/* Adapted from fold_read_from_constant_string. */
14211435
scalar_int_mode char_mode;
14221436
if (TREE_CODE (byte_offset_cst) == INTEGER_CST
1423-
&& compare_tree_int (byte_offset_cst,
1424-
TREE_STRING_LENGTH (string_cst)) < 0
14251437
&& is_int_mode (TYPE_MODE (TREE_TYPE (TREE_TYPE (string_cst))),
14261438
&char_mode)
14271439
&& GET_MODE_SIZE (char_mode) == 1)
14281440
{
1441+
/* If we're beyond the string_cst, the read is unsuccessful. */
1442+
if (compare_constants (byte_offset_cst,
1443+
GE_EXPR,
1444+
get_string_cst_size (string_cst)).is_true ())
1445+
return NULL;
1446+
1447+
int char_val;
1448+
if (compare_tree_int (byte_offset_cst,
1449+
TREE_STRING_LENGTH (string_cst)) < 0)
1450+
/* We're within the area defined by TREE_STRING_POINTER. */
1451+
char_val = (TREE_STRING_POINTER (string_cst)
1452+
[TREE_INT_CST_LOW (byte_offset_cst)]);
1453+
else
1454+
/* We're in the padding area of trailing zeroes. */
1455+
char_val = 0;
14291456
tree char_cst
1430-
= build_int_cst_type (TREE_TYPE (TREE_TYPE (string_cst)),
1431-
(TREE_STRING_POINTER (string_cst)
1432-
[TREE_INT_CST_LOW (byte_offset_cst)]));
1457+
= build_int_cst_type (TREE_TYPE (TREE_TYPE (string_cst)), char_val);
14331458
return get_or_create_constant_svalue (char_cst);
14341459
}
14351460
return NULL;

gcc/analyzer/region-model.cc

+16-1
Original file line numberDiff line numberDiff line change
@@ -3648,7 +3648,22 @@ string_cst_has_null_terminator (tree string_cst,
36483648
byte_offset_t *out_bytes_read)
36493649
{
36503650
gcc_assert (bytes.m_start_byte_offset >= 0);
3651-
gcc_assert (bytes.m_start_byte_offset < TREE_STRING_LENGTH (string_cst));
3651+
3652+
/* If we're beyond the string_cst, reads are unsuccessful. */
3653+
if (tree cst_size = get_string_cst_size (string_cst))
3654+
if (TREE_CODE (cst_size) == INTEGER_CST)
3655+
if (bytes.m_start_byte_offset >= TREE_INT_CST_LOW (cst_size))
3656+
return tristate::unknown ();
3657+
3658+
/* Assume all bytes after TREE_STRING_LENGTH are zero. This handles
3659+
the case where an array is initialized with a string_cst that isn't
3660+
as long as the array, where the remaining elements are
3661+
empty-initialized and thus zeroed. */
3662+
if (bytes.m_start_byte_offset >= TREE_STRING_LENGTH (string_cst))
3663+
{
3664+
*out_bytes_read = 1;
3665+
return tristate (true);
3666+
}
36523667

36533668
/* Look for the first 0 byte within STRING_CST
36543669
from START_READ_OFFSET onwards. */
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
int main() {
2+
char a[20] = "ab";
3+
__builtin_strcpy(a, "123");
4+
long n0 = __builtin_strlen(a);
5+
__builtin_strncpy(a + 3, a, n0);
6+
__builtin_strlen(a);
7+
return 0;
8+
}

gcc/testsuite/gcc.dg/analyzer/strlen-1.c

+52
Original file line numberDiff line numberDiff line change
@@ -52,3 +52,55 @@ test_passthrough (const char *str)
5252
- complain if NULL str
5353
- should it be tainted if str's bytes are tainted?
5454
*/
55+
56+
static size_t __attribute__((noinline))
57+
call_strlen_3 (const char *p)
58+
{
59+
return __builtin_strlen (p);
60+
}
61+
62+
void test_array_initialization_from_shorter_literal (void)
63+
{
64+
const char buf[10] = "abc";
65+
__analyzer_eval (call_strlen_3 (buf) == 3); /* { dg-warning "TRUE" } */
66+
__analyzer_eval (call_strlen_3 (buf + 5) == 0); /* { dg-warning "TRUE" } */
67+
__analyzer_eval (buf[5] == 0); /* { dg-warning "TRUE" } */
68+
}
69+
70+
static size_t __attribute__((noinline))
71+
call_strlen_4 (const char *p)
72+
{
73+
return __builtin_strlen (p); /* { dg-warning "stack-based buffer over-read" } */
74+
}
75+
76+
char test_array_initialization_from_longer_literal (void)
77+
{
78+
const char buf[3] = "abcdefg"; /* { dg-warning "initializer-string for array of 'char' is too long" } */
79+
__analyzer_eval (call_strlen_4 (buf) == 3); /* { dg-warning "UNKNOWN" } */
80+
return buf[5]; /* { dg-warning "stack-based buffer over-read" } */
81+
}
82+
83+
static size_t __attribute__((noinline))
84+
call_strlen_5 (const char *p)
85+
{
86+
return __builtin_strlen (p);
87+
}
88+
89+
static size_t __attribute__((noinline))
90+
call_strlen_5a (const char *p)
91+
{
92+
return __builtin_strlen (p); /* { dg-warning "stack-based buffer over-read" } */
93+
}
94+
95+
char test_array_initialization_implicit_length (void)
96+
{
97+
const char buf[] = "abc";
98+
__analyzer_eval (call_strlen_5 (buf) == 3); /* { dg-warning "TRUE" } */
99+
__analyzer_eval (call_strlen_5 (buf + 2) == 1); /* { dg-warning "TRUE" } */
100+
__analyzer_eval (call_strlen_5 (buf + 3) == 0); /* { dg-warning "TRUE" } */
101+
__analyzer_eval (buf[0] == 'a'); /* { dg-warning "TRUE" } */
102+
__analyzer_eval (buf[3] == 0); /* { dg-warning "TRUE" } */
103+
__analyzer_eval (call_strlen_5a (buf + 4) == 0); /* { dg-warning "UNKNOWN" } */
104+
return buf[4]; /* { dg-warning "stack-based buffer over-read" } */
105+
}
106+

0 commit comments

Comments
 (0)