analyzer: start adding support for errno
gcc/analyzer/ChangeLog: * region-model-impl-calls.cc (region_model::impl_call_errno_location): New. * region-model-manager.cc (region_model_manager::region_model_manager): Initialize m_thread_local_region and m_errno_region. * region-model-manager.h (region_model_manager::get_errno_region): New accessor. (region_model_manager::m_thread_local_region): New. (region_model_manager::m_errno_region): New. * region-model.cc (region_model::on_call_pre): Special-case "__errno_location". (region_model::set_errno): New. * region-model.h (impl_call_errno_location): New decl. (region_model::set_errno): New decl. * region.cc (thread_local_region::dump_to_pp): New. (errno_region::dump_to_pp): New. * region.h (enum memory_space): Add MEMSPACE_THREAD_LOCAL. (enum region_kind): Add RK_THREAD_LOCAL and RK_ERRNO. (class thread_local_region): New. (is_a_helper <const thread_local_region *>::test): New. (class errno_region): New. (is_a_helper <const errno_region *>::test): New. * store.cc (binding_cluster::escaped_p): New. (store::escaped_p): Treat errno as always having escaped. (store::replay_call_summary_cluster): Handle RK_THREAD_LOCAL and RK_ERRNO. * store.h (binding_cluster::escaped_p): Remove definition. gcc/testsuite/ChangeLog: * gcc.dg/analyzer/errno-1.c: New test. Signed-off-by: David Malcolm <dmalcolm@redhat.com>
This commit is contained in:
parent
be9fdbda1c
commit
3d2d04cda4
10 changed files with 167 additions and 2 deletions
|
@ -413,6 +413,20 @@ region_model::impl_call_calloc (const call_details &cd)
|
|||
}
|
||||
}
|
||||
|
||||
/* Handle the on_call_pre part of "__errno_location". */
|
||||
|
||||
void
|
||||
region_model::impl_call_errno_location (const call_details &cd)
|
||||
{
|
||||
if (cd.get_lhs_region ())
|
||||
{
|
||||
const region *errno_reg = m_mgr->get_errno_region ();
|
||||
const svalue *errno_ptr = m_mgr->get_ptr_svalue (cd.get_lhs_type (),
|
||||
errno_reg);
|
||||
cd.maybe_set_lhs (errno_ptr);
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle the on_call_pre part of "error" and "error_at_line" from
|
||||
GNU's non-standard <error.h>.
|
||||
MIN_ARGS identifies the minimum number of expected arguments
|
||||
|
|
|
@ -74,6 +74,8 @@ region_model_manager::region_model_manager (logger *logger)
|
|||
m_fndecls_map (), m_labels_map (),
|
||||
m_globals_region (alloc_region_id (), &m_root_region),
|
||||
m_globals_map (),
|
||||
m_thread_local_region (alloc_region_id (), &m_root_region),
|
||||
m_errno_region (alloc_region_id (), &m_thread_local_region),
|
||||
m_store_mgr (this),
|
||||
m_range_mgr (new bounded_ranges_manager ()),
|
||||
m_known_fn_mgr (logger)
|
||||
|
|
|
@ -107,6 +107,7 @@ public:
|
|||
{
|
||||
return &m_globals_region;
|
||||
}
|
||||
const errno_region *get_errno_region () const { return &m_errno_region; }
|
||||
const function_region *get_region_for_fndecl (tree fndecl);
|
||||
const label_region *get_region_for_label (tree label);
|
||||
const decl_region *get_region_for_global (tree expr);
|
||||
|
@ -287,6 +288,9 @@ private:
|
|||
typedef globals_map_t::iterator globals_iterator_t;
|
||||
globals_map_t m_globals_map;
|
||||
|
||||
thread_local_region m_thread_local_region;
|
||||
errno_region m_errno_region;
|
||||
|
||||
consolidation_map<field_region> m_field_regions;
|
||||
consolidation_map<element_region> m_element_regions;
|
||||
consolidation_map<offset_region> m_offset_regions;
|
||||
|
|
|
@ -2288,6 +2288,11 @@ region_model::on_call_pre (const gcall *call, region_model_context *ctxt,
|
|||
impl_call_realloc (cd);
|
||||
return false;
|
||||
}
|
||||
else if (is_named_call_p (callee_fndecl, "__errno_location", call, 0))
|
||||
{
|
||||
impl_call_errno_location (cd);
|
||||
return false;
|
||||
}
|
||||
else if (is_named_call_p (callee_fndecl, "error"))
|
||||
{
|
||||
if (impl_call_error (cd, 3, out_terminate_path))
|
||||
|
@ -6418,6 +6423,23 @@ region_model::maybe_complain_about_infoleak (const region *dst_reg,
|
|||
copied_sval));
|
||||
}
|
||||
|
||||
/* Set errno to a positive symbolic int, as if some error has occurred. */
|
||||
|
||||
void
|
||||
region_model::set_errno (const call_details &cd)
|
||||
{
|
||||
const region *errno_reg = m_mgr->get_errno_region ();
|
||||
conjured_purge p (this, cd.get_ctxt ());
|
||||
const svalue *new_errno_sval
|
||||
= m_mgr->get_or_create_conjured_svalue (integer_type_node,
|
||||
cd.get_call_stmt (),
|
||||
errno_reg, p);
|
||||
const svalue *zero
|
||||
= m_mgr->get_or_create_int_cst (integer_type_node, 0);
|
||||
add_constraint (new_errno_sval, GT_EXPR, zero, cd.get_ctxt ());
|
||||
set_value (errno_reg, new_errno_sval, cd.get_ctxt ());
|
||||
}
|
||||
|
||||
/* class noop_region_model_context : public region_model_context. */
|
||||
|
||||
void
|
||||
|
|
|
@ -349,6 +349,7 @@ class region_model
|
|||
void impl_call_analyzer_get_unknown_ptr (const call_details &cd);
|
||||
void impl_call_builtin_expect (const call_details &cd);
|
||||
void impl_call_calloc (const call_details &cd);
|
||||
void impl_call_errno_location (const call_details &cd);
|
||||
bool impl_call_error (const call_details &cd, unsigned min_args,
|
||||
bool *out_terminate_path);
|
||||
void impl_call_fgets (const call_details &cd);
|
||||
|
@ -544,6 +545,8 @@ class region_model
|
|||
const region *src_reg,
|
||||
region_model_context *ctxt);
|
||||
|
||||
void set_errno (const call_details &cd);
|
||||
|
||||
/* Implemented in sm-fd.cc */
|
||||
void mark_as_valid_fd (const svalue *sval, region_model_context *ctxt);
|
||||
|
||||
|
|
|
@ -1050,6 +1050,17 @@ root_region::dump_to_pp (pretty_printer *pp, bool simple) const
|
|||
pp_string (pp, "root_region()");
|
||||
}
|
||||
|
||||
/* class thread_local_region : public space_region. */
|
||||
|
||||
void
|
||||
thread_local_region::dump_to_pp (pretty_printer *pp, bool simple) const
|
||||
{
|
||||
if (simple)
|
||||
pp_string (pp, "thread_local_region");
|
||||
else
|
||||
pp_string (pp, "thread_local_region()");
|
||||
}
|
||||
|
||||
/* class symbolic_region : public map_region. */
|
||||
|
||||
/* symbolic_region's ctor. */
|
||||
|
@ -1811,6 +1822,17 @@ var_arg_region::get_frame_region () const
|
|||
return as_a <const frame_region *> (get_parent_region ());
|
||||
}
|
||||
|
||||
/* class errno_region : public region. */
|
||||
|
||||
void
|
||||
errno_region::dump_to_pp (pretty_printer *pp, bool simple) const
|
||||
{
|
||||
if (simple)
|
||||
pp_string (pp, "errno_region");
|
||||
else
|
||||
pp_string (pp, "errno_region()");
|
||||
}
|
||||
|
||||
/* class unknown_region : public region. */
|
||||
|
||||
/* Implementation of region::dump_to_pp vfunc for unknown_region. */
|
||||
|
|
|
@ -34,7 +34,8 @@ enum memory_space
|
|||
MEMSPACE_GLOBALS,
|
||||
MEMSPACE_STACK,
|
||||
MEMSPACE_HEAP,
|
||||
MEMSPACE_READONLY_DATA
|
||||
MEMSPACE_READONLY_DATA,
|
||||
MEMSPACE_THREAD_LOCAL
|
||||
};
|
||||
|
||||
/* An enum for discriminating between the different concrete subclasses
|
||||
|
@ -49,6 +50,7 @@ enum region_kind
|
|||
RK_LABEL,
|
||||
RK_STACK,
|
||||
RK_HEAP,
|
||||
RK_THREAD_LOCAL,
|
||||
RK_ROOT,
|
||||
RK_SYMBOLIC,
|
||||
RK_DECL,
|
||||
|
@ -62,6 +64,7 @@ enum region_kind
|
|||
RK_STRING,
|
||||
RK_BIT_RANGE,
|
||||
RK_VAR_ARG,
|
||||
RK_ERRNO,
|
||||
RK_UNKNOWN,
|
||||
};
|
||||
|
||||
|
@ -77,6 +80,8 @@ enum region_kind
|
|||
code_region (RK_CODE): represents the code segment, containing functions
|
||||
stack_region (RK_STACK): a stack, containing all stack frames
|
||||
heap_region (RK_HEAP): the heap, containing heap_allocated_regions
|
||||
thread_local_region (RK_THREAD_LOCAL): thread-local data for the thread
|
||||
being analyzed
|
||||
root_region (RK_ROOT): the top-level region
|
||||
function_region (RK_FUNCTION): the code for a particular function
|
||||
label_region (RK_LABEL): a particular label within a function
|
||||
|
@ -102,6 +107,7 @@ enum region_kind
|
|||
within another region
|
||||
var_arg_region (RK_VAR_ARG): a region for the N-th vararg within a
|
||||
frame_region for a variadic call
|
||||
errno_region (RK_ERRNO): a region for holding "errno"
|
||||
unknown_region (RK_UNKNOWN): for handling unimplemented tree codes. */
|
||||
|
||||
/* Abstract base class for representing ways of accessing chunks of memory.
|
||||
|
@ -555,6 +561,32 @@ is_a_helper <const heap_region *>::test (const region *reg)
|
|||
|
||||
namespace ana {
|
||||
|
||||
/* Concrete space_region subclass: thread-local data for the thread
|
||||
being analyzed. */
|
||||
|
||||
class thread_local_region : public space_region
|
||||
{
|
||||
public:
|
||||
thread_local_region (unsigned id, region *parent)
|
||||
: space_region (id, parent)
|
||||
{}
|
||||
|
||||
enum region_kind get_kind () const final override { return RK_THREAD_LOCAL; }
|
||||
void dump_to_pp (pretty_printer *pp, bool simple) const final override;
|
||||
};
|
||||
|
||||
} // namespace ana
|
||||
|
||||
template <>
|
||||
template <>
|
||||
inline bool
|
||||
is_a_helper <const thread_local_region *>::test (const region *reg)
|
||||
{
|
||||
return reg->get_kind () == RK_THREAD_LOCAL;
|
||||
}
|
||||
|
||||
namespace ana {
|
||||
|
||||
/* Concrete region subclass. The root region, containing all regions
|
||||
(either directly, or as descendents).
|
||||
Unique within a region_model_manager. */
|
||||
|
@ -1362,6 +1394,32 @@ template <> struct default_hash_traits<var_arg_region::key_t>
|
|||
|
||||
namespace ana {
|
||||
|
||||
/* A region for errno for the current thread. */
|
||||
|
||||
class errno_region : public region
|
||||
{
|
||||
public:
|
||||
errno_region (unsigned id, const thread_local_region *parent)
|
||||
: region (complexity (parent), id, parent, integer_type_node)
|
||||
{}
|
||||
|
||||
enum region_kind get_kind () const final override { return RK_ERRNO; }
|
||||
|
||||
void dump_to_pp (pretty_printer *pp, bool simple) const final override;
|
||||
};
|
||||
|
||||
} // namespace ana
|
||||
|
||||
template <>
|
||||
template <>
|
||||
inline bool
|
||||
is_a_helper <const errno_region *>::test (const region *reg)
|
||||
{
|
||||
return reg->get_kind () == RK_ERRNO;
|
||||
}
|
||||
|
||||
namespace ana {
|
||||
|
||||
/* An unknown region, for handling unimplemented tree codes. */
|
||||
|
||||
class unknown_region : public region
|
||||
|
|
|
@ -2036,6 +2036,17 @@ binding_cluster::on_asm (const gasm *stmt,
|
|||
m_touched = true;
|
||||
}
|
||||
|
||||
/* Return true if this cluster has escaped. */
|
||||
|
||||
bool
|
||||
binding_cluster::escaped_p () const
|
||||
{
|
||||
/* Consider the "errno" region to always have escaped. */
|
||||
if (m_base_region->get_kind () == RK_ERRNO)
|
||||
return true;
|
||||
return m_escaped;
|
||||
}
|
||||
|
||||
/* Return true if this binding_cluster has no information
|
||||
i.e. if there are no bindings, and it hasn't been marked as having
|
||||
escaped, or touched symbolically. */
|
||||
|
@ -2946,6 +2957,10 @@ store::escaped_p (const region *base_reg) const
|
|||
gcc_assert (base_reg);
|
||||
gcc_assert (base_reg->get_base_region () == base_reg);
|
||||
|
||||
/* "errno" can always be modified by external code. */
|
||||
if (base_reg->get_kind () == RK_ERRNO)
|
||||
return true;
|
||||
|
||||
if (binding_cluster **cluster_slot
|
||||
= const_cast <cluster_map_t &>(m_cluster_map).get (base_reg))
|
||||
return (*cluster_slot)->escaped_p ();
|
||||
|
@ -3192,6 +3207,7 @@ store::replay_call_summary_cluster (call_summary_replay &r,
|
|||
case RK_CODE:
|
||||
case RK_STACK:
|
||||
case RK_HEAP:
|
||||
case RK_THREAD_LOCAL:
|
||||
case RK_ROOT:
|
||||
/* Child regions. */
|
||||
case RK_FIELD:
|
||||
|
@ -3242,6 +3258,7 @@ store::replay_call_summary_cluster (call_summary_replay &r,
|
|||
|
||||
case RK_HEAP_ALLOCATED:
|
||||
case RK_DECL:
|
||||
case RK_ERRNO:
|
||||
{
|
||||
const region *caller_dest_reg
|
||||
= r.convert_region_from_summary (summary_base_reg);
|
||||
|
|
|
@ -644,7 +644,7 @@ public:
|
|||
void on_asm (const gasm *stmt, store_manager *mgr,
|
||||
const conjured_purge &p);
|
||||
|
||||
bool escaped_p () const { return m_escaped; }
|
||||
bool escaped_p () const;
|
||||
bool touched_p () const { return m_touched; }
|
||||
|
||||
bool redundant_p () const;
|
||||
|
|
23
gcc/testsuite/gcc.dg/analyzer/errno-1.c
Normal file
23
gcc/testsuite/gcc.dg/analyzer/errno-1.c
Normal file
|
@ -0,0 +1,23 @@
|
|||
#include <errno.h>
|
||||
#include "analyzer-decls.h"
|
||||
|
||||
extern void external_fn (void);
|
||||
|
||||
int test_reading_errno (void)
|
||||
{
|
||||
return errno;
|
||||
}
|
||||
|
||||
void test_setting_errno (int val)
|
||||
{
|
||||
errno = val;
|
||||
}
|
||||
|
||||
void test_storing_to_errno (int val)
|
||||
{
|
||||
__analyzer_eval (errno == val); /* { dg-warning "UNKNOWN" } */
|
||||
errno = val;
|
||||
__analyzer_eval (errno == val); /* { dg-warning "TRUE" } */
|
||||
external_fn ();
|
||||
__analyzer_eval (errno == val); /* { dg-warning "UNKNOWN" } */
|
||||
}
|
Loading…
Add table
Reference in a new issue