std::unique_ptr<exploded_path>
get_best_epath (const exploded_node *target_enode,
+ const gimple *target_stmt,
const pending_diagnostic &pd,
const char *desc, unsigned diag_idx,
std::unique_ptr<feasibility_problem> *out_problem);
std::unique_ptr<exploded_path>
explore_feasible_paths (const exploded_node *target_enode,
+ const gimple *target_stmt,
const pending_diagnostic &pd,
const char *desc, unsigned diag_idx);
bool
const trimmed_graph &tg,
feasible_graph *fg,
const exploded_node *target_enode,
+ const gimple *target_stmt,
const pending_diagnostic &pd,
unsigned diag_idx,
std::unique_ptr<exploded_path> *out_best_path) const;
/* Get the "best" exploded_path for reaching ENODE from the origin,
returning ownership of it to the caller.
+ If TARGET_STMT is non-NULL, then check for reaching that stmt
+ within ENODE.
+
Ideally we want to report the shortest feasible path.
Return NULL if we could not find a feasible path
(when flag_analyzer_feasibility is true).
std::unique_ptr<exploded_path>
epath_finder::get_best_epath (const exploded_node *enode,
+ const gimple *target_stmt,
const pending_diagnostic &pd,
const char *desc, unsigned diag_idx,
std::unique_ptr<feasibility_problem> *out_problem)
if (logger)
logger->log ("trying to find shortest feasible path");
if (std::unique_ptr<exploded_path> epath
- = explore_feasible_paths (enode, pd, desc, diag_idx))
+ = explore_feasible_paths (enode, target_stmt, pd, desc, diag_idx))
{
if (logger)
logger->log ("accepting %qs at EN: %i, SN: %i (sd: %i)"
TARGET_ENODE by iteratively building a feasible_graph, in which
every path to a feasible_node is feasible by construction.
+ If TARGET_STMT is non-NULL, then check for reaching that stmt
+ within TARGET_ENODE.
+
We effectively explore the tree of feasible paths in order of shortest
path until we either find a feasible path to TARGET_ENODE, or hit
a limit and give up.
std::unique_ptr<exploded_path>
epath_finder::explore_feasible_paths (const exploded_node *target_enode,
+ const gimple *target_stmt,
const pending_diagnostic &pd,
const char *desc, unsigned diag_idx)
{
{
auto_checking_feasibility sentinel (mgr);
- while (process_worklist_item (&worklist, tg, &fg, target_enode, pd,
- diag_idx, &best_path))
+ while (process_worklist_item (&worklist, tg, &fg, target_enode, target_stmt,
+ pd, diag_idx, &best_path))
{
/* Empty; the work is done within process_worklist_item. */
}
const trimmed_graph &tg,
feasible_graph *fg,
const exploded_node *target_enode,
+ const gimple *target_stmt,
const pending_diagnostic &pd,
unsigned diag_idx,
std::unique_ptr<exploded_path> *out_best_path) const
" (length: %i)",
target_enode->m_index, diag_idx,
succ_fnode->get_path_length ());
- if (!pd.check_valid_fpath_p (succ_fnode))
+ if (!pd.check_valid_fpath_p (*succ_fnode, target_stmt))
{
if (logger)
logger->log ("rejecting feasible path due to"
LOG_SCOPE (logger);
m_problem = NULL;
- m_best_epath = pf->get_best_epath (m_enode, *m_d, m_d->get_kind (), m_idx,
+ m_best_epath = pf->get_best_epath (m_enode, m_stmt,
+ *m_d, m_d->get_kind (), m_idx,
&m_problem);
/* Handle failure to find a feasible path. */
auto_cfun sentinel (src_point.get_function ());
input_location = stmt->location;
- if (const gassign *assign = dyn_cast <const gassign *> (stmt))
- m_model.on_assignment (assign, NULL);
- else if (const gasm *asm_stmt = dyn_cast <const gasm *> (stmt))
- m_model.on_asm_stmt (asm_stmt, NULL);
- else if (const gcall *call = dyn_cast <const gcall *> (stmt))
- {
- bool unknown_side_effects = m_model.on_call_pre (call, NULL);
- m_model.on_call_post (call, unknown_side_effects, NULL);
- }
- else if (const greturn *return_ = dyn_cast <const greturn *> (stmt))
- m_model.on_return (return_, NULL);
+ update_for_stmt (stmt);
}
const superedge *sedge = eedge->m_sedge;
return true;
}
+/* Update this object for the effects of STMT. */
+
+void
+feasibility_state::update_for_stmt (const gimple *stmt)
+{
+ if (const gassign *assign = dyn_cast <const gassign *> (stmt))
+ m_model.on_assignment (assign, NULL);
+ else if (const gasm *asm_stmt = dyn_cast <const gasm *> (stmt))
+ m_model.on_asm_stmt (asm_stmt, NULL);
+ else if (const gcall *call = dyn_cast <const gcall *> (stmt))
+ {
+ bool unknown_side_effects = m_model.on_call_pre (call, NULL);
+ m_model.on_call_post (call, unknown_side_effects, NULL);
+ }
+ else if (const greturn *return_ = dyn_cast <const greturn *> (stmt))
+ m_model.on_return (return_, NULL);
+}
+
/* Dump this object to PP. */
void
bool maybe_update_for_edge (logger *logger,
const exploded_edge *eedge,
rejected_constraint **out_rc);
+ void update_for_stmt (const gimple *stmt);
const region_model &get_model () const { return m_model; }
const auto_sbitmap &get_snodes_visited () const { return m_snodes_visited; }
pp_flush (pp);
}
+/* Attempt to get the region_model for this node's state at TARGET_STMT.
+ Return true and write to *OUT if found.
+ Return false if there's a problem. */
+
+bool
+feasible_node::get_state_at_stmt (const gimple *target_stmt,
+ region_model *out) const
+{
+ if (!target_stmt)
+ return false;
+
+ feasibility_state result (m_state);
+
+ /* Update state for the stmts that were processed in each enode. */
+ for (unsigned stmt_idx = 0; stmt_idx < m_inner_node->m_num_processed_stmts;
+ stmt_idx++)
+ {
+ const gimple *stmt = m_inner_node->get_processed_stmt (stmt_idx);
+ if (stmt == target_stmt)
+ {
+ *out = result.get_model ();
+ return true;
+ }
+ result.update_for_stmt (stmt);
+ }
+
+ /* TARGET_STMT not found; wrong node? */
+ return false;
+}
+
/* Implementation of dump_dot vfunc for infeasible_node.
In particular, show the rejected constraint. */
#ifndef GCC_ANALYZER_FEASIBLE_GRAPH_H
#define GCC_ANALYZER_FEASIBLE_GRAPH_H
+#include "analyzer/exploded-graph.h"
+
namespace ana {
/* Forward decls. */
unsigned get_path_length () const { return m_path_length; }
+ bool get_state_at_stmt (const gimple *target_stmt,
+ region_model *out) const;
+
private:
feasibility_state m_state;
unsigned m_path_length;
/* Reject paths in which conjured svalues have affected control flow
since m_prev_entry_enode. */
- bool check_valid_fpath_p (const feasible_node *final_fnode)
+ bool check_valid_fpath_p (const feasible_node &final_fnode,
+ const gimple *)
const final override
{
/* Reject paths in which calls with unknown side effects have occurred
since m_prev_entry_enode.
Find num calls with side effects. Walk backward until we reach the
pref */
- gcc_assert (final_fnode->get_inner_node () == m_new_entry_enode);
+ gcc_assert (final_fnode.get_inner_node () == m_new_entry_enode);
/* FG is actually a tree. Walk backwards from FINAL_FNODE until we
reach the prev_entry_enode (or the origin). */
- const feasible_node *iter_fnode = final_fnode;
+ const feasible_node *iter_fnode = &final_fnode;
while (iter_fnode->get_inner_node ()->m_index != 0)
{
gcc_assert (iter_fnode->m_preds.length () == 1);
/* Vfunc to give diagnostic subclasses the opportunity to reject diagnostics
by imposing their own additional feasibility checks on the path to a
given feasible_node. */
- virtual bool check_valid_fpath_p (const feasible_node *) const
+ virtual bool check_valid_fpath_p (const feasible_node &,
+ const gimple *) const
{
/* Default implementation: accept this path. */
return true;
#include "gcc-rich-location.h"
#include "analyzer/checker-event.h"
#include "analyzer/checker-path.h"
+#include "analyzer/feasible-graph.h"
#if ENABLE_ANALYZER
{
public:
poisoned_value_diagnostic (tree expr, enum poison_kind pkind,
- const region *src_region)
+ const region *src_region,
+ tree check_expr)
: m_expr (expr), m_pkind (pkind),
- m_src_region (src_region)
+ m_src_region (src_region),
+ m_check_expr (check_expr)
{}
const char *get_kind () const final override { return "poisoned_value_diagnostic"; }
interest->add_region_creation (m_src_region);
}
+ /* Attempt to suppress false positives.
+ Reject paths where the value of the underlying region isn't poisoned.
+ This can happen due to state merging when exploring the exploded graph,
+ where the more precise analysis during feasibility analysis finds that
+ the region is in fact valid.
+ To do this we need to get the value from the fgraph. Unfortunately
+ we can't simply query the state of m_src_region (from the enode),
+ since it might be a different region in the fnode state (e.g. with
+ heap-allocated regions, the numbering could be different).
+ Hence we access m_check_expr, if available. */
+
+ bool check_valid_fpath_p (const feasible_node &fnode,
+ const gimple *emission_stmt)
+ const final override
+ {
+ if (!m_check_expr)
+ return true;
+
+ /* We've reached the enode, but not necessarily the right function_point.
+ Try to get the state at the correct stmt. */
+ region_model emission_model (fnode.get_model ().get_manager());
+ if (!fnode.get_state_at_stmt (emission_stmt, &emission_model))
+ /* Couldn't get state; accept this diagnostic. */
+ return true;
+
+ const svalue *fsval = emission_model.get_rvalue (m_check_expr, NULL);
+ /* Check to see if the expr is also poisoned in FNODE (and in the
+ same way). */
+ const poisoned_svalue * fspval = fsval->dyn_cast_poisoned_svalue ();
+ if (!fspval)
+ return false;
+ if (fspval->get_poison_kind () != m_pkind)
+ return false;
+ return true;
+ }
+
private:
tree m_expr;
enum poison_kind m_pkind;
const region *m_src_region;
+ tree m_check_expr;
};
/* A subclass of pending_diagnostic for complaining about shifts
tree diag_arg = fixup_tree_for_diagnostic (expr);
if (src_region == NULL && pkind == POISON_KIND_UNINIT)
src_region = get_region_for_poisoned_expr (expr);
+
+ /* Can we reliably get the poisoned value from "expr"?
+ This is for use by poisoned_value_diagnostic::check_valid_fpath_p.
+ Unfortunately, we might not have a reliable value for EXPR.
+ Hence we only query its value now, and only use it if we get the
+ poisoned value back again. */
+ tree check_expr = expr;
+ const svalue *foo_sval = get_rvalue (expr, NULL);
+ if (foo_sval == sval)
+ check_expr = expr;
+ else
+ check_expr = NULL;
if (ctxt->warn (make_unique<poisoned_value_diagnostic> (diag_arg,
pkind,
- src_region)))
+ src_region,
+ check_expr)))
{
/* We only want to report use of a poisoned value at the first
place it gets used; return an unknown value to avoid generating
= as_a <const poisoned_svalue *> (ptr_sval);
enum poison_kind pkind = poisoned_sval->get_poison_kind ();
ctxt->warn (make_unique<poisoned_value_diagnostic>
- (ptr, pkind, NULL));
+ (ptr, pkind, NULL, NULL));
}
}
}
this path. */
auto_bitmap base_regs_in_use;
get_referenced_base_regions (base_regs_in_use);
+
+ /* Don't reuse regions that are marked as TOUCHED. */
+ for (store::cluster_map_t::iterator iter = m_store.begin ();
+ iter != m_store.end (); ++iter)
+ if ((*iter).second->touched_p ())
+ {
+ const region *base_reg = (*iter).first;
+ bitmap_set_bit (base_regs_in_use, base_reg->get_id ());
+ }
+
const region *reg
= m_mgr->get_or_create_region_for_heap_alloc (base_regs_in_use);
if (size_in_bytes)
--- /dev/null
+/* Reduced from coreutils's cksum.c: cksum_slice8 */
+
+typedef long unsigned int size_t;
+typedef unsigned int __uint32_t;
+typedef unsigned long int __uintmax_t;
+typedef struct _IO_FILE FILE;
+extern size_t
+fread_unlocked(void* __restrict __ptr,
+ size_t __size,
+ size_t __n,
+ FILE* __restrict __stream);
+extern int
+feof_unlocked(FILE* __stream) __attribute__((__nothrow__, __leaf__));
+extern int
+ferror_unlocked(FILE* __stream) __attribute__((__nothrow__, __leaf__));
+static __inline __uint32_t
+__bswap_32(__uint32_t __bsx)
+{
+
+ return __builtin_bswap32(__bsx);
+}
+typedef __uint32_t uint32_t;
+typedef unsigned long int uint_fast32_t;
+typedef __uintmax_t uintmax_t;
+extern int*
+__errno_location(void) __attribute__((__nothrow__, __leaf__))
+__attribute__((__const__));
+extern uint_fast32_t const crctab[8][256];
+
+static _Bool
+cksum_slice8(FILE* fp, uint_fast32_t* crc_out, uintmax_t* length_out)
+{
+ uint32_t buf[(1 << 16) / sizeof(uint32_t)];
+ uint_fast32_t crc = 0;
+ uintmax_t length = 0;
+ size_t bytes_read;
+
+ if (!fp || !crc_out || !length_out)
+ return 0;
+
+ while ((bytes_read = fread_unlocked(buf, 1, (1 << 16), fp)) > 0) {
+ uint32_t* datap;
+
+ if (length + bytes_read < length) {
+
+ (*__errno_location()) = 75;
+ return 0;
+ }
+ length += bytes_read;
+
+ if (bytes_read == 0) {
+ if (ferror_unlocked(fp))
+ return 0;
+ }
+
+ datap = (uint32_t*)buf;
+ while (bytes_read >= 8) {
+ uint32_t first = *datap++, second = *datap++; /* { dg-bogus "use of uninitialized value" "PR analyzer/108664" } */
+ crc ^= __bswap_32(first);
+ second = __bswap_32(second);
+ crc =
+ (crctab[7][(crc >> 24) & 0xFF] ^ crctab[6][(crc >> 16) & 0xFF] ^
+ crctab[5][(crc >> 8) & 0xFF] ^ crctab[4][(crc)&0xFF] ^
+ crctab[3][(second >> 24) & 0xFF] ^ crctab[2][(second >> 16) & 0xFF] ^
+ crctab[1][(second >> 8) & 0xFF] ^ crctab[0][(second)&0xFF]);
+ bytes_read -= 8;
+ }
+
+ unsigned char* cp = (unsigned char*)datap;
+ while (bytes_read--)
+ crc = (crc << 8) ^ crctab[0][((crc >> 24) ^ *cp++) & 0xFF];
+ if (feof_unlocked(fp))
+ break;
+ }
+
+ *crc_out = crc;
+ *length_out = length;
+
+ return 1;
+}
--- /dev/null
+/* Reduced from coreutils's sum.c: bsd_sum_stream */
+
+typedef long unsigned int size_t;
+typedef unsigned char __uint8_t;
+typedef unsigned long int __uintmax_t;
+typedef struct _IO_FILE FILE;
+extern size_t
+fread_unlocked(void* __restrict __ptr,
+ size_t __size,
+ size_t __n,
+ FILE* __restrict __stream);
+extern int
+feof_unlocked(FILE* __stream) __attribute__((__nothrow__, __leaf__));
+extern int
+ferror_unlocked(FILE* __stream) __attribute__((__nothrow__, __leaf__));
+extern void*
+memcpy(void* __restrict __dest, const void* __restrict __src, size_t __n)
+ __attribute__((__nothrow__, __leaf__)) __attribute__((__nonnull__(1, 2)));
+extern void
+rpl_free(void*);
+extern int*
+__errno_location(void) __attribute__((__nothrow__, __leaf__))
+__attribute__((__const__));
+extern void*
+malloc(size_t __size) __attribute__((__nothrow__, __leaf__))
+__attribute__((__malloc__)) __attribute__((__alloc_size__(1)));
+typedef __uint8_t uint8_t;
+typedef __uintmax_t uintmax_t;
+
+int
+bsd_sum_stream(FILE* stream, void* resstream, uintmax_t* length)
+{
+ int ret = -1;
+ size_t sum, n;
+ int checksum = 0;
+ uintmax_t total_bytes = 0;
+ static const size_t buffer_length = 32768;
+ uint8_t* buffer = malloc(buffer_length);
+
+ if (!buffer)
+ return -1;
+
+ while (1) {
+ sum = 0;
+
+ while (1) {
+ n = fread_unlocked(buffer + sum, 1, buffer_length - sum, stream);
+ sum += n;
+
+ if (buffer_length == sum)
+ break;
+
+ if (n == 0) {
+ if (ferror_unlocked(stream))
+ goto cleanup_buffer;
+ goto final_process;
+ }
+
+ if (feof_unlocked(stream))
+ goto final_process;
+ }
+
+ for (size_t i = 0; i < sum; i++) {
+ checksum = (checksum >> 1) + ((checksum & 1) << 15);
+ checksum += buffer[i]; /* { dg-bogus "use of uninitialized value" "PR analyzer/108666" } */
+ checksum &= 0xffff;
+ }
+ if (total_bytes + sum < total_bytes) {
+
+ (*__errno_location()) = 75;
+ goto cleanup_buffer;
+ }
+ total_bytes += sum;
+ }
+
+final_process:;
+
+ for (size_t i = 0; i < sum; i++) {
+ checksum = (checksum >> 1) + ((checksum & 1) << 15);
+ checksum += buffer[i]; /* { dg-bogus "use of uninitialized value" "PR analyzer/108666" } */
+ checksum &= 0xffff;
+ }
+ if (total_bytes + sum < total_bytes) {
+
+ (*__errno_location()) = 75;
+ goto cleanup_buffer;
+ }
+ total_bytes += sum;
+
+ memcpy(resstream, &checksum, sizeof checksum);
+ *length = total_bytes;
+ ret = 0;
+cleanup_buffer:
+
+ rpl_free(buffer);
+ return ret;
+}
+
--- /dev/null
+/* Reduced from an example in qemu-7.2.0: dump/win_dump.c */
+
+#include <stdbool.h>
+#include <stdint.h>
+
+extern int cpu_memory_rw_debug (bool x64, void *ptr);
+
+int cpu_read_ptr(bool x64, uint64_t *ptr)
+{
+ int ret;
+ uint32_t ptr32;
+ uint64_t ptr64;
+
+ ret = cpu_memory_rw_debug(x64, x64 ? (void *)&ptr64 : (void *)&ptr32);
+
+ *ptr = x64 ? ptr64 : ptr32; /* { dg-bogus "use of uninitialized value" } */
+
+ return ret;
+}