model->set_value (sized_dest_reg, src_contents_sval, cd.get_ctxt ());
}
+/* Handler for "strdup" and "__builtin_strdup". */
+
+class kf_strdup : public known_function
+{
+public:
+ bool matches_call_types_p (const call_details &cd) const final override
+ {
+ return (cd.num_args () == 1 && cd.arg_is_pointer_p (0));
+ }
+ void impl_call_pre (const call_details &cd) const final override
+ {
+ region_model *model = cd.get_model ();
+ region_model_manager *mgr = cd.get_manager ();
+ /* Ideally we'd get the size here, and simulate copying the bytes. */
+ const region *new_reg
+ = model->get_or_create_region_for_heap_alloc (NULL, cd.get_ctxt ());
+ model->mark_region_as_unknown (new_reg, NULL);
+ if (cd.get_lhs_type ())
+ {
+ const svalue *ptr_sval
+ = mgr->get_ptr_svalue (cd.get_lhs_type (), new_reg);
+ cd.maybe_set_lhs (ptr_sval);
+ }
+ }
+};
+
/* Handle the on_call_pre part of "strlen". */
class kf_strlen : public known_function
/* Otherwise a conjured value. */
}
+/* Handler for "strndup" and "__builtin_strndup". */
+
+class kf_strndup : public known_function
+{
+public:
+ bool matches_call_types_p (const call_details &cd) const final override
+ {
+ return (cd.num_args () == 2 && cd.arg_is_pointer_p (0));
+ }
+ void impl_call_pre (const call_details &cd) const final override
+ {
+ region_model *model = cd.get_model ();
+ region_model_manager *mgr = cd.get_manager ();
+ /* Ideally we'd get the size here, and simulate copying the bytes. */
+ const region *new_reg
+ = model->get_or_create_region_for_heap_alloc (NULL, cd.get_ctxt ());
+ model->mark_region_as_unknown (new_reg, NULL);
+ if (cd.get_lhs_type ())
+ {
+ const svalue *ptr_sval
+ = mgr->get_ptr_svalue (cd.get_lhs_type (), new_reg);
+ cd.maybe_set_lhs (ptr_sval);
+ }
+ }
+};
+
class kf_ubsan_bounds : public internal_known_function
{
/* Empty. */
kfm.add (BUILT_IN_STRCHR, make_unique<kf_strchr> ());
kfm.add (BUILT_IN_STRCPY, make_unique<kf_strcpy> (2));
kfm.add (BUILT_IN_STRCPY_CHK, make_unique<kf_strcpy> (3));
+ kfm.add (BUILT_IN_STRDUP, make_unique<kf_strdup> ());
+ kfm.add (BUILT_IN_STRNDUP, make_unique<kf_strndup> ());
kfm.add (BUILT_IN_STRLEN, make_unique<kf_strlen> ());
register_varargs_builtins (kfm);
/* Known builtins and C standard library functions. */
{
kfm.add ("memset", make_unique<kf_memset> ());
+ kfm.add ("strdup", make_unique<kf_strdup> ());
+ kfm.add ("strndup", make_unique<kf_strndup> ());
}
/* Known POSIX functions, and some non-standard extensions. */
if (ctxt)
check_call_args (cd);
+ tree callee_fndecl = get_fndecl_for_call (call, ctxt);
+
/* Some of the cases below update the lhs of the call based on the
return value, but not all. Provide a default value, which may
get overwritten below. */
const svalue *sval = maybe_get_const_fn_result (cd);
if (!sval)
{
- /* For the common case of functions without __attribute__((const)),
- use a conjured value, and purge any prior state involving that
- value (in case this is in a loop). */
- sval = m_mgr->get_or_create_conjured_svalue (TREE_TYPE (lhs), call,
- lhs_region,
- conjured_purge (this,
- ctxt));
+ if (callee_fndecl
+ && lookup_attribute ("malloc", DECL_ATTRIBUTES (callee_fndecl)))
+ {
+ const region *new_reg
+ = get_or_create_region_for_heap_alloc (NULL, ctxt);
+ mark_region_as_unknown (new_reg, NULL);
+ sval = m_mgr->get_ptr_svalue (cd.get_lhs_type (), new_reg);
+ }
+ else
+ /* For the common case of functions without __attribute__((const)),
+ use a conjured value, and purge any prior state involving that
+ value (in case this is in a loop). */
+ sval = m_mgr->get_or_create_conjured_svalue (TREE_TYPE (lhs), call,
+ lhs_region,
+ conjured_purge (this,
+ ctxt));
}
set_value (lhs_region, sval, ctxt);
}
return false;
}
- if (tree callee_fndecl = get_fndecl_for_call (call, ctxt))
+ if (callee_fndecl)
{
int callee_fndecl_flags = flags_from_decl_or_type (callee_fndecl);
get_referenced_base_regions (base_regs_in_use);
const region *reg
= m_mgr->get_or_create_region_for_heap_alloc (base_regs_in_use);
- if (compat_types_p (size_in_bytes->get_type (), size_type_node))
- set_dynamic_extents (reg, size_in_bytes, ctxt);
+ if (size_in_bytes)
+ if (compat_types_p (size_in_bytes->get_type (), size_type_node))
+ set_dynamic_extents (reg, size_in_bytes, ctxt);
return reg;
}
const region *ptr_base_reg = ptr_dst->get_base_region ();
mark_as_escaped (ptr_base_reg);
}
+ if (uncertainty)
+ uncertainty->on_maybe_bound_sval (rhs_sval);
}
else if (lhs_base_reg->tracked_p ())
{
--- /dev/null
+struct foo
+{
+ int m_int;
+};
+
+extern void foo_release (struct foo *);
+extern struct foo *foo_acquire (void)
+ __attribute__ ((malloc (foo_release)));
+
+struct {
+ /* [...snip...] */
+ struct foo *listen_default_ciphers;
+ struct foo *connect_default_ciphers;
+ /* [...snip...] */
+} g;
+
+int parse_global_ciphers(char **args)
+{
+ struct foo **target;
+ target = ((args[0][12] == 'b')
+ ? &g.listen_default_ciphers
+ : &g.connect_default_ciphers);
+ *target = foo_acquire ();
+ return 0; /* { dg-bogus "leak" } */
+}
--- /dev/null
+extern int open(const char *, int mode);
+#define O_RDONLY 0
+
+struct {
+ int fd_a;
+ int fd_b;
+} g;
+
+int test (const char *path, int flag)
+{
+ int *target;
+ target = flag ? &g.fd_a : &g.fd_b;
+ *target = open (path, O_RDONLY);
+ return 0; /* { dg-bogus "leak of file descriptor" } */
+}
if ( number_to_move == YY_MORE_ADJ )
{
ret_val = EOB_ACT_END_OF_FILE;
- yyrestart( yyin );/* { dg-bogus "leak" "" { xfail *-*-* } } */
- /* TODO: leak false positive: PR analyzer/103546. */
+ yyrestart( yyin );/* { dg-bogus "leak" } */
}
else
#ifdef __cplusplus
return yyinput();
#else
- return input(); /* { dg-bogus "infinite recursion" "" { xfail *-*-* } } */
- /* TODO: infinite recursion positive: PR analyzer/103546. */
+ return input(); /* { dg-bogus "infinite recursion" } */
#endif
}
+/* { dg-additional-options "-Wno-analyzer-too-complex" } */
+
typedef __SIZE_TYPE__ size_t;
extern void *calloc (size_t __nmemb, size_t __size)
+/* { dg-additional-options "-Wno-analyzer-too-complex" } */
+
extern char *strdup (const char *__s)
__attribute__ ((__nothrow__ , __leaf__, __malloc__, __nonnull__ (1)));
--- /dev/null
+extern char *strdup (const char *__s)
+ __attribute__ ((__nothrow__ , __leaf__, __malloc__, __nonnull__ (1)));
+
+struct {
+ /* [...snip...] */
+ char *listen_default_ciphers;
+ char *connect_default_ciphers;
+ /* [...snip...] */
+} g;
+
+int parse_global_ciphers(char **args)
+{
+ char **target;
+ target = ((args[0][12] == 'b')
+ ? &g.listen_default_ciphers
+ : &g.connect_default_ciphers);
+ *target = strdup(args[1]);
+ return 0; /* { dg-bogus "leak" } */
+}
--- /dev/null
+typedef __SIZE_TYPE__ size_t;
+
+extern char *strndup (const char *__s, size_t sz)
+ __attribute__ ((__nothrow__ , __leaf__, __malloc__, __nonnull__ (1)));
+
+struct {
+ /* [...snip...] */
+ char *listen_default_ciphers;
+ char *connect_default_ciphers;
+ /* [...snip...] */
+} g;
+
+int parse_global_ciphers(char **args)
+{
+ char **target;
+ target = ((args[0][12] == 'b')
+ ? &g.listen_default_ciphers
+ : &g.connect_default_ciphers);
+ *target = strndup(args[1], 42);
+ return 0; /* { dg-bogus "leak" } */
+}