extern tristate
compare_constants (tree lhs_const, enum tree_code op, tree rhs_const);
+extern tree
+get_string_cst_size (const_tree string_cst);
+
} // namespace ana
extern bool is_special_named_call_p (const gcall *call, const char *funcname,
return const_fn_result_sval;
}
+/* Get a tree for the size of STRING_CST, or NULL_TREE.
+ Note that this may be larger than TREE_STRING_LENGTH (implying
+ a run of trailing zero bytes from TREE_STRING_LENGTH up to this
+ higher limit). */
+
+tree
+get_string_cst_size (const_tree string_cst)
+{
+ gcc_assert (TREE_CODE (string_cst) == STRING_CST);
+ gcc_assert (TREE_CODE (TREE_TYPE (string_cst)) == ARRAY_TYPE);
+
+ return TYPE_SIZE_UNIT (TREE_TYPE (string_cst));
+}
+
/* Given STRING_CST, a STRING_CST and BYTE_OFFSET_CST a constant,
attempt to get the character at that offset, returning either
the svalue for the character constant, or NULL if unsuccessful. */
/* Adapted from fold_read_from_constant_string. */
scalar_int_mode char_mode;
if (TREE_CODE (byte_offset_cst) == INTEGER_CST
- && compare_tree_int (byte_offset_cst,
- TREE_STRING_LENGTH (string_cst)) < 0
&& is_int_mode (TYPE_MODE (TREE_TYPE (TREE_TYPE (string_cst))),
&char_mode)
&& GET_MODE_SIZE (char_mode) == 1)
{
+ /* If we're beyond the string_cst, the read is unsuccessful. */
+ if (compare_constants (byte_offset_cst,
+ GE_EXPR,
+ get_string_cst_size (string_cst)).is_true ())
+ return NULL;
+
+ int char_val;
+ if (compare_tree_int (byte_offset_cst,
+ TREE_STRING_LENGTH (string_cst)) < 0)
+ /* We're within the area defined by TREE_STRING_POINTER. */
+ char_val = (TREE_STRING_POINTER (string_cst)
+ [TREE_INT_CST_LOW (byte_offset_cst)]);
+ else
+ /* We're in the padding area of trailing zeroes. */
+ char_val = 0;
tree char_cst
- = build_int_cst_type (TREE_TYPE (TREE_TYPE (string_cst)),
- (TREE_STRING_POINTER (string_cst)
- [TREE_INT_CST_LOW (byte_offset_cst)]));
+ = build_int_cst_type (TREE_TYPE (TREE_TYPE (string_cst)), char_val);
return get_or_create_constant_svalue (char_cst);
}
return NULL;
byte_offset_t *out_bytes_read)
{
gcc_assert (bytes.m_start_byte_offset >= 0);
- gcc_assert (bytes.m_start_byte_offset < TREE_STRING_LENGTH (string_cst));
+
+ /* If we're beyond the string_cst, reads are unsuccessful. */
+ if (tree cst_size = get_string_cst_size (string_cst))
+ if (TREE_CODE (cst_size) == INTEGER_CST)
+ if (bytes.m_start_byte_offset >= TREE_INT_CST_LOW (cst_size))
+ return tristate::unknown ();
+
+ /* Assume all bytes after TREE_STRING_LENGTH are zero. This handles
+ the case where an array is initialized with a string_cst that isn't
+ as long as the array, where the remaining elements are
+ empty-initialized and thus zeroed. */
+ if (bytes.m_start_byte_offset >= TREE_STRING_LENGTH (string_cst))
+ {
+ *out_bytes_read = 1;
+ return tristate (true);
+ }
/* Look for the first 0 byte within STRING_CST
from START_READ_OFFSET onwards. */
--- /dev/null
+int main() {
+ char a[20] = "ab";
+ __builtin_strcpy(a, "123");
+ long n0 = __builtin_strlen(a);
+ __builtin_strncpy(a + 3, a, n0);
+ __builtin_strlen(a);
+ return 0;
+}
- complain if NULL str
- should it be tainted if str's bytes are tainted?
*/
+
+static size_t __attribute__((noinline))
+call_strlen_3 (const char *p)
+{
+ return __builtin_strlen (p);
+}
+
+void test_array_initialization_from_shorter_literal (void)
+{
+ const char buf[10] = "abc";
+ __analyzer_eval (call_strlen_3 (buf) == 3); /* { dg-warning "TRUE" } */
+ __analyzer_eval (call_strlen_3 (buf + 5) == 0); /* { dg-warning "TRUE" } */
+ __analyzer_eval (buf[5] == 0); /* { dg-warning "TRUE" } */
+}
+
+static size_t __attribute__((noinline))
+call_strlen_4 (const char *p)
+{
+ return __builtin_strlen (p); /* { dg-warning "stack-based buffer over-read" } */
+}
+
+char test_array_initialization_from_longer_literal (void)
+{
+ const char buf[3] = "abcdefg"; /* { dg-warning "initializer-string for array of 'char' is too long" } */
+ __analyzer_eval (call_strlen_4 (buf) == 3); /* { dg-warning "UNKNOWN" } */
+ return buf[5]; /* { dg-warning "stack-based buffer over-read" } */
+}
+
+static size_t __attribute__((noinline))
+call_strlen_5 (const char *p)
+{
+ return __builtin_strlen (p);
+}
+
+static size_t __attribute__((noinline))
+call_strlen_5a (const char *p)
+{
+ return __builtin_strlen (p); /* { dg-warning "stack-based buffer over-read" } */
+}
+
+char test_array_initialization_implicit_length (void)
+{
+ const char buf[] = "abc";
+ __analyzer_eval (call_strlen_5 (buf) == 3); /* { dg-warning "TRUE" } */
+ __analyzer_eval (call_strlen_5 (buf + 2) == 1); /* { dg-warning "TRUE" } */
+ __analyzer_eval (call_strlen_5 (buf + 3) == 0); /* { dg-warning "TRUE" } */
+ __analyzer_eval (buf[0] == 'a'); /* { dg-warning "TRUE" } */
+ __analyzer_eval (buf[3] == 0); /* { dg-warning "TRUE" } */
+ __analyzer_eval (call_strlen_5a (buf + 4) == 0); /* { dg-warning "UNKNOWN" } */
+ return buf[4]; /* { dg-warning "stack-based buffer over-read" } */
+}
+