Skip to content

Commit 3242fb5

Browse files
committed
analyzer: reimplement kf_strlen [PR105899]
Reimplement kf_strlen in terms of the new string scanning implementation, sharing strlen's implementation with __analyzer_get_strlen. gcc/analyzer/ChangeLog: PR analyzer/105899 * kf-analyzer.cc (class kf_analyzer_get_strlen): Move to kf.cc. (register_known_analyzer_functions): Use make_kf_strlen. * kf.cc (class kf_strlen::impl_call_pre): Replace with implementation of kf_analyzer_get_strlen from kf-analyzer.cc. Handle "UNKNOWN" return from check_for_null_terminated_string_arg by falling back to a conjured svalue. (make_kf_strlen): New. (register_known_functions): Use make_kf_strlen. * known-function-manager.h (make_kf_strlen): New decl. gcc/testsuite/ChangeLog: PR analyzer/105899 * gcc.dg/analyzer/null-terminated-strings-1.c: Update expected results on symbolic values. * gcc.dg/analyzer/strlen-1.c: New test. Signed-off-by: David Malcolm <dmalcolm@redhat.com>
1 parent f40d24c commit 3242fb5

File tree

5 files changed

+85
-61
lines changed

5 files changed

+85
-61
lines changed

gcc/analyzer/kf-analyzer.cc

Lines changed: 1 addition & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -358,33 +358,6 @@ class kf_analyzer_get_unknown_ptr : public known_function
358358
}
359359
};
360360

361-
/* Handler for "__analyzer_get_strlen". */
362-
363-
class kf_analyzer_get_strlen : public known_function
364-
{
365-
public:
366-
bool matches_call_types_p (const call_details &cd) const final override
367-
{
368-
return cd.num_args () == 1 && cd.arg_is_pointer_p (0);
369-
}
370-
void impl_call_pre (const call_details &cd) const final override
371-
{
372-
if (const svalue *bytes_read = cd.check_for_null_terminated_string_arg (0))
373-
{
374-
region_model_manager *mgr = cd.get_manager ();
375-
/* strlen is (bytes_read - 1). */
376-
const svalue *strlen_sval
377-
= mgr->get_or_create_binop (size_type_node,
378-
MINUS_EXPR,
379-
bytes_read,
380-
mgr->get_or_create_int_cst (size_type_node, 1));
381-
cd.maybe_set_lhs (strlen_sval);
382-
}
383-
else
384-
cd.set_any_lhs_with_defaults ();
385-
}
386-
};
387-
388361
/* Populate KFM with instances of known functions used for debugging the
389362
analyzer and for writing DejaGnu tests, all with a "__analyzer_" prefix. */
390363

@@ -406,8 +379,7 @@ register_known_analyzer_functions (known_function_manager &kfm)
406379
kfm.add ("__analyzer_eval", make_unique<kf_analyzer_eval> ());
407380
kfm.add ("__analyzer_get_unknown_ptr",
408381
make_unique<kf_analyzer_get_unknown_ptr> ());
409-
kfm.add ("__analyzer_get_strlen",
410-
make_unique<kf_analyzer_get_strlen> ());
382+
kfm.add ("__analyzer_get_strlen", make_kf_strlen ());
411383
}
412384

413385
} // namespace ana

gcc/analyzer/kf.cc

Lines changed: 26 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1187,7 +1187,7 @@ class kf_strdup : public known_function
11871187
}
11881188
};
11891189

1190-
/* Handle the on_call_pre part of "strlen". */
1190+
/* Handler for "strlen" and for "__analyzer_get_strlen". */
11911191

11921192
class kf_strlen : public known_function
11931193
{
@@ -1196,37 +1196,33 @@ class kf_strlen : public known_function
11961196
{
11971197
return (cd.num_args () == 1 && cd.arg_is_pointer_p (0));
11981198
}
1199-
void impl_call_pre (const call_details &cd) const final override;
1200-
};
1201-
1202-
void
1203-
kf_strlen::impl_call_pre (const call_details &cd) const
1204-
{
1205-
region_model_context *ctxt = cd.get_ctxt ();
1206-
region_model *model = cd.get_model ();
1207-
region_model_manager *mgr = cd.get_manager ();
1208-
1209-
const svalue *arg_sval = cd.get_arg_svalue (0);
1210-
const region *buf_reg
1211-
= model->deref_rvalue (arg_sval, cd.get_arg_tree (0), ctxt);
1212-
if (const string_region *str_reg
1213-
= buf_reg->dyn_cast_string_region ())
1214-
{
1215-
tree str_cst = str_reg->get_string_cst ();
1216-
/* TREE_STRING_LENGTH is sizeof, not strlen. */
1217-
int sizeof_cst = TREE_STRING_LENGTH (str_cst);
1218-
int strlen_cst = sizeof_cst - 1;
1219-
if (cd.get_lhs_type ())
1199+
void impl_call_pre (const call_details &cd) const final override
1200+
{
1201+
if (const svalue *bytes_read = cd.check_for_null_terminated_string_arg (0))
1202+
if (bytes_read->get_kind () != SK_UNKNOWN)
12201203
{
1221-
tree t_cst = build_int_cst (cd.get_lhs_type (), strlen_cst);
1222-
const svalue *result_sval
1223-
= mgr->get_or_create_constant_svalue (t_cst);
1224-
cd.maybe_set_lhs (result_sval);
1204+
region_model_manager *mgr = cd.get_manager ();
1205+
/* strlen is (bytes_read - 1). */
1206+
const svalue *one = mgr->get_or_create_int_cst (size_type_node, 1);
1207+
const svalue *strlen_sval = mgr->get_or_create_binop (size_type_node,
1208+
MINUS_EXPR,
1209+
bytes_read,
1210+
one);
1211+
cd.maybe_set_lhs (strlen_sval);
12251212
return;
12261213
}
1227-
}
1228-
/* Otherwise a conjured value. */
1229-
cd.set_any_lhs_with_defaults ();
1214+
1215+
/* Use a conjured svalue. */
1216+
cd.set_any_lhs_with_defaults ();
1217+
}
1218+
};
1219+
1220+
/* Factory function, so that kf-analyzer.cc can use this class. */
1221+
1222+
std::unique_ptr<known_function>
1223+
make_kf_strlen ()
1224+
{
1225+
return make_unique<kf_strlen> ();
12301226
}
12311227

12321228
/* Handler for "strndup" and "__builtin_strndup". */
@@ -1434,7 +1430,7 @@ register_known_functions (known_function_manager &kfm)
14341430
kfm.add (BUILT_IN_STRCPY_CHK, make_unique<kf_strcpy> (3));
14351431
kfm.add (BUILT_IN_STRDUP, make_unique<kf_strdup> ());
14361432
kfm.add (BUILT_IN_STRNDUP, make_unique<kf_strndup> ());
1437-
kfm.add (BUILT_IN_STRLEN, make_unique<kf_strlen> ());
1433+
kfm.add (BUILT_IN_STRLEN, make_kf_strlen ());
14381434

14391435
register_atomic_builtins (kfm);
14401436
register_varargs_builtins (kfm);

gcc/analyzer/known-function-manager.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ class known_function_manager : public log_user
6464
known_function *m_combined_fns_arr[CFN_LAST];
6565
};
6666

67+
extern std::unique_ptr<known_function> make_kf_strlen ();
68+
6769
} // namespace ana
6870

6971
#endif /* GCC_ANALYZER_KNOWN_FUNCTION_MANAGER_H */

gcc/testsuite/gcc.dg/analyzer/null-terminated-strings-1.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,12 +129,12 @@ char *test_dynamic_4 (const char *src)
129129

130130
void test_symbolic_ptr (const char *ptr)
131131
{
132-
__analyzer_describe (0, __analyzer_get_strlen (ptr)); /* { dg-warning "UNKNOWN" } */
132+
__analyzer_describe (0, __analyzer_get_strlen (ptr)); /* { dg-warning "CONJURED" } */
133133
}
134134

135135
void test_symbolic_offset (size_t idx)
136136
{
137-
__analyzer_describe (0, __analyzer_get_strlen ("abc" + idx)); /* { dg-warning "UNKNOWN" } */
137+
__analyzer_describe (0, __analyzer_get_strlen ("abc" + idx)); /* { dg-warning "CONJURED" } */
138138
}
139139

140140
void test_casts (void)
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/* See e.g. https://en.cppreference.com/w/c/string/byte/strlen */
2+
3+
#include "analyzer-decls.h"
4+
5+
typedef __SIZE_TYPE__ size_t;
6+
7+
static size_t __attribute__((noinline))
8+
call_strlen_1 (const char *p)
9+
{
10+
return __builtin_strlen (p);
11+
}
12+
13+
void test_string (void)
14+
{
15+
__analyzer_eval (call_strlen_1 ("abc") == 3); /* { dg-warning "TRUE" } */
16+
}
17+
18+
static size_t __attribute__((noinline))
19+
call_strlen_2 (const char *p)
20+
{
21+
return __builtin_strlen (p); /* { dg-warning "stack-based buffer over-read" } */
22+
}
23+
24+
void test_unterminated (void)
25+
{
26+
const char buf[3] = "abc";
27+
__analyzer_eval (call_strlen_2 (buf) == 3); /* { dg-warning "UNKNOWN" } */
28+
}
29+
30+
void test_uninitialized (void)
31+
{
32+
char buf[16];
33+
__builtin_strlen (buf); /* { dg-warning "use of uninitialized value 'buf\\\[0\\\]'" } */
34+
}
35+
36+
void test_partially_initialized (void)
37+
{
38+
char buf[16];
39+
buf[0] = 'a';
40+
__builtin_strlen (buf); /* { dg-warning "use of uninitialized value 'buf\\\[1\\\]'" } */
41+
}
42+
43+
extern size_t strlen (const char *str);
44+
45+
size_t
46+
test_passthrough (const char *str)
47+
{
48+
return strlen (str);
49+
}
50+
51+
/* TODO
52+
- complain if NULL str
53+
- should it be tainted if str's bytes are tainted?
54+
*/

0 commit comments

Comments
 (0)