diff options
-rw-r--r-- | gcc/analyzer/analyzer.h | 5 | ||||
-rw-r--r-- | gcc/analyzer/engine.cc | 5 | ||||
-rw-r--r-- | gcc/analyzer/program-state.cc | 1 | ||||
-rw-r--r-- | gcc/analyzer/region-model-impl-calls.cc | 39 | ||||
-rw-r--r-- | gcc/analyzer/region-model-manager.cc | 313 | ||||
-rw-r--r-- | gcc/analyzer/region-model.cc | 72 | ||||
-rw-r--r-- | gcc/analyzer/region-model.h | 27 | ||||
-rw-r--r-- | gcc/analyzer/region.cc | 230 | ||||
-rw-r--r-- | gcc/analyzer/region.h | 125 | ||||
-rw-r--r-- | gcc/analyzer/store.cc | 653 | ||||
-rw-r--r-- | gcc/analyzer/store.h | 157 | ||||
-rw-r--r-- | gcc/analyzer/svalue.cc | 381 | ||||
-rw-r--r-- | gcc/analyzer/svalue.h | 262 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/analyzer/clobbers-1.c | 98 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/analyzer/clobbers-2.c | 72 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/analyzer/data-model-1.c | 24 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/analyzer/explode-1.c | 2 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/analyzer/memset-1.c | 118 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/analyzer/memset-CVE-2017-18549-1.c | 107 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/analyzer/symbolic-8.c | 11 |
20 files changed, 2207 insertions, 495 deletions
diff --git a/gcc/analyzer/analyzer.h b/gcc/analyzer/analyzer.h index f06b68c..02830e4 100644 --- a/gcc/analyzer/analyzer.h +++ b/gcc/analyzer/analyzer.h @@ -46,6 +46,8 @@ class svalue; class unaryop_svalue; class binop_svalue; class sub_svalue; + class repeated_svalue; + class bits_within_svalue; class unmergeable_svalue; class placeholder_svalue; class widening_svalue; @@ -60,6 +62,7 @@ class region; class symbolic_region; class element_region; class offset_region; + class sized_region; class cast_region; class field_region; class string_region; @@ -147,6 +150,8 @@ typedef offset_int byte_size_t; extern bool int_size_in_bits (const_tree type, bit_size_t *out); +extern tree get_field_at_bit_offset (tree record_type, bit_offset_t bit_offset); + /* The location of a region expressesd as an offset relative to a base region. */ diff --git a/gcc/analyzer/engine.cc b/gcc/analyzer/engine.cc index f322fdb..4456d9b 100644 --- a/gcc/analyzer/engine.cc +++ b/gcc/analyzer/engine.cc @@ -2275,6 +2275,7 @@ exploded_graph::get_or_create_node (const program_point &point, if (pruned_state.can_merge_with_p (existing_state, point, &merged_state)) { + merged_state.validate (m_ext_state); if (logger) logger->log ("merging new state with that of EN: %i", existing_enode->m_index); @@ -2794,6 +2795,7 @@ maybe_process_run_of_before_supernode_enodes (exploded_node *enode) items.quick_push (it); const program_state &state = iter_enode->get_state (); program_state *next_state = &it->m_processed_state; + next_state->validate (m_ext_state); const program_point &iter_point = iter_enode->get_point (); if (const superedge *iter_sedge = iter_point.get_from_edge ()) { @@ -2807,6 +2809,7 @@ maybe_process_run_of_before_supernode_enodes (exploded_node *enode) next_state->m_region_model->update_for_phis (snode, last_cfg_superedge, &ctxt); } + next_state->validate (m_ext_state); } /* Attempt to partition the items into a set of merged states. @@ -2823,10 +2826,12 @@ maybe_process_run_of_before_supernode_enodes (exploded_node *enode) unsigned iter_merger_idx; FOR_EACH_VEC_ELT (merged_states, iter_merger_idx, merged_state) { + merged_state->validate (m_ext_state); program_state merge (m_ext_state); if (it_state.can_merge_with_p (*merged_state, next_point, &merge)) { *merged_state = merge; + merged_state->validate (m_ext_state); it->m_merger_idx = iter_merger_idx; if (logger) logger->log ("reusing merger state %i for item %i (EN: %i)", diff --git a/gcc/analyzer/program-state.cc b/gcc/analyzer/program-state.cc index 67dd785..6d60c04 100644 --- a/gcc/analyzer/program-state.cc +++ b/gcc/analyzer/program-state.cc @@ -1142,6 +1142,7 @@ program_state::validate (const extrinsic_state &ext_state) const #endif gcc_assert (m_checker_states.length () == ext_state.get_num_checkers ()); + m_region_model->validate (); } static void diff --git a/gcc/analyzer/region-model-impl-calls.cc b/gcc/analyzer/region-model-impl-calls.cc index 099520a..466d397 100644 --- a/gcc/analyzer/region-model-impl-calls.cc +++ b/gcc/analyzer/region-model-impl-calls.cc @@ -389,36 +389,15 @@ region_model::impl_call_memset (const call_details &cd) const region *dest_reg = deref_rvalue (dest_sval, cd.get_arg_tree (0), cd.get_ctxt ()); - if (tree num_bytes = num_bytes_sval->maybe_get_constant ()) - { - /* "memset" of zero size is a no-op. */ - if (zerop (num_bytes)) - return true; - - /* Set with known amount. */ - byte_size_t reg_size; - if (dest_reg->get_byte_size (®_size)) - { - /* Check for an exact size match. */ - if (reg_size == wi::to_offset (num_bytes)) - { - if (tree cst = fill_value_sval->maybe_get_constant ()) - { - if (zerop (cst)) - { - zero_fill_region (dest_reg); - return true; - } - } - } - } - } - - check_for_writable_region (dest_reg, cd.get_ctxt ()); - - /* Otherwise, mark region's contents as unknown. */ - mark_region_as_unknown (dest_reg, cd.get_uncertainty ()); - return false; + const svalue *fill_value_u8 + = m_mgr->get_or_create_cast (unsigned_char_type_node, fill_value_sval); + + const region *sized_dest_reg = m_mgr->get_sized_region (dest_reg, + NULL_TREE, + num_bytes_sval); + check_for_writable_region (sized_dest_reg, cd.get_ctxt ()); + fill_region (sized_dest_reg, fill_value_u8); + return true; } /* Handle the on_call_pre part of "operator new". */ diff --git a/gcc/analyzer/region-model-manager.cc b/gcc/analyzer/region-model-manager.cc index 1ee6663..55acb90 100644 --- a/gcc/analyzer/region-model-manager.cc +++ b/gcc/analyzer/region-model-manager.cc @@ -477,7 +477,7 @@ maybe_undo_optimize_bit_field_compare (tree type, bound_bits = bit_range (BITS_PER_UNIT - bits.get_next_bit_offset (), bits.m_size_in_bits); const concrete_binding *conc - = get_store_manager ()->get_concrete_binding (bound_bits, BK_direct); + = get_store_manager ()->get_concrete_binding (bound_bits); const svalue *sval = map.get (conc); if (!sval) return NULL; @@ -686,13 +686,13 @@ region_model_manager::maybe_fold_sub_svalue (tree type, return get_or_create_cast (type, char_sval); } - /* SUB(INIT(r)).FIELD -> INIT(r.FIELD) - i.e. - Subvalue(InitialValue(R1), FieldRegion(R2, F)) - -> InitialValue(FieldRegion(R1, F)). */ if (const initial_svalue *init_sval - = parent_svalue->dyn_cast_initial_svalue ()) + = parent_svalue->dyn_cast_initial_svalue ()) { + /* SUB(INIT(r)).FIELD -> INIT(r.FIELD) + i.e. + Subvalue(InitialValue(R1), FieldRegion(R2, F)) + -> InitialValue(FieldRegion(R1, F)). */ if (const field_region *field_reg = subregion->dyn_cast_field_region ()) { const region *field_reg_new @@ -700,8 +700,24 @@ region_model_manager::maybe_fold_sub_svalue (tree type, field_reg->get_field ()); return get_or_create_initial_value (field_reg_new); } + /* SUB(INIT(r)[ELEMENT] -> INIT(e[ELEMENT]) + i.e. + Subvalue(InitialValue(R1), ElementRegion(R2, IDX)) + -> InitialValue(ElementRegion(R1, IDX)). */ + if (const element_region *element_reg = subregion->dyn_cast_element_region ()) + { + const region *element_reg_new + = get_element_region (init_sval->get_region (), + element_reg->get_type (), + element_reg->get_index ()); + return get_or_create_initial_value (element_reg_new); + } } + if (const repeated_svalue *repeated_sval + = parent_svalue->dyn_cast_repeated_svalue ()) + return get_or_create_cast (type, repeated_sval->get_inner_svalue ()); + return NULL; } @@ -727,6 +743,255 @@ region_model_manager::get_or_create_sub_svalue (tree type, return sub_sval; } +/* Subroutine of region_model_manager::get_or_create_repeated_svalue. + Return a folded svalue, or NULL. */ + +const svalue * +region_model_manager::maybe_fold_repeated_svalue (tree type, + const svalue *outer_size, + const svalue *inner_svalue) +{ + /* If INNER_SVALUE is the same size as OUTER_SIZE, + turn into simply a cast. */ + if (tree cst_outer_num_bytes = outer_size->maybe_get_constant ()) + { + HOST_WIDE_INT num_bytes_inner_svalue + = int_size_in_bytes (inner_svalue->get_type ()); + if (num_bytes_inner_svalue != -1) + if (num_bytes_inner_svalue + == (HOST_WIDE_INT)tree_to_uhwi (cst_outer_num_bytes)) + { + if (type) + return get_or_create_cast (type, inner_svalue); + else + return inner_svalue; + } + } + + /* Handle zero-fill of a specific type. */ + if (tree cst = inner_svalue->maybe_get_constant ()) + if (zerop (cst) && type) + return get_or_create_cast (type, inner_svalue); + + return NULL; +} + +/* Return the svalue * of type TYPE in which INNER_SVALUE is repeated + enough times to be of size OUTER_SIZE, creating it if necessary. + e.g. for filling buffers with a constant value. */ + +const svalue * +region_model_manager::get_or_create_repeated_svalue (tree type, + const svalue *outer_size, + const svalue *inner_svalue) +{ + if (const svalue *folded + = maybe_fold_repeated_svalue (type, outer_size, inner_svalue)) + return folded; + + repeated_svalue::key_t key (type, outer_size, inner_svalue); + if (repeated_svalue **slot = m_repeated_values_map.get (key)) + return *slot; + repeated_svalue *repeated_sval + = new repeated_svalue (type, outer_size, inner_svalue); + RETURN_UNKNOWN_IF_TOO_COMPLEX (repeated_sval); + m_repeated_values_map.put (key, repeated_sval); + return repeated_sval; +} + +/* Attempt to get the bit_range for FIELD within a RECORD_TYPE. + Return true and write the result to OUT if successful. + Return false otherwise. */ + +static bool +get_bit_range_for_field (tree field, bit_range *out) +{ + bit_size_t bit_size; + if (!int_size_in_bits (TREE_TYPE (field), &bit_size)) + return false; + int field_bit_offset = int_bit_position (field); + *out = bit_range (field_bit_offset, bit_size); + return true; +} + +/* Attempt to get the byte_range for FIELD within a RECORD_TYPE. + Return true and write the result to OUT if successful. + Return false otherwise. */ + +static bool +get_byte_range_for_field (tree field, byte_range *out) +{ + bit_range field_bits (0, 0); + if (!get_bit_range_for_field (field, &field_bits)) + return false; + return field_bits.as_byte_range (out); +} + +/* Attempt to determine if there is a specific field within RECORD_TYPE + at BYTES. If so, return it, and write the location of BYTES relative + to the field to *OUT_RANGE_WITHIN_FIELD. + Otherwise, return NULL_TREE. + For example, given: + struct foo { uint32 a; uint32; b}; + and + bytes = {bytes 6-7} (of foo) + we have bytes 3-4 of field b. */ + +static tree +get_field_at_byte_range (tree record_type, const byte_range &bytes, + byte_range *out_range_within_field) +{ + bit_offset_t bit_offset = bytes.m_start_byte_offset * BITS_PER_UNIT; + + tree field = get_field_at_bit_offset (record_type, bit_offset); + if (!field) + return NULL_TREE; + + byte_range field_bytes (0,0); + if (!get_byte_range_for_field (field, &field_bytes)) + return NULL_TREE; + + /* Is BYTES fully within field_bytes? */ + byte_range bytes_within_field (0,0); + if (!field_bytes.contains_p (bytes, &bytes_within_field)) + return NULL_TREE; + + *out_range_within_field = bytes_within_field; + return field; +} + +/* Subroutine of region_model_manager::get_or_create_bits_within. + Return a folded svalue, or NULL. */ + +const svalue * +region_model_manager::maybe_fold_bits_within_svalue (tree type, + const bit_range &bits, + const svalue *inner_svalue) +{ + tree inner_type = inner_svalue->get_type (); + /* Fold: + BITS_WITHIN ((0, sizeof (VAL), VAL)) + to: + CAST(TYPE, VAL). */ + if (bits.m_start_bit_offset == 0 && inner_type) + { + bit_size_t inner_type_size; + if (int_size_in_bits (inner_type, &inner_type_size)) + if (inner_type_size == bits.m_size_in_bits) + { + if (type) + return get_or_create_cast (type, inner_svalue); + else + return inner_svalue; + } + } + + /* Kind-specific folding. */ + if (const svalue *sval + = inner_svalue->maybe_fold_bits_within (type, bits, this)) + return sval; + + byte_range bytes (0,0); + if (bits.as_byte_range (&bytes) && inner_type) + switch (TREE_CODE (inner_type)) + { + default: + break; + case ARRAY_TYPE: + { + /* Fold: + BITS_WITHIN (range, KIND(REG)) + to: + BITS_WITHIN (range - offsetof(ELEMENT), KIND(REG.ELEMENT)) + if range1 is a byte-range fully within one ELEMENT. */ + tree element_type = TREE_TYPE (inner_type); + HOST_WIDE_INT element_byte_size + = int_size_in_bytes (element_type); + if (element_byte_size > 0) + { + HOST_WIDE_INT start_idx + = (bytes.get_start_byte_offset ().to_shwi () + / element_byte_size); + HOST_WIDE_INT last_idx + = (bytes.get_last_byte_offset ().to_shwi () + / element_byte_size); + if (start_idx == last_idx) + { + if (const initial_svalue *initial_sval + = inner_svalue->dyn_cast_initial_svalue ()) + { + bit_offset_t start_of_element + = start_idx * element_byte_size * BITS_PER_UNIT; + bit_range bits_within_element + (bits.m_start_bit_offset - start_of_element, + bits.m_size_in_bits); + const svalue *idx_sval + = get_or_create_int_cst (integer_type_node, start_idx); + const region *element_reg = + get_element_region (initial_sval->get_region (), + element_type, idx_sval); + const svalue *element_reg_sval + = get_or_create_initial_value (element_reg); + return get_or_create_bits_within (type, + bits_within_element, + element_reg_sval); + } + } + } + } + break; + case RECORD_TYPE: + { + /* Fold: + BYTES_WITHIN (range, KIND(REG)) + to: + BYTES_WITHIN (range - offsetof(FIELD), KIND(REG.FIELD)) + if range1 is fully within FIELD. */ + byte_range bytes_within_field (0, 0); + if (tree field = get_field_at_byte_range (inner_type, bytes, + &bytes_within_field)) + { + if (const initial_svalue *initial_sval + = inner_svalue->dyn_cast_initial_svalue ()) + { + const region *field_reg = + get_field_region (initial_sval->get_region (), field); + const svalue *initial_reg_sval + = get_or_create_initial_value (field_reg); + return get_or_create_bits_within + (type, + bytes_within_field.as_bit_range (), + initial_reg_sval); + } + } + } + break; + } + return NULL; +} + +/* Return the svalue * of type TYPE for extracting BITS from INNER_SVALUE, + creating it if necessary. */ + +const svalue * +region_model_manager::get_or_create_bits_within (tree type, + const bit_range &bits, + const svalue *inner_svalue) +{ + if (const svalue *folded + = maybe_fold_bits_within_svalue (type, bits, inner_svalue)) + return folded; + + bits_within_svalue::key_t key (type, bits, inner_svalue); + if (bits_within_svalue **slot = m_bits_within_values_map.get (key)) + return *slot; + bits_within_svalue *bits_within_sval + = new bits_within_svalue (type, bits, inner_svalue); + RETURN_UNKNOWN_IF_TOO_COMPLEX (bits_within_sval); + m_bits_within_values_map.put (key, bits_within_sval); + return bits_within_sval; +} + /* Return the svalue * that decorates ARG as being unmergeable, creating it if necessary. */ @@ -966,6 +1231,38 @@ region_model_manager::get_offset_region (const region *parent, return offset_reg; } +/* Return the region that describes accessing the subregion of type + TYPE of size BYTE_SIZE_SVAL within PARENT, creating it if necessary. */ + +const region * +region_model_manager::get_sized_region (const region *parent, + tree type, + const svalue *byte_size_sval) +{ + if (byte_size_sval->get_type () != size_type_node) + byte_size_sval = get_or_create_cast (size_type_node, byte_size_sval); + + /* If PARENT is already that size, return it. */ + const svalue *parent_byte_size_sval = parent->get_byte_size_sval (this); + if (tree parent_size_cst = parent_byte_size_sval->maybe_get_constant ()) + if (tree size_cst = byte_size_sval->maybe_get_constant ()) + { + tree comparison + = fold_binary (EQ_EXPR, boolean_type_node, parent_size_cst, size_cst); + if (comparison == boolean_true_node) + return parent; + } + + sized_region::key_t key (parent, type, byte_size_sval); + if (sized_region *reg = m_sized_regions.get (key)) + return reg; + + sized_region *sized_reg + = new sized_region (alloc_region_id (), parent, type, byte_size_sval); + m_sized_regions.put (key, sized_reg); + return sized_reg; +} + /* Return the region that describes accessing PARENT_REGION as if it were of type TYPE, creating it if necessary. */ @@ -1180,6 +1477,9 @@ region_model_manager::log_stats (logger *logger, bool show_objs) const log_uniq_map (logger, show_objs, "unaryop_svalue", m_unaryop_values_map); log_uniq_map (logger, show_objs, "binop_svalue", m_binop_values_map); log_uniq_map (logger, show_objs, "sub_svalue", m_sub_values_map); + log_uniq_map (logger, show_objs, "repeated_svalue", m_repeated_values_map); + log_uniq_map (logger, show_objs, "bits_within_svalue", + m_bits_within_values_map); log_uniq_map (logger, show_objs, "unmergeable_svalue", m_unmergeable_values_map); log_uniq_map (logger, show_objs, "widening_svalue", m_widening_values_map); @@ -1198,6 +1498,7 @@ region_model_manager::log_stats (logger *logger, bool show_objs) const log_uniq_map (logger, show_objs, "field_region", m_field_regions); log_uniq_map (logger, show_objs, "element_region", m_element_regions); log_uniq_map (logger, show_objs, "offset_region", m_offset_regions); + log_uniq_map (logger, show_objs, "sized_region", m_sized_regions); log_uniq_map (logger, show_objs, "cast_region", m_cast_regions); log_uniq_map (logger, show_objs, "frame_region", m_frame_regions); log_uniq_map (logger, show_objs, "symbolic_region", m_symbolic_regions); diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc index ee11e82..4fb6bc9 100644 --- a/gcc/analyzer/region-model.cc +++ b/gcc/analyzer/region-model.cc @@ -393,6 +393,14 @@ region_model::debug () const dump (true); } +/* Assert that this object is valid. */ + +void +region_model::validate () const +{ + m_store.validate (); +} + /* Canonicalize the store and constraints, to maximize the chance of equality between region_model instances. */ @@ -847,11 +855,9 @@ region_model::on_assignment (const gassign *assign, region_model_context *ctxt) case STRING_CST: { /* e.g. "struct s2 x = {{'A', 'B', 'C', 'D'}};". */ - /* Add a default binding, rather than a direct one, so that array - access will "inherit" the individual chars. */ const svalue *rhs_sval = get_rvalue (rhs1, ctxt); m_store.set_value (m_mgr->get_store_manager(), lhs_reg, rhs_sval, - BK_default, ctxt ? ctxt->get_uncertainty () : NULL); + ctxt ? ctxt->get_uncertainty () : NULL); } break; } @@ -1662,8 +1668,7 @@ region_model::get_initial_value_for_global (const region *reg) const { /* Get the value for REG within base_reg_init. */ binding_cluster c (base_reg); - c.bind (m_mgr->get_store_manager (), base_reg, base_reg_init, - BK_direct); + c.bind (m_mgr->get_store_manager (), base_reg, base_reg_init); const svalue *sval = c.get_any_binding (m_mgr->get_store_manager (), reg); if (sval) @@ -1854,45 +1859,7 @@ region_model::get_rvalue_for_bits (tree type, const bit_range &bits) const { const svalue *sval = get_store_value (reg); - if (const compound_svalue *compound_sval = sval->dyn_cast_compound_svalue ()) - { - const binding_map &map = compound_sval->get_map (); - binding_map result_map; - for (auto iter : map) - { - const binding_key *key = iter.first; - if (const concrete_binding *conc_key - = key->dyn_cast_concrete_binding ()) - { - /* Ignore concrete bindings outside BITS. */ - if (!conc_key->get_bit_range ().intersects_p (bits)) - continue; - if ((conc_key->get_start_bit_offset () - < bits.get_start_bit_offset ()) - || (conc_key->get_next_bit_offset () - > bits.get_next_bit_offset ())) - { - /* If we have any concrete keys that aren't fully within BITS, - then bail out. */ - return m_mgr->get_or_create_unknown_svalue (type); - } - const concrete_binding *offset_conc_key - = m_mgr->get_store_manager ()->get_concrete_binding - (conc_key->get_start_bit_offset () - - bits.get_start_bit_offset (), - conc_key->get_size_in_bits (), - conc_key->get_kind ()); - const svalue *sval = iter.second; - result_map.put (offset_conc_key, sval); - } - else - /* If we have any symbolic keys we can't get it as bits. */ - return m_mgr->get_or_create_unknown_svalue (type); - } - return m_mgr->get_or_create_compound_svalue (type, result_map); - } - - return m_mgr->get_or_create_unknown_svalue (type); + return m_mgr->get_or_create_bits_within (type, bits, sval); } /* A subclass of pending_diagnostic for complaining about writes to @@ -2035,6 +2002,10 @@ region_model::get_capacity (const region *reg) const } } break; + case RK_SIZED: + /* Look through sized regions to get at the capacity + of the underlying regions. */ + return get_capacity (reg->get_parent_region ()); } if (const svalue *recorded = get_dynamic_extents (reg)) @@ -2056,7 +2027,7 @@ region_model::set_value (const region *lhs_reg, const svalue *rhs_sval, check_for_writable_region (lhs_reg, ctxt); m_store.set_value (m_mgr->get_store_manager(), lhs_reg, rhs_sval, - BK_direct, ctxt ? ctxt->get_uncertainty () : NULL); + ctxt ? ctxt->get_uncertainty () : NULL); } /* Set the value of the region given by LHS to the value given by RHS. */ @@ -2087,6 +2058,14 @@ region_model::purge_region (const region *reg) m_store.purge_region (m_mgr->get_store_manager(), reg); } +/* Fill REG with SVAL. */ + +void +region_model::fill_region (const region *reg, const svalue *sval) +{ + m_store.fill_region (m_mgr->get_store_manager(), reg, sval); +} + /* Zero-fill REG. */ void @@ -2711,6 +2690,9 @@ region_model::get_representative_path_var_1 (const region *reg, parent_pv.m_stack_depth); } + case RK_SIZED: + return path_var (NULL_TREE, 0); + case RK_CAST: { path_var parent_pv diff --git a/gcc/analyzer/region-model.h b/gcc/analyzer/region-model.h index a4b584d..b42852b 100644 --- a/gcc/analyzer/region-model.h +++ b/gcc/analyzer/region-model.h @@ -212,6 +212,8 @@ public: virtual void visit_unaryop_svalue (const unaryop_svalue *) {} virtual void visit_binop_svalue (const binop_svalue *) {} virtual void visit_sub_svalue (const sub_svalue *) {} + virtual void visit_repeated_svalue (const repeated_svalue *) {} + virtual void visit_bits_within_svalue (const bits_within_svalue *) {} virtual void visit_unmergeable_svalue (const unmergeable_svalue *) {} virtual void visit_placeholder_svalue (const placeholder_svalue *) {} virtual void visit_widening_svalue (const widening_svalue *) {} @@ -255,6 +257,12 @@ public: const svalue *get_or_create_sub_svalue (tree type, const svalue *parent_svalue, const region *subregion); + const svalue *get_or_create_repeated_svalue (tree type, + const svalue *outer_size, + const svalue *inner_svalue); + const svalue *get_or_create_bits_within (tree type, + const bit_range &bits, + const svalue *inner_svalue); const svalue *get_or_create_unmergeable (const svalue *arg); const svalue *get_or_create_widening_svalue (tree type, const program_point &point, @@ -286,6 +294,9 @@ public: const region *get_offset_region (const region *parent, tree type, const svalue *byte_offset); + const region *get_sized_region (const region *parent, + tree type, + const svalue *byte_size_sval); const region *get_cast_region (const region *original_region, tree type); const frame_region *get_frame_region (const frame_region *calling_frame, @@ -321,6 +332,12 @@ private: const svalue *maybe_fold_sub_svalue (tree type, const svalue *parent_svalue, const region *subregion); + const svalue *maybe_fold_repeated_svalue (tree type, + const svalue *outer_size, + const svalue *inner_svalue); + const svalue *maybe_fold_bits_within_svalue (tree type, + const bit_range &bits, + const svalue *inner_svalue); const svalue *maybe_undo_optimize_bit_field_compare (tree type, const compound_svalue *compound_sval, tree cst, const svalue *arg1); @@ -362,6 +379,14 @@ private: typedef hash_map<sub_svalue::key_t, sub_svalue *> sub_values_map_t; sub_values_map_t m_sub_values_map; + typedef hash_map<repeated_svalue::key_t, + repeated_svalue *> repeated_values_map_t; + repeated_values_map_t m_repeated_values_map; + + typedef hash_map<bits_within_svalue::key_t, + bits_within_svalue *> bits_within_values_map_t; + bits_within_values_map_t m_bits_within_values_map; + typedef hash_map<const svalue *, unmergeable_svalue *> unmergeable_values_map_t; unmergeable_values_map_t m_unmergeable_values_map; @@ -402,6 +427,7 @@ private: consolidation_map<field_region> m_field_regions; consolidation_map<element_region> m_element_regions; consolidation_map<offset_region> m_offset_regions; + consolidation_map<sized_region> m_sized_regions; consolidation_map<cast_region> m_cast_regions; consolidation_map<frame_region> m_frame_regions; consolidation_map<symbolic_region> m_symbolic_regions; @@ -575,6 +601,7 @@ class region_model void set_value (tree lhs, tree rhs, region_model_context *ctxt); void clobber_region (const region *reg); void purge_region (const region *reg); + void fill_region (const region *reg, const svalue *sval); void zero_fill_region (const region *reg); void mark_region_as_unknown (const region *reg, uncertainty_t *uncertainty); diff --git a/gcc/analyzer/region.cc b/gcc/analyzer/region.cc index 5f246df..4633717 100644 --- a/gcc/analyzer/region.cc +++ b/gcc/analyzer/region.cc @@ -98,6 +98,7 @@ region::get_base_region () const case RK_FIELD: case RK_ELEMENT: case RK_OFFSET: + case RK_SIZED: iter = iter->get_parent_region (); continue; case RK_CAST: @@ -121,6 +122,7 @@ region::base_region_p () const case RK_FIELD: case RK_ELEMENT: case RK_OFFSET: + case RK_SIZED: case RK_CAST: return false; @@ -188,7 +190,8 @@ region::get_offset () const return *m_cached_offset; } -/* If the size of this region (in bytes) is known statically, write it to *OUT +/* Base class implementation of region::get_byte_size vfunc. + If the size of this region (in bytes) is known statically, write it to *OUT and return true. Otherwise return false. */ @@ -208,8 +211,29 @@ region::get_byte_size (byte_size_t *out) const return true; } -/* If the size of TYPE (in bits) is constant, write it to *OUT - and return true. +/* Base implementation of region::get_byte_size_sval vfunc. */ + +const svalue * +region::get_byte_size_sval (region_model_manager *mgr) const +{ + tree type = get_type (); + + /* Bail out e.g. for heap-allocated regions. */ + if (!type) + return mgr->get_or_create_unknown_svalue (size_type_node); + + HOST_WIDE_INT bytes = int_size_in_bytes (type); + if (bytes == -1) + return mgr->get_or_create_unknown_svalue (size_type_node); + + tree byte_size = size_in_bytes (type); + if (TREE_TYPE (byte_size) != size_type_node) + byte_size = fold_build1 (NOP_EXPR, size_type_node, byte_size); + return mgr->get_or_create_constant_svalue (byte_size); +} + +/* Attempt to get the size of TYPE in bits. + If successful, return true and write the size to *OUT. Otherwise return false. */ bool @@ -249,7 +273,7 @@ region::get_bit_size (bit_size_t *out) const /* Get the field within RECORD_TYPE at BIT_OFFSET. */ -static tree +tree get_field_at_bit_offset (tree record_type, bit_offset_t bit_offset) { gcc_assert (TREE_CODE (record_type) == RECORD_TYPE); @@ -375,18 +399,10 @@ region::calc_offset () const = (const field_region *)iter_region; iter_region = iter_region->get_parent_region (); - /* Compare with e.g. gimple-fold.c's - fold_nonarray_ctor_reference. */ - tree field = field_reg->get_field (); - tree byte_offset = DECL_FIELD_OFFSET (field); - if (TREE_CODE (byte_offset) != INTEGER_CST) + bit_offset_t rel_bit_offset; + if (!field_reg->get_relative_concrete_offset (&rel_bit_offset)) return region_offset::make_symbolic (iter_region); - tree field_offset = DECL_FIELD_BIT_OFFSET (field); - /* Compute bit offset of the field. */ - offset_int bitoffset - = (wi::to_offset (field_offset) - + (wi::to_offset (byte_offset) << LOG2_BITS_PER_UNIT)); - accum_bit_offset += bitoffset; + accum_bit_offset += rel_bit_offset; } continue; @@ -396,28 +412,10 @@ region::calc_offset () const = (const element_region *)iter_region; iter_region = iter_region->get_parent_region (); - if (tree idx_cst - = element_reg->get_index ()->maybe_get_constant ()) - { - gcc_assert (TREE_CODE (idx_cst) == INTEGER_CST); - - tree elem_type = element_reg->get_type (); - offset_int element_idx = wi::to_offset (idx_cst); - - /* First, use int_size_in_bytes, to reject the case where we - have an incomplete type, or a non-constant value. */ - HOST_WIDE_INT hwi_byte_size = int_size_in_bytes (elem_type); - if (hwi_byte_size > 0) - { - offset_int element_bit_size - = hwi_byte_size << LOG2_BITS_PER_UNIT; - offset_int element_bit_offset - = element_idx * element_bit_size; - accum_bit_offset += element_bit_offset; - continue; - } - } - return region_offset::make_symbolic (iter_region); + bit_offset_t rel_bit_offset; + if (!element_reg->get_relative_concrete_offset (&rel_bit_offset)) + return region_offset::make_symbolic (iter_region); + accum_bit_offset += rel_bit_offset; } continue; @@ -427,22 +425,17 @@ region::calc_offset () const = (const offset_region *)iter_region; iter_region = iter_region->get_parent_region (); - if (tree byte_offset_cst - = offset_reg->get_byte_offset ()->maybe_get_constant ()) - { - gcc_assert (TREE_CODE (byte_offset_cst) == INTEGER_CST); - /* Use a signed value for the byte offset, to handle - negative offsets. */ - HOST_WIDE_INT byte_offset - = wi::to_offset (byte_offset_cst).to_shwi (); - HOST_WIDE_INT bit_offset = byte_offset * BITS_PER_UNIT; - accum_bit_offset += bit_offset; - } - else + bit_offset_t rel_bit_offset; + if (!offset_reg->get_relative_concrete_offset (&rel_bit_offset)) return region_offset::make_symbolic (iter_region); + accum_bit_offset += rel_bit_offset; } continue; + case RK_SIZED: + iter_region = iter_region->get_parent_region (); + continue; + case RK_CAST: { const cast_region *cast_reg @@ -458,6 +451,14 @@ region::calc_offset () const return region_offset::make_concrete (iter_region, accum_bit_offset); } +/* Base implementation of region::get_relative_concrete_offset vfunc. */ + +bool +region::get_relative_concrete_offset (bit_offset_t *) const +{ + return false; +} + /* Copy from SRC_REG to DST_REG, using CTXT for any issues that occur. */ void @@ -984,7 +985,7 @@ decl_region::get_svalue_for_initializer (region_model_manager *mgr) const which can fail if we have a region with unknown size (e.g. "extern const char arr[];"). */ const binding_key *binding - = binding_key::make (mgr->get_store_manager (), this, BK_direct); + = binding_key::make (mgr->get_store_manager (), this); if (binding->symbolic_p ()) return NULL; @@ -1030,6 +1031,26 @@ field_region::dump_to_pp (pretty_printer *pp, bool simple) const } } +/* Implementation of region::get_relative_concrete_offset vfunc + for field_region. */ + +bool +field_region::get_relative_concrete_offset (bit_offset_t *out) const +{ + /* Compare with e.g. gimple-fold.c's + fold_nonarray_ctor_reference. */ + tree byte_offset = DECL_FIELD_OFFSET (m_field); + if (TREE_CODE (byte_offset) != INTEGER_CST) + return false; + tree field_offset = DECL_FIELD_BIT_OFFSET (m_field); + /* Compute bit offset of the field. */ + offset_int bitoffset + = (wi::to_offset (field_offset) + + (wi::to_offset (byte_offset) << LOG2_BITS_PER_UNIT)); + *out = bitoffset; + return true; +} + /* class element_region : public region. */ /* Implementation of region::accept vfunc for element_region. */ @@ -1067,6 +1088,35 @@ element_region::dump_to_pp (pretty_printer *pp, bool simple) const } } +/* Implementation of region::get_relative_concrete_offset vfunc + for element_region. */ + +bool +element_region::get_relative_concrete_offset (bit_offset_t *out) const +{ + if (tree idx_cst = m_index->maybe_get_constant ()) + { + gcc_assert (TREE_CODE (idx_cst) == INTEGER_CST); + + tree elem_type = get_type (); + offset_int element_idx = wi::to_offset (idx_cst); + + /* First, use int_size_in_bytes, to reject the case where we + have an incomplete type, or a non-constant value. */ + HOST_WIDE_INT hwi_byte_size = int_size_in_bytes (elem_type); + if (hwi_byte_size > 0) + { + offset_int element_bit_size + = hwi_byte_size << LOG2_BITS_PER_UNIT; + offset_int element_bit_offset + = element_idx * element_bit_size; + *out = element_bit_offset; + return true; + } + } + return false; +} + /* class offset_region : public region. */ /* Implementation of region::accept vfunc for offset_region. */ @@ -1103,6 +1153,86 @@ offset_region::dump_to_pp (pretty_printer *pp, bool simple) const } } +/* Implementation of region::get_relative_concrete_offset vfunc + for offset_region. */ + +bool +offset_region::get_relative_concrete_offset (bit_offset_t *out) const +{ + if (tree byte_offset_cst = m_byte_offset->maybe_get_constant ()) + { + gcc_assert (TREE_CODE (byte_offset_cst) == INTEGER_CST); + /* Use a signed value for the byte offset, to handle + negative offsets. */ + HOST_WIDE_INT byte_offset + = wi::to_offset (byte_offset_cst).to_shwi (); + HOST_WIDE_INT bit_offset = byte_offset * BITS_PER_UNIT; + *out = bit_offset; + return true; + } + return false; +} + +/* class sized_region : public region. */ + +/* Implementation of region::accept vfunc for sized_region. */ + +void +sized_region::accept (visitor *v) const +{ + region::accept (v); + m_byte_size_sval->accept (v); +} + +/* Implementation of region::dump_to_pp vfunc for sized_region. */ + +void +sized_region::dump_to_pp (pretty_printer *pp, bool simple) const +{ + if (simple) + { + pp_string (pp, "SIZED_REG("); + get_parent_region ()->dump_to_pp (pp, simple); + pp_string (pp, ", "); + m_byte_size_sval->dump_to_pp (pp, simple); + pp_string (pp, ")"); + } + else + { + pp_string (pp, "sized_region("); + get_parent_region ()->dump_to_pp (pp, simple); + pp_string (pp, ", "); + m_byte_size_sval->dump_to_pp (pp, simple); + pp_printf (pp, ")"); + } +} + +/* Implementation of region::get_byte_size vfunc for sized_region. */ + +bool +sized_region::get_byte_size (byte_size_t *out) const +{ + if (tree cst = m_byte_size_sval->maybe_get_constant ()) + { + gcc_assert (TREE_CODE (cst) == INTEGER_CST); + *out = tree_to_uhwi (cst); + return true; + } + return false; +} + +/* Implementation of region::get_bit_size vfunc for sized_region. */ + +bool +sized_region::get_bit_size (bit_size_t *out) const +{ + byte_size_t byte_size; + if (!get_byte_size (&byte_size)) + return false; + *out = byte_size * BITS_PER_UNIT; + return true; +} + /* class cast_region : public region. */ /* Implementation of region::accept vfunc for cast_region. */ diff --git a/gcc/analyzer/region.h b/gcc/analyzer/region.h index 175a82a..353d5c4 100644 --- a/gcc/analyzer/region.h +++ b/gcc/analyzer/region.h @@ -43,6 +43,7 @@ enum region_kind RK_FIELD, RK_ELEMENT, RK_OFFSET, + RK_SIZED, RK_CAST, RK_HEAP_ALLOCATED, RK_ALLOCA, @@ -70,6 +71,7 @@ enum region_kind field_region (RK_FIELD) element_region (RK_ELEMENT) offset_region (RK_OFFSET) + sized_region (RK_SIZED) cast_region (RK_CAST) heap_allocated_region (RK_HEAP_ALLOCATED) alloca_region (RK_ALLOCA) @@ -107,6 +109,8 @@ public: dyn_cast_element_region () const { return NULL; } virtual const offset_region * dyn_cast_offset_region () const { return NULL; } + virtual const sized_region * + dyn_cast_sized_region () const { return NULL; } virtual const cast_region * dyn_cast_cast_region () const { return NULL; } virtual const string_region * @@ -138,8 +142,25 @@ public: static int cmp_ptr_ptr (const void *, const void *); region_offset get_offset () const; - bool get_byte_size (byte_size_t *out) const; - bool get_bit_size (bit_size_t *out) const; + + /* Attempt to get the size of this region as a concrete number of bytes. + If successful, return true and write the size to *OUT. + Otherwise return false. */ + virtual bool get_byte_size (byte_size_t *out) const; + + /* Attempt to get the size of this region as a concrete number of bits. + If successful, return true and write the size to *OUT. + Otherwise return false. */ + virtual bool get_bit_size (bit_size_t *out) const; + + /* Get a symbolic value describing the size of this region in bytes + (which could be "unknown"). */ + virtual const svalue *get_byte_size_sval (region_model_manager *mgr) const; + + /* Attempt to get the offset in bits of this region relative to its parent. + If successful, return true and write to *OUT. + Otherwise return false. */ + virtual bool get_relative_concrete_offset (bit_offset_t *out) const; void get_subregions_for_binding (region_model_manager *mgr, @@ -670,6 +691,8 @@ public: tree get_field () const { return m_field; } + bool get_relative_concrete_offset (bit_offset_t *out) const FINAL OVERRIDE; + private: tree m_field; }; @@ -751,6 +774,9 @@ public: const svalue *get_index () const { return m_index; } + virtual bool + get_relative_concrete_offset (bit_offset_t *out) const FINAL OVERRIDE; + private: const svalue *m_index; }; @@ -833,6 +859,8 @@ public: const svalue *get_byte_offset () const { return m_byte_offset; } + bool get_relative_concrete_offset (bit_offset_t *out) const FINAL OVERRIDE; + private: const svalue *m_byte_offset; }; @@ -855,6 +883,99 @@ template <> struct default_hash_traits<offset_region::key_t> namespace ana { +/* A region that is size BYTES_SIZE_SVAL in size within its parent + region (or possibly larger, which would lead to an overflow. */ + +class sized_region : public region +{ +public: + /* A support class for uniquifying instances of sized_region. */ + struct key_t + { + key_t (const region *parent, tree element_type, + const svalue *byte_size_sval) + : m_parent (parent), m_element_type (element_type), + m_byte_size_sval (byte_size_sval) + { + gcc_assert (byte_size_sval); + } + + hashval_t hash () const + { + inchash::hash hstate; + hstate.add_ptr (m_parent); + hstate.add_ptr (m_element_type); + hstate.add_ptr (m_byte_size_sval); + return hstate.end (); + } + + bool operator== (const key_t &other) const + { + return (m_parent == other.m_parent + && m_element_type == other.m_element_type + && m_byte_size_sval == other.m_byte_size_sval); + } + + void mark_deleted () { m_byte_size_sval = reinterpret_cast<const svalue *> (1); } + void mark_empty () { m_byte_size_sval = NULL; } + bool is_deleted () const + { + return m_byte_size_sval == reinterpret_cast<const svalue *> (1); + } + bool is_empty () const { return m_byte_size_sval == NULL; } + + const region *m_parent; + tree m_element_type; + const svalue *m_byte_size_sval; + const svalue *m_end_offset; + }; + + sized_region (unsigned id, const region *parent, tree type, + const svalue *byte_size_sval) + : region (complexity::from_pair (parent, byte_size_sval), + id, parent, type), + m_byte_size_sval (byte_size_sval) + {} + + enum region_kind get_kind () const FINAL OVERRIDE { return RK_SIZED; } + const sized_region * + dyn_cast_sized_region () const FINAL OVERRIDE { return this; } + + void accept (visitor *v) const FINAL OVERRIDE; + + void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE; + + bool get_byte_size (byte_size_t *out) const FINAL OVERRIDE; + bool get_bit_size (bit_size_t *out) const FINAL OVERRIDE; + + const svalue * + get_byte_size_sval (region_model_manager *) const FINAL OVERRIDE + { + return m_byte_size_sval; + } + +private: + const svalue *m_byte_size_sval; +}; + +} // namespace ana + +template <> +template <> +inline bool +is_a_helper <const sized_region *>::test (const region *reg) +{ + return reg->get_kind () == RK_SIZED; +} + +template <> struct default_hash_traits<sized_region::key_t> +: public member_function_hash_traits<sized_region::key_t> +{ + static const bool empty_zero_p = true; +}; + +namespace ana { + /* A region that views another region using a different type. */ class cast_region : public region diff --git a/gcc/analyzer/store.cc b/gcc/analyzer/store.cc index d5f8798..a65c741 100644 --- a/gcc/analyzer/store.cc +++ b/gcc/analyzer/store.cc @@ -118,52 +118,25 @@ uncertainty_t::dump (bool simple) const pp_flush (&pp); } -/* Get a human-readable string for KIND for dumps. */ - -const char *binding_kind_to_string (enum binding_kind kind) -{ - switch (kind) - { - default: - case BK_empty: - case BK_deleted: - /* We shouldn't be attempting to print the hash kinds. */ - gcc_unreachable (); - case BK_direct: - return "direct"; - case BK_default: - return "default"; - } -} - /* class binding_key. */ const binding_key * -binding_key::make (store_manager *mgr, const region *r, - enum binding_kind kind) +binding_key::make (store_manager *mgr, const region *r) { region_offset offset = r->get_offset (); if (offset.symbolic_p ()) - return mgr->get_symbolic_binding (r, kind); + return mgr->get_symbolic_binding (r); else { bit_size_t bit_size; if (r->get_bit_size (&bit_size)) return mgr->get_concrete_binding (offset.get_bit_offset (), - bit_size, kind); + bit_size); else - return mgr->get_symbolic_binding (r, kind); + return mgr->get_symbolic_binding (r); } } -/* Base class implementation of binding_key::dump_to_pp vfunc. */ - -void -binding_key::dump_to_pp (pretty_printer *pp, bool /*simple*/) const -{ - pp_printf (pp, "kind: %s", binding_kind_to_string (m_kind)); -} - /* Dump this binding_key to stderr. */ DEBUG_FUNCTION void @@ -204,11 +177,6 @@ binding_key::cmp_ptrs (const void *p1, const void *p2) int binding_key::cmp (const binding_key *k1, const binding_key *k2) { - enum binding_kind kind1 = k1->get_kind (); - enum binding_kind kind2 = k2->get_kind (); - if (kind1 != kind2) - return (int)kind1 - (int)kind2; - int concrete1 = k1->concrete_p (); int concrete2 = k2->concrete_p (); if (int concrete_cmp = concrete1 - concrete2) @@ -236,7 +204,7 @@ binding_key::cmp (const binding_key *k1, const binding_key *k2) } } -/* struct struct bit_range. */ +/* struct bit_range. */ void bit_range::dump_to_pp (pretty_printer *pp) const @@ -267,6 +235,24 @@ bit_range::dump () const pp_flush (&pp); } +/* If OTHER is a subset of this, return true and write + to *OUT the relative range of OTHER within this. + Otherwise return false. */ + +bool +bit_range::contains_p (const bit_range &other, bit_range *out) const +{ + if (contains_p (other.get_start_bit_offset ()) + && contains_p (other.get_last_bit_offset ())) + { + out->m_start_bit_offset = other.m_start_bit_offset - m_start_bit_offset; + out->m_size_in_bits = other.m_size_in_bits; + return true; + } + else + return false; +} + int bit_range::cmp (const bit_range &br1, const bit_range &br2) { @@ -371,15 +357,57 @@ byte_range::dump_to_pp (pretty_printer *pp) const } } +/* Dump this object to stderr. */ + +DEBUG_FUNCTION void +byte_range::dump () const +{ + pretty_printer pp; + pp.buffer->stream = stderr; + dump_to_pp (&pp); + pp_newline (&pp); + pp_flush (&pp); +} + +/* If OTHER is a subset of this, return true and write + to *OUT the relative range of OTHER within this. + Otherwise return false. */ + +bool +byte_range::contains_p (const byte_range &other, byte_range *out) const +{ + if (contains_p (other.get_start_byte_offset ()) + && contains_p (other.get_last_byte_offset ())) + { + out->m_start_byte_offset = other.m_start_byte_offset - m_start_byte_offset; + out->m_size_in_bytes = other.m_size_in_bytes; + return true; + } + else + return false; +} + +/* qsort comparator for byte ranges. */ + +int +byte_range::cmp (const byte_range &br1, const byte_range &br2) +{ + /* Order first by offset. */ + if (int start_cmp = wi::cmps (br1.m_start_byte_offset, + br2.m_start_byte_offset)) + return start_cmp; + + /* ...then by size. */ + return wi::cmpu (br1.m_size_in_bytes, br2.m_size_in_bytes); +} + /* class concrete_binding : public binding_key. */ /* Implementation of binding_key::dump_to_pp vfunc for concrete_binding. */ void -concrete_binding::dump_to_pp (pretty_printer *pp, bool simple) const +concrete_binding::dump_to_pp (pretty_printer *pp, bool) const { - binding_key::dump_to_pp (pp, simple); - pp_string (pp, ", "); m_bit_range.dump_to_pp (pp); } @@ -402,9 +430,6 @@ concrete_binding::cmp_ptr_ptr (const void *p1, const void *p2) const concrete_binding *b1 = *(const concrete_binding * const *)p1; const concrete_binding *b2 = *(const concrete_binding * const *)p2; - if (int kind_cmp = b1->get_kind () - b2->get_kind ()) - return kind_cmp; - return bit_range::cmp (b1->m_bit_range, b2->m_bit_range); } @@ -413,8 +438,8 @@ concrete_binding::cmp_ptr_ptr (const void *p1, const void *p2) void symbolic_binding::dump_to_pp (pretty_printer *pp, bool simple) const { - binding_key::dump_to_pp (pp, simple); - pp_string (pp, ", region: "); + //binding_key::dump_to_pp (pp, simple); + pp_string (pp, "region: "); m_region->dump_to_pp (pp, simple); } @@ -426,9 +451,6 @@ symbolic_binding::cmp_ptr_ptr (const void *p1, const void *p2) const symbolic_binding *b1 = *(const symbolic_binding * const *)p1; const symbolic_binding *b2 = *(const symbolic_binding * const *)p2; - if (int kind_cmp = b1->get_kind () - b2->get_kind ()) - return kind_cmp; - return region::cmp_ids (b1->get_region (), b2->get_region ()); } @@ -777,8 +799,7 @@ binding_map::apply_ctor_val_to_range (const region *parent_reg, return false; bit_offset_t start_bit_offset = min_offset.get_bit_offset (); store_manager *smgr = mgr->get_store_manager (); - const binding_key *max_element_key - = binding_key::make (smgr, max_element, BK_direct); + const binding_key *max_element_key = binding_key::make (smgr, max_element); if (max_element_key->symbolic_p ()) return false; const concrete_binding *max_element_ckey @@ -786,8 +807,7 @@ binding_map::apply_ctor_val_to_range (const region *parent_reg, bit_size_t range_size_in_bits = max_element_ckey->get_next_bit_offset () - start_bit_offset; const concrete_binding *range_key - = smgr->get_concrete_binding (start_bit_offset, range_size_in_bits, - BK_direct); + = smgr->get_concrete_binding (start_bit_offset, range_size_in_bits); if (range_key->symbolic_p ()) return false; @@ -819,8 +839,7 @@ binding_map::apply_ctor_pair_to_child_region (const region *parent_reg, { const svalue *sval = get_svalue_for_ctor_val (val, mgr); const binding_key *k - = binding_key::make (mgr->get_store_manager (), child_reg, - BK_direct); + = binding_key::make (mgr->get_store_manager (), child_reg); /* Handle the case where we have an unknown size for child_reg (e.g. due to it being a trailing field with incomplete array type. */ @@ -844,7 +863,7 @@ binding_map::apply_ctor_pair_to_child_region (const region *parent_reg, - parent_base_offset.get_bit_offset ()); /* Create a concrete key for the child within the parent. */ k = mgr->get_store_manager ()->get_concrete_binding - (child_parent_offset, sval_bit_size, BK_direct); + (child_parent_offset, sval_bit_size); } gcc_assert (k->concrete_p ()); put (k, sval); @@ -852,6 +871,166 @@ binding_map::apply_ctor_pair_to_child_region (const region *parent_reg, } } +/* Populate OUT with all bindings within this map that overlap KEY. */ + +void +binding_map::get_overlapping_bindings (const binding_key *key, + auto_vec<const binding_key *> *out) +{ + for (auto iter : *this) + { + const binding_key *iter_key = iter.first; + if (const concrete_binding *ckey + = key->dyn_cast_concrete_binding ()) + { + if (const concrete_binding *iter_ckey + = iter_key->dyn_cast_concrete_binding ()) + { + if (ckey->overlaps_p (*iter_ckey)) + out->safe_push (iter_key); + } + else + { + /* Assume overlap. */ + out->safe_push (iter_key); + } + } + else + { + /* Assume overlap. */ + out->safe_push (iter_key); + } + } +} + +/* Remove, truncate, and/or split any bindings within this map that + overlap DROP_KEY. + + For example, if we have: + + +------------------------------------+ + | old binding | + +------------------------------------+ + + which is to be overwritten with: + + .......+----------------------+....... + .......| new binding |....... + .......+----------------------+....... + + this function "cuts a hole" out of the old binding: + + +------+......................+------+ + |prefix| hole for new binding |suffix| + +------+......................+------+ + + into which the new binding can be added without + overlapping the prefix or suffix. + + The prefix and suffix (if added) will be bound to the pertinent + parts of the value of the old binding. + + For example, given: + struct s5 + { + char arr[8]; + }; + void test_5 (struct s5 *p) + { + struct s5 f = *p; + f.arr[3] = 42; + } + then after the "f = *p;" we have: + cluster for: f: INIT_VAL((*INIT_VAL(p_33(D)))) + and at the "f.arr[3] = 42;" we remove the bindings overlapping + "f.arr[3]", replacing it with a prefix (bytes 0-2) and suffix (bytes 4-7) + giving: + cluster for: f + key: {bytes 0-2} + value: {BITS_WITHIN(bytes 0-2, inner_val: INIT_VAL((*INIT_VAL(p_33(D))).arr))} + key: {bytes 4-7} + value: {BITS_WITHIN(bytes 4-7, inner_val: INIT_VAL((*INIT_VAL(p_33(D))).arr))} + punching a hole into which the new value can be written at byte 3: + cluster for: f + key: {bytes 0-2} + value: {BITS_WITHIN(bytes 0-2, inner_val: INIT_VAL((*INIT_VAL(p_33(D))).arr))} + key: {byte 3} + value: 'char' {(char)42} + key: {bytes 4-7} + value: {BITS_WITHIN(bytes 4-7, inner_val: INIT_VAL((*INIT_VAL(p_33(D))).arr))} + + If UNCERTAINTY is non-NULL, use it to record any svalues that + were removed, as being maybe-bound. */ + +void +binding_map::remove_overlapping_bindings (store_manager *mgr, + const binding_key *drop_key, + uncertainty_t *uncertainty) +{ + auto_vec<const binding_key *> bindings; + get_overlapping_bindings (drop_key, &bindings); + + unsigned i; + const binding_key *iter_binding; + FOR_EACH_VEC_ELT (bindings, i, iter_binding) + { + const svalue *old_sval = get (iter_binding); + if (uncertainty) + uncertainty->on_maybe_bound_sval (old_sval); + + /* Begin by removing the old binding. */ + m_map.remove (iter_binding); + + /* Now potentially add the prefix and suffix. */ + if (const concrete_binding *drop_ckey + = drop_key->dyn_cast_concrete_binding ()) + if (const concrete_binding *iter_ckey + = iter_binding->dyn_cast_concrete_binding ()) + { + gcc_assert (drop_ckey->overlaps_p (*iter_ckey)); + + const bit_range &drop_bits = drop_ckey->get_bit_range (); + const bit_range &iter_bits = iter_ckey->get_bit_range (); + + if (iter_bits.get_start_bit_offset () + < drop_bits.get_start_bit_offset ()) + { + /* We have a truncated prefix. */ + bit_range prefix_bits (iter_bits.get_start_bit_offset (), + (drop_bits.get_start_bit_offset () + - iter_bits.get_start_bit_offset ())); + const concrete_binding *prefix_key + = mgr->get_concrete_binding (prefix_bits); + bit_range rel_prefix (0, prefix_bits.m_size_in_bits); + const svalue *prefix_sval + = old_sval->extract_bit_range (NULL_TREE, + rel_prefix, + mgr->get_svalue_manager ()); + m_map.put (prefix_key, prefix_sval); + } + + if (iter_bits.get_next_bit_offset () + > drop_bits.get_next_bit_offset ()) + { + /* We have a truncated suffix. */ + bit_range suffix_bits (drop_bits.get_next_bit_offset (), + (iter_bits.get_next_bit_offset () + - drop_bits.get_next_bit_offset ())); + const concrete_binding *suffix_key + = mgr->get_concrete_binding (suffix_bits); + bit_range rel_suffix (drop_bits.get_next_bit_offset () + - iter_bits.get_start_bit_offset (), + suffix_bits.m_size_in_bits); + const svalue *suffix_sval + = old_sval->extract_bit_range (NULL_TREE, + rel_suffix, + mgr->get_svalue_manager ()); + m_map.put (suffix_key, suffix_sval); + } + } + } +} + /* class binding_cluster. */ /* binding_cluster's copy ctor. */ @@ -964,6 +1143,27 @@ binding_cluster::dump (bool simple) const pp_flush (&pp); } +/* Assert that this object is valid. */ + +void +binding_cluster::validate () const +{ + int num_symbolic = 0; + int num_concrete = 0; + for (auto iter : m_map) + { + if (iter.first->symbolic_p ()) + num_symbolic++; + else + num_concrete++; + } + /* We shouldn't have more than one symbolic key per cluster + (or one would have clobbered the other). */ + gcc_assert (num_symbolic < 2); + /* We can't have both concrete and symbolic keys. */ + gcc_assert (num_concrete == 0 || num_symbolic == 0); +} + /* Return a new json::object of the form {"escaped": true/false, "touched": true/false, @@ -986,8 +1186,7 @@ binding_cluster::to_json () const void binding_cluster::bind (store_manager *mgr, - const region *reg, const svalue *sval, - binding_kind kind) + const region *reg, const svalue *sval) { if (const compound_svalue *compound_sval = sval->dyn_cast_compound_svalue ()) @@ -996,7 +1195,7 @@ binding_cluster::bind (store_manager *mgr, return; } - const binding_key *binding = binding_key::make (mgr, reg, kind); + const binding_key *binding = binding_key::make (mgr, reg); bind_key (binding, sval); } @@ -1045,8 +1244,7 @@ binding_cluster::bind_compound_sval (store_manager *mgr, + reg_offset.get_bit_offset ()); const concrete_binding *effective_concrete_key = mgr->get_concrete_binding (effective_start, - concrete_key->get_size_in_bits (), - iter_key->get_kind ()); + concrete_key->get_size_in_bits ()); bind_key (effective_concrete_key, iter_sval); } else @@ -1069,31 +1267,35 @@ binding_cluster::purge_region (store_manager *mgr, const region *reg) { gcc_assert (reg->get_kind () == RK_DECL); const binding_key *binding - = binding_key::make (mgr, const_cast<region *> (reg), - BK_direct); + = binding_key::make (mgr, const_cast<region *> (reg)); m_map.remove (binding); } -/* Mark REG within this cluster as being filled with zeroes. - Remove all bindings, add a default binding to zero, and clear the - TOUCHED flag. */ +/* Clobber REG and fill it with repeated copies of SVAL. */ void -binding_cluster::zero_fill_region (store_manager *mgr, const region *reg) +binding_cluster::fill_region (store_manager *mgr, + const region *reg, + const svalue *sval) { clobber_region (mgr, reg); - /* Add a default binding to zero. */ region_model_manager *sval_mgr = mgr->get_svalue_manager (); - const svalue *cst_sval - = sval_mgr->get_or_create_int_cst (integer_type_node, 0); - const svalue *bound_sval = cst_sval; - if (reg->get_type ()) - bound_sval = sval_mgr->get_or_create_unaryop (reg->get_type (), NOP_EXPR, - cst_sval); - bind (mgr, reg, bound_sval, BK_default); + const svalue *byte_size_sval = reg->get_byte_size_sval (sval_mgr); + const svalue *fill_sval + = sval_mgr->get_or_create_repeated_svalue (reg->get_type (), + byte_size_sval, sval); + bind (mgr, reg, fill_sval); +} + +/* Clobber REG within this cluster and fill it with zeroes. */ - m_touched = false; +void +binding_cluster::zero_fill_region (store_manager *mgr, const region *reg) +{ + region_model_manager *sval_mgr = mgr->get_svalue_manager (); + const svalue *zero_sval = sval_mgr->get_or_create_int_cst (char_type_node, 0); + fill_region (mgr, reg, zero_sval); } /* Mark REG within this cluster as being unknown. @@ -1111,7 +1313,7 @@ binding_cluster::mark_region_as_unknown (store_manager *mgr, region_model_manager *sval_mgr = mgr->get_svalue_manager (); const svalue *sval = sval_mgr->get_or_create_unknown_svalue (reg->get_type ()); - bind (mgr, reg, sval, BK_default); + bind (mgr, reg, sval); } /* Get any SVAL bound to REG within this cluster via kind KIND, @@ -1119,10 +1321,9 @@ binding_cluster::mark_region_as_unknown (store_manager *mgr, const svalue * binding_cluster::get_binding (store_manager *mgr, - const region *reg, - binding_kind kind) const + const region *reg) const { - const binding_key *reg_binding = binding_key::make (mgr, reg, kind); + const binding_key *reg_binding = binding_key::make (mgr, reg); const svalue *sval = m_map.get (reg_binding); if (sval) { @@ -1140,7 +1341,7 @@ binding_cluster::get_binding (store_manager *mgr, while (const region *parent_reg = reg->get_parent_region ()) { const binding_key *parent_reg_binding - = binding_key::make (mgr, parent_reg, kind); + = binding_key::make (mgr, parent_reg); if (parent_reg_binding == reg_binding && sval->get_type () && reg->get_type () @@ -1161,7 +1362,7 @@ binding_cluster::get_binding (store_manager *mgr, FOR_EACH_VEC_ELT_REVERSE (regions, i, iter_reg) { region_model_manager *rmm_mgr = mgr->get_svalue_manager (); - sval = rmm_mgr->get_or_create_sub_svalue (reg->get_type (), + sval = rmm_mgr->get_or_create_sub_svalue (iter_reg->get_type (), sval, iter_reg); } } @@ -1169,21 +1370,20 @@ binding_cluster::get_binding (store_manager *mgr, return sval; } -/* Get any SVAL bound to REG within this cluster via kind KIND, +/* Get any SVAL bound to REG within this cluster, either directly for REG, or recursively checking for bindings within parent regions and extracting subvalues if need be. */ const svalue * binding_cluster::get_binding_recursive (store_manager *mgr, - const region *reg, - enum binding_kind kind) const + const region *reg) const { - if (const svalue *sval = get_binding (mgr, reg, kind)) + if (const svalue *sval = get_binding (mgr, reg)) return sval; if (reg != m_base_region) if (const region *parent_reg = reg->get_parent_region ()) if (const svalue *parent_sval - = get_binding_recursive (mgr, parent_reg, kind)) + = get_binding_recursive (mgr, parent_reg)) { /* Extract child svalue from parent svalue. */ region_model_manager *rmm_mgr = mgr->get_svalue_manager (); @@ -1199,18 +1399,11 @@ const svalue * binding_cluster::get_any_binding (store_manager *mgr, const region *reg) const { - /* Look for a "direct" binding. */ + /* Look for a direct binding. */ if (const svalue *direct_sval - = get_binding_recursive (mgr, reg, BK_direct)) + = get_binding_recursive (mgr, reg)) return direct_sval; - /* Look for a "default" binding, but not if there's been a symbolic - write. */ - if (!m_touched) - if (const svalue *default_sval - = get_binding_recursive (mgr, reg, BK_default)) - return default_sval; - /* If this cluster has been touched by a symbolic write, then the content of any subregion not currently specifically bound is "UNKNOWN". */ if (m_touched) @@ -1251,8 +1444,6 @@ const svalue * binding_cluster::maybe_get_compound_binding (store_manager *mgr, const region *reg) const { - binding_map map; - region_offset cluster_offset = m_base_region->get_offset (); if (cluster_offset.symbolic_p ()) return NULL; @@ -1260,6 +1451,36 @@ binding_cluster::maybe_get_compound_binding (store_manager *mgr, if (reg_offset.symbolic_p ()) return NULL; + region_model_manager *sval_mgr = mgr->get_svalue_manager (); + + /* We will a build the result map in two parts: + (a) result_map, holding the concrete keys from this cluster, + + (b) default_map, holding the initial values for the region + (e.g. uninitialized, initializer values, or zero), unless this + cluster has been touched. + + We will populate (a), and as we do, clobber (b), trimming and + splitting its bindings as necessary. + Finally, we will merge (b) into (a), giving a concrete map + that merges both the initial values and the bound values from + the binding_cluster. + Doing it this way reduces N for the O(N^2) intersection-finding, + perhaps we should have a spatial-organized data structure for + concrete keys, though. */ + + binding_map result_map; + binding_map default_map; + + /* Set up default values in default_map. */ + const svalue *default_sval; + if (m_touched) + default_sval = sval_mgr->get_or_create_unknown_svalue (reg->get_type ()); + else + default_sval = sval_mgr->get_or_create_initial_value (reg); + const binding_key *default_key = binding_key::make (mgr, reg); + default_map.put (default_key, default_sval); + for (map_t::iterator iter = m_map.begin (); iter != m_map.end (); ++iter) { const binding_key *key = (*iter).first; @@ -1268,78 +1489,77 @@ binding_cluster::maybe_get_compound_binding (store_manager *mgr, if (const concrete_binding *concrete_key = key->dyn_cast_concrete_binding ()) { - /* Skip bindings that are outside the bit range of REG. */ - if (concrete_key->get_start_bit_offset () - < reg_offset.get_bit_offset ()) - continue; - bit_size_t reg_bit_size; - if (reg->get_bit_size (®_bit_size)) - if (concrete_key->get_start_bit_offset () - >= reg_offset.get_bit_offset () + reg_bit_size) - continue; - - /* Get offset of KEY relative to REG, rather than to - the cluster. */ - bit_offset_t relative_start - = (concrete_key->get_start_bit_offset () - - reg_offset.get_bit_offset ()); - const concrete_binding *offset_concrete_key - = mgr->get_concrete_binding (relative_start, - concrete_key->get_size_in_bits (), - key->get_kind ()); - map.put (offset_concrete_key, sval); - } - else - return NULL; - } + const bit_range &bound_range = concrete_key->get_bit_range (); - if (map.elements () == 0) - return NULL; + bit_size_t reg_bit_size; + if (!reg->get_bit_size (®_bit_size)) + return NULL; - region_model_manager *sval_mgr = mgr->get_svalue_manager (); - return sval_mgr->get_or_create_compound_svalue (reg->get_type (), map); -} + bit_range reg_range (reg_offset.get_bit_offset (), + reg_bit_size); + /* Skip bindings that are outside the bit range of REG. */ + if (!bound_range.intersects_p (reg_range)) + continue; -/* Populate OUT with all bindings within this cluster that overlap REG. */ + /* We shouldn't have an exact match; that should have been + handled already. */ + gcc_assert (!(reg_range == bound_range)); -void -binding_cluster::get_overlapping_bindings (store_manager *mgr, - const region *reg, - auto_vec<const binding_key *> *out) -{ - const binding_key *binding - = binding_key::make (mgr, reg, BK_direct); - for (map_t::iterator iter = m_map.begin (); - iter != m_map.end (); ++iter) - { - const binding_key *iter_key = (*iter).first; - if (const concrete_binding *ckey - = binding->dyn_cast_concrete_binding ()) - { - if (const concrete_binding *iter_ckey - = iter_key->dyn_cast_concrete_binding ()) + bit_range subrange (0, 0); + if (reg_range.contains_p (bound_range, &subrange)) { - if (ckey->overlaps_p (*iter_ckey)) - out->safe_push (iter_key); + /* We have a bound range fully within REG. + Add it to map, offsetting accordingly. */ + + /* Get offset of KEY relative to REG, rather than to + the cluster. */ + const concrete_binding *offset_concrete_key + = mgr->get_concrete_binding (subrange); + result_map.put (offset_concrete_key, sval); + + /* Clobber default_map, removing/trimming/spliting where + it overlaps with offset_concrete_key. */ + default_map.remove_overlapping_bindings (mgr, + offset_concrete_key, + NULL); + } + else if (bound_range.contains_p (reg_range, &subrange)) + { + /* REG is fully within the bound range, but + is not equal to it; we're extracting a subvalue. */ + return sval->extract_bit_range (reg->get_type (), + subrange, + mgr->get_svalue_manager ()); } else { - /* Assume overlap. */ - out->safe_push (iter_key); + /* REG and the bound range partially overlap. + We don't handle this case yet. */ + return NULL; } } else - { - /* Assume overlap. */ - out->safe_push (iter_key); - } + /* Can't handle symbolic bindings. */ + return NULL; + } + + if (result_map.elements () == 0) + return NULL; + + /* Merge any bindings from default_map into result_map. */ + for (auto iter : default_map) + { + const binding_key *key = iter.first; + const svalue *sval = iter.second; + result_map.put (key, sval); } + + return sval_mgr->get_or_create_compound_svalue (reg->get_type (), result_map); } -/* Remove any bindings within this cluster that overlap REG, - but retain default bindings that overlap but aren't fully covered - by REG. +/* Remove, truncate, and/or split any bindings within this map that + overlap REG. If UNCERTAINTY is non-NULL, use it to record any svalues that were removed, as being maybe-bound. */ @@ -1348,26 +1568,9 @@ binding_cluster::remove_overlapping_bindings (store_manager *mgr, const region *reg, uncertainty_t *uncertainty) { - auto_vec<const binding_key *> bindings; - get_overlapping_bindings (mgr, reg, &bindings); + const binding_key *reg_binding = binding_key::make (mgr, reg); - unsigned i; - const binding_key *iter_binding; - FOR_EACH_VEC_ELT (bindings, i, iter_binding) - { - /* Don't remove default bindings, unless the default binding - is fully covered by REG. */ - if (iter_binding->get_kind () == BK_default) - { - const binding_key *reg_binding - = binding_key::make (mgr, reg, BK_default); - if (reg_binding != iter_binding) - continue; - } - if (uncertainty) - uncertainty->on_maybe_bound_sval (m_map.get (iter_binding)); - m_map.remove (iter_binding); - } + m_map.remove_overlapping_bindings (mgr, reg_binding, uncertainty); } /* Attempt to merge CLUSTER_A and CLUSTER_B into OUT_CLUSTER, using @@ -1428,6 +1631,8 @@ binding_cluster::can_merge_p (const binding_cluster *cluster_a, const binding_key *key_b = (*iter_b).first; keys.add (key_b); } + int num_symbolic_keys = 0; + int num_concrete_keys = 0; for (hash_set<const binding_key *>::iterator iter = keys.begin (); iter != keys.end (); ++iter) { @@ -1435,6 +1640,11 @@ binding_cluster::can_merge_p (const binding_cluster *cluster_a, const svalue *sval_a = cluster_a->get_any_value (key); const svalue *sval_b = cluster_b->get_any_value (key); + if (key->symbolic_p ()) + num_symbolic_keys++; + else + num_concrete_keys++; + if (sval_a == sval_b) { gcc_assert (sval_a); @@ -1463,29 +1673,15 @@ binding_cluster::can_merge_p (const binding_cluster *cluster_a, out_cluster->m_map.put (key, unknown_sval); } - /* Handle the case where we get a default binding from one and a direct - binding from the other. */ - auto_vec<const concrete_binding *> duplicate_keys; - for (map_t::iterator iter = out_cluster->m_map.begin (); - iter != out_cluster->m_map.end (); ++iter) - { - const concrete_binding *ckey - = (*iter).first->dyn_cast_concrete_binding (); - if (!ckey) - continue; - if (ckey->get_kind () != BK_direct) - continue; - const concrete_binding *def_ckey - = mgr->get_concrete_binding (ckey->get_start_bit_offset (), - ckey->get_size_in_bits (), - BK_default); - if (out_cluster->m_map.get (def_ckey)) - duplicate_keys.safe_push (def_ckey); + /* We can only have at most one symbolic key per cluster, + and if we do, we can't have any concrete keys. + If this happens, mark the cluster as touched, with no keys. */ + if (num_symbolic_keys >= 2 + || (num_concrete_keys > 0 && num_symbolic_keys > 0)) + { + out_cluster->m_touched = true; + out_cluster->m_map.empty (); } - unsigned i; - const concrete_binding *key; - FOR_EACH_VEC_ELT (duplicate_keys, i, key) - out_cluster->m_map.remove (key); /* We don't handle other kinds of overlaps yet. */ @@ -1558,7 +1754,7 @@ binding_cluster::on_unknown_fncall (const gcall *call, const svalue *sval = mgr->get_svalue_manager ()->get_or_create_conjured_svalue (m_base_region->get_type (), call, m_base_region); - bind (mgr, m_base_region, sval, BK_direct); + bind (mgr, m_base_region, sval); m_touched = true; } @@ -1665,7 +1861,7 @@ binding_cluster::maybe_get_simple_value (store_manager *mgr) const if (m_map.elements () != 1) return NULL; - const binding_key *key = binding_key::make (mgr, m_base_region, BK_direct); + const binding_key *key = binding_key::make (mgr, m_base_region); return get_any_value (key); } @@ -1675,10 +1871,9 @@ binding_cluster::maybe_get_simple_value (store_manager *mgr) const const concrete_binding * store_manager::get_concrete_binding (bit_offset_t start_bit_offset, - bit_offset_t size_in_bits, - enum binding_kind kind) + bit_offset_t size_in_bits) { - concrete_binding b (start_bit_offset, size_in_bits, kind); + concrete_binding b (start_bit_offset, size_in_bits); if (concrete_binding *existing = m_concrete_binding_key_mgr.get (b)) return existing; @@ -1688,10 +1883,9 @@ store_manager::get_concrete_binding (bit_offset_t start_bit_offset, } const symbolic_binding * -store_manager::get_symbolic_binding (const region *reg, - enum binding_kind kind) +store_manager::get_symbolic_binding (const region *reg) { - symbolic_binding b (reg, kind); + symbolic_binding b (reg); if (symbolic_binding *existing = m_symbolic_binding_key_mgr.get (b)) return existing; @@ -1952,6 +2146,15 @@ store::dump (bool simple) const pp_flush (&pp); } +/* Assert that this object is valid. */ + +void +store::validate () const +{ + for (auto iter : m_cluster_map) + iter.second->validate (); +} + /* Return a new json::object of the form {PARENT_REGION_DESC: {BASE_REGION_DESC: object for binding_map, ... for each cluster within parent region}, @@ -2027,7 +2230,7 @@ store::get_any_binding (store_manager *mgr, const region *reg) const void store::set_value (store_manager *mgr, const region *lhs_reg, - const svalue *rhs_sval, enum binding_kind kind, + const svalue *rhs_sval, uncertainty_t *uncertainty) { remove_overlapping_bindings (mgr, lhs_reg); @@ -2054,7 +2257,7 @@ store::set_value (store_manager *mgr, const region *lhs_reg, else { lhs_cluster = get_or_create_cluster (lhs_base_reg); - lhs_cluster->bind (mgr, lhs_reg, rhs_sval, kind); + lhs_cluster->bind (mgr, lhs_reg, rhs_sval); } /* Bindings to a cluster can affect other clusters if a symbolic @@ -2209,16 +2412,26 @@ store::purge_region (store_manager *mgr, const region *reg) } } -/* Zero-fill REG. */ +/* Fill REG with SVAL. */ void -store::zero_fill_region (store_manager *mgr, const region *reg) +store::fill_region (store_manager *mgr, const region *reg, const svalue *sval) { const region *base_reg = reg->get_base_region (); if (base_reg->symbolic_for_unknown_ptr_p ()) return; binding_cluster *cluster = get_or_create_cluster (base_reg); - cluster->zero_fill_region (mgr, reg); + cluster->fill_region (mgr, reg, sval); +} + +/* Zero-fill REG. */ + +void +store::zero_fill_region (store_manager *mgr, const region *reg) +{ + region_model_manager *sval_mgr = mgr->get_svalue_manager (); + const svalue *zero_sval = sval_mgr->get_or_create_int_cst (char_type_node, 0); + fill_region (mgr, reg, zero_sval); } /* Mark REG as having unknown content. */ @@ -2740,26 +2953,18 @@ test_binding_key_overlap () store_manager mgr (NULL); /* Various 8-bit bindings. */ - const concrete_binding *cb_0_7 - = mgr.get_concrete_binding (0, 8, BK_direct); - const concrete_binding *cb_8_15 - = mgr.get_concrete_binding (8, 8, BK_direct); - const concrete_binding *cb_16_23 - = mgr.get_concrete_binding (16, 8, BK_direct); - const concrete_binding *cb_24_31 - = mgr.get_concrete_binding (24, 8, BK_direct); + const concrete_binding *cb_0_7 = mgr.get_concrete_binding (0, 8); + const concrete_binding *cb_8_15 = mgr.get_concrete_binding (8, 8); + const concrete_binding *cb_16_23 = mgr.get_concrete_binding (16, 8); + const concrete_binding *cb_24_31 = mgr.get_concrete_binding (24, 8); /* 16-bit bindings. */ - const concrete_binding *cb_0_15 - = mgr.get_concrete_binding (0, 16, BK_direct); - const concrete_binding *cb_8_23 - = mgr.get_concrete_binding (8, 16, BK_direct); - const concrete_binding *cb_16_31 - = mgr.get_concrete_binding (16, 16, BK_direct); + const concrete_binding *cb_0_15 = mgr.get_concrete_binding (0, 16); + const concrete_binding *cb_8_23 = mgr.get_concrete_binding (8, 16); + const concrete_binding *cb_16_31 = mgr.get_concrete_binding (16, 16); /* 32-bit binding. */ - const concrete_binding *cb_0_31 - = mgr.get_concrete_binding (0, 32, BK_direct); + const concrete_binding *cb_0_31 = mgr.get_concrete_binding (0, 32); /* Everything should self-overlap. */ ASSERT_OVERLAP (cb_0_7, cb_0_7); diff --git a/gcc/analyzer/store.h b/gcc/analyzer/store.h index e0c60e1..2ac2923 100644 --- a/gcc/analyzer/store.h +++ b/gcc/analyzer/store.h @@ -199,29 +199,6 @@ private: class byte_range; class concrete_binding; -/* An enum for discriminating between "direct" vs "default" levels of - mapping. */ - -enum binding_kind -{ - /* Special-case value for hash support. - This is the initial entry, so that hash traits can have - empty_zero_p = true. */ - BK_empty = 0, - - /* Special-case value for hash support. */ - BK_deleted, - - /* The normal kind of mapping. */ - BK_direct, - - /* A lower-priority kind of mapping, for use when inheriting - default values from a parent region. */ - BK_default -}; - -extern const char *binding_kind_to_string (enum binding_kind kind); - /* Abstract base class for describing ranges of bits within a binding_map that can have svalues bound to them. */ @@ -232,10 +209,9 @@ public: virtual bool concrete_p () const = 0; bool symbolic_p () const { return !concrete_p (); } - static const binding_key *make (store_manager *mgr, const region *r, - enum binding_kind kind); + static const binding_key *make (store_manager *mgr, const region *r); - virtual void dump_to_pp (pretty_printer *pp, bool simple) const; + virtual void dump_to_pp (pretty_printer *pp, bool simple) const = 0; void dump (bool simple) const; label_text get_desc (bool simple=true) const; @@ -244,28 +220,6 @@ public: virtual const concrete_binding *dyn_cast_concrete_binding () const { return NULL; } - - enum binding_kind get_kind () const { return m_kind; } - - void mark_deleted () { m_kind = BK_deleted; } - void mark_empty () { m_kind = BK_empty; } - bool is_deleted () const { return m_kind == BK_deleted; } - bool is_empty () const { return m_kind == BK_empty; } - -protected: - binding_key (enum binding_kind kind) : m_kind (kind) {} - - hashval_t impl_hash () const - { - return m_kind; - } - bool impl_eq (const binding_key &other) const - { - return m_kind == other.m_kind; - } - -private: - enum binding_kind m_kind; }; /* A concrete range of bits. */ @@ -288,6 +242,10 @@ struct bit_range { return m_start_bit_offset + m_size_in_bits; } + bit_offset_t get_last_bit_offset () const + { + return get_next_bit_offset () - 1; + } bool contains_p (bit_offset_t offset) const { @@ -295,6 +253,8 @@ struct bit_range && offset < get_next_bit_offset ()); } + bool contains_p (const bit_range &other, bit_range *out) const; + bool operator== (const bit_range &other) const { return (m_start_bit_offset == other.m_start_bit_offset @@ -327,12 +287,42 @@ struct byte_range {} void dump_to_pp (pretty_printer *pp) const; + void dump () const; + + bool contains_p (byte_offset_t offset) const + { + return (offset >= get_start_byte_offset () + && offset < get_next_byte_offset ()); + } + bool contains_p (const byte_range &other, byte_range *out) const; + + bool operator== (const byte_range &other) const + { + return (m_start_byte_offset == other.m_start_byte_offset + && m_size_in_bytes == other.m_size_in_bytes); + } + byte_offset_t get_start_byte_offset () const + { + return m_start_byte_offset; + } + byte_offset_t get_next_byte_offset () const + { + return m_start_byte_offset + m_size_in_bytes; + } byte_offset_t get_last_byte_offset () const { return m_start_byte_offset + m_size_in_bytes - 1; } + bit_range as_bit_range () const + { + return bit_range (m_start_byte_offset * BITS_PER_UNIT, + m_size_in_bytes * BITS_PER_UNIT); + } + + static int cmp (const byte_range &br1, const byte_range &br2); + byte_offset_t m_start_byte_offset; byte_size_t m_size_in_bytes; }; @@ -346,10 +336,8 @@ public: /* This class is its own key for the purposes of consolidation. */ typedef concrete_binding key_t; - concrete_binding (bit_offset_t start_bit_offset, bit_size_t size_in_bits, - enum binding_kind kind) - : binding_key (kind), - m_bit_range (start_bit_offset, size_in_bits) + concrete_binding (bit_offset_t start_bit_offset, bit_size_t size_in_bits) + : m_bit_range (start_bit_offset, size_in_bits) {} bool concrete_p () const FINAL OVERRIDE { return true; } @@ -358,12 +346,10 @@ public: inchash::hash hstate; hstate.add_wide_int (m_bit_range.m_start_bit_offset); hstate.add_wide_int (m_bit_range.m_size_in_bits); - return hstate.end () ^ binding_key::impl_hash (); + return hstate.end (); } bool operator== (const concrete_binding &other) const { - if (!binding_key::impl_eq (other)) - return false; return m_bit_range == other.m_bit_range; } @@ -392,6 +378,11 @@ public: static int cmp_ptr_ptr (const void *, const void *); + void mark_deleted () { m_bit_range.m_start_bit_offset = -1; } + void mark_empty () { m_bit_range.m_start_bit_offset = -2; } + bool is_deleted () const { return m_bit_range.m_start_bit_offset == -1; } + bool is_empty () const { return m_bit_range.m_start_bit_offset == -2; } + private: bit_range m_bit_range; }; @@ -401,7 +392,7 @@ private: template <> struct default_hash_traits<ana::concrete_binding> : public member_function_hash_traits<ana::concrete_binding> { - static const bool empty_zero_p = true; + static const bool empty_zero_p = false; }; namespace ana { @@ -415,21 +406,16 @@ public: /* This class is its own key for the purposes of consolidation. */ typedef symbolic_binding key_t; - symbolic_binding (const region *region, enum binding_kind kind) - : binding_key (kind), - m_region (region) - {} + symbolic_binding (const region *region) : m_region (region) {} bool concrete_p () const FINAL OVERRIDE { return false; } hashval_t hash () const { - return (binding_key::impl_hash () ^ (intptr_t)m_region); + return (intptr_t)m_region; } bool operator== (const symbolic_binding &other) const { - if (!binding_key::impl_eq (other)) - return false; - return (m_region == other.m_region); + return m_region == other.m_region; } void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE; @@ -438,6 +424,12 @@ public: static int cmp_ptr_ptr (const void *, const void *); + void mark_deleted () { m_region = reinterpret_cast<const region *> (1); } + void mark_empty () { m_region = NULL; } + bool is_deleted () const + { return m_region == reinterpret_cast<const region *> (1); } + bool is_empty () const { return m_region == NULL; } + private: const region *m_region; }; @@ -504,7 +496,13 @@ public: static int cmp (const binding_map &map1, const binding_map &map2); + void remove_overlapping_bindings (store_manager *mgr, + const binding_key *drop_key, + uncertainty_t *uncertainty); + private: + void get_overlapping_bindings (const binding_key *key, + auto_vec<const binding_key *> *out); bool apply_ctor_val_to_range (const region *parent_reg, region_model_manager *mgr, tree min_index, tree max_index, @@ -553,22 +551,22 @@ public: void dump_to_pp (pretty_printer *pp, bool simple, bool multiline) const; void dump (bool simple) const; + void validate () const; + json::object *to_json () const; - void bind (store_manager *mgr, const region *, const svalue *, - binding_kind kind); + void bind (store_manager *mgr, const region *, const svalue *); void clobber_region (store_manager *mgr, const region *reg); void purge_region (store_manager *mgr, const region *reg); + void fill_region (store_manager *mgr, const region *reg, const svalue *sval); void zero_fill_region (store_manager *mgr, const region *reg); void mark_region_as_unknown (store_manager *mgr, const region *reg, uncertainty_t *uncertainty); - const svalue *get_binding (store_manager *mgr, const region *reg, - binding_kind kind) const; + const svalue *get_binding (store_manager *mgr, const region *reg) const; const svalue *get_binding_recursive (store_manager *mgr, - const region *reg, - enum binding_kind kind) const; + const region *reg) const; const svalue *get_any_binding (store_manager *mgr, const region *reg) const; const svalue *maybe_get_compound_binding (store_manager *mgr, @@ -630,8 +628,6 @@ public: private: const svalue *get_any_value (const binding_key *key) const; - void get_overlapping_bindings (store_manager *mgr, const region *reg, - auto_vec<const binding_key *> *out); void bind_compound_sval (store_manager *mgr, const region *reg, const compound_svalue *compound_sval); @@ -684,6 +680,8 @@ public: void dump (bool simple) const; void summarize_to_pp (pretty_printer *pp, bool simple) const; + void validate () const; + json::object *to_json () const; const svalue *get_any_binding (store_manager *mgr, const region *reg) const; @@ -691,10 +689,11 @@ public: bool called_unknown_fn_p () const { return m_called_unknown_fn; } void set_value (store_manager *mgr, const region *lhs_reg, - const svalue *rhs_sval, enum binding_kind kind, + const svalue *rhs_sval, uncertainty_t *uncertainty); void clobber_region (store_manager *mgr, const region *reg); void purge_region (store_manager *mgr, const region *reg); + void fill_region (store_manager *mgr, const region *reg, const svalue *sval); void zero_fill_region (store_manager *mgr, const region *reg); void mark_region_as_unknown (store_manager *mgr, const region *reg, uncertainty_t *uncertainty); @@ -773,19 +772,15 @@ public: /* binding consolidation. */ const concrete_binding * get_concrete_binding (bit_offset_t start_bit_offset, - bit_offset_t size_in_bits, - enum binding_kind kind); + bit_offset_t size_in_bits); const concrete_binding * - get_concrete_binding (const bit_range &bits, - enum binding_kind kind) + get_concrete_binding (const bit_range &bits) { return get_concrete_binding (bits.get_start_bit_offset (), - bits.m_size_in_bits, - kind); + bits.m_size_in_bits); } const symbolic_binding * - get_symbolic_binding (const region *region, - enum binding_kind kind); + get_symbolic_binding (const region *region); region_model_manager *get_svalue_manager () const { diff --git a/gcc/analyzer/svalue.cc b/gcc/analyzer/svalue.cc index a16563d..6c8afef 100644 --- a/gcc/analyzer/svalue.cc +++ b/gcc/analyzer/svalue.cc @@ -415,6 +415,27 @@ svalue::cmp_ptr (const svalue *sval1, const svalue *sval2) sub_sval2->get_subregion ()); } break; + case SK_REPEATED: + { + const repeated_svalue *repeated_sval1 = (const repeated_svalue *)sval1; + const repeated_svalue *repeated_sval2 = (const repeated_svalue *)sval2; + return svalue::cmp_ptr (repeated_sval1->get_inner_svalue (), + repeated_sval2->get_inner_svalue ()); + } + break; + case SK_BITS_WITHIN: + { + const bits_within_svalue *bits_within_sval1 + = (const bits_within_svalue *)sval1; + const bits_within_svalue *bits_within_sval2 + = (const bits_within_svalue *)sval2; + if (int cmp = bit_range::cmp (bits_within_sval1->get_bits (), + bits_within_sval2->get_bits ())) + return cmp; + return svalue::cmp_ptr (bits_within_sval1->get_inner_svalue (), + bits_within_sval2->get_inner_svalue ()); + } + break; case SK_UNMERGEABLE: { const unmergeable_svalue *unmergeable_sval1 @@ -515,6 +536,27 @@ svalue::involves_p (const svalue *other) const return v.found_p (); } +/* Extract SUBRANGE from this value, of type TYPE. */ + +const svalue * +svalue::extract_bit_range (tree type, + const bit_range &subrange, + region_model_manager *mgr) const +{ + return mgr->get_or_create_bits_within (type, subrange, this); +} + +/* Base implementation of svalue::maybe_fold_bits_within vfunc. */ + +const svalue * +svalue::maybe_fold_bits_within (tree, + const bit_range &, + region_model_manager *) const +{ + /* By default, don't fold. */ + return NULL; +} + /* class region_svalue : public svalue. */ /* Implementation of svalue::dump_to_pp vfunc for region_svalue. */ @@ -680,6 +722,26 @@ constant_svalue::eval_condition (const constant_svalue *lhs, return tristate::TS_UNKNOWN; } +/* Implementation of svalue::maybe_fold_bits_within vfunc + for constant_svalue. */ + +const svalue * +constant_svalue::maybe_fold_bits_within (tree type, + const bit_range &, + region_model_manager *mgr) const +{ + /* Bits within an all-zero value are also all zero. */ + if (zerop (m_cst_expr)) + { + if (type) + return mgr->get_or_create_cast (type, this); + else + return this; + } + /* Otherwise, don't fold. */ + return NULL; +} + /* class unknown_svalue : public svalue. */ /* Implementation of svalue::dump_to_pp vfunc for unknown_svalue. */ @@ -711,6 +773,18 @@ unknown_svalue::accept (visitor *v) const v->visit_unknown_svalue (this); } +/* Implementation of svalue::maybe_fold_bits_within vfunc + for unknown_svalue. */ + +const svalue * +unknown_svalue::maybe_fold_bits_within (tree type, + const bit_range &, + region_model_manager *mgr) const +{ + /* Bits within an unknown_svalue are themselves unknown. */ + return mgr->get_or_create_unknown_svalue (type); +} + /* Get a string for KIND for use in debug dumps. */ const char * @@ -892,6 +966,34 @@ unaryop_svalue::implicitly_live_p (const svalue_set *live_svalues, return get_arg ()->live_p (live_svalues, model); } +/* Implementation of svalue::maybe_fold_bits_within vfunc + for unaryop_svalue. */ + +const svalue * +unaryop_svalue::maybe_fold_bits_within (tree type, + const bit_range &, + region_model_manager *mgr) const +{ + switch (m_op) + { + default: + break; + case NOP_EXPR: + /* A cast of zero is zero. */ + if (tree cst = m_arg->maybe_get_constant ()) + if (zerop (cst)) + { + if (type) + return mgr->get_or_create_cast (type, this); + else + return this; + } + break; + } + /* Otherwise, don't fold. */ + return NULL; +} + /* class binop_svalue : public svalue. */ /* Implementation of svalue::dump_to_pp vfunc for binop_svalue. */ @@ -995,6 +1097,216 @@ sub_svalue::implicitly_live_p (const svalue_set *live_svalues, return get_parent ()->live_p (live_svalues, model); } +/* class repeated_svalue : public svalue. */ + +/* repeated_svalue'c ctor. */ + +repeated_svalue::repeated_svalue (tree type, + const svalue *outer_size, + const svalue *inner_svalue) +: svalue (complexity::from_pair (outer_size, inner_svalue), type), + m_outer_size (outer_size), + m_inner_svalue (inner_svalue) +{ +} + +/* Implementation of svalue::dump_to_pp vfunc for repeated_svalue. */ + +void +repeated_svalue::dump_to_pp (pretty_printer *pp, bool simple) const +{ + if (simple) + { + pp_string (pp, "REPEATED("); + if (get_type ()) + { + print_quoted_type (pp, get_type ()); + pp_string (pp, ", "); + } + pp_string (pp, "outer_size: "); + m_outer_size->dump_to_pp (pp, simple); + pp_string (pp, ", inner_val: "); + m_inner_svalue->dump_to_pp (pp, simple); + pp_character (pp, ')'); + } + else + { + pp_string (pp, "repeated_svalue ("); + if (get_type ()) + { + print_quoted_type (pp, get_type ()); + pp_string (pp, ", "); + } + pp_string (pp, "outer_size: "); + m_outer_size->dump_to_pp (pp, simple); + pp_string (pp, ", inner_val: "); + m_inner_svalue->dump_to_pp (pp, simple); + pp_character (pp, ')'); + } +} + +/* Implementation of svalue::accept vfunc for repeated_svalue. */ + +void +repeated_svalue::accept (visitor *v) const +{ + v->visit_repeated_svalue (this); + m_inner_svalue->accept (v); +} + +/* Return true if this value is known to be all zeroes. */ + +bool +repeated_svalue::all_zeroes_p () const +{ + if (tree cst = m_inner_svalue->maybe_get_constant ()) + if (zerop (cst)) + return true; + return false; +} + +/* Implementation of svalue::maybe_fold_bits_within vfunc + for repeated_svalue. */ + +const svalue * +repeated_svalue::maybe_fold_bits_within (tree type, + const bit_range &bits, + region_model_manager *mgr) const +{ + const svalue *innermost_sval = m_inner_svalue; + /* Fold + BITS_WITHIN (range, REPEATED_SVALUE (ZERO)) + to: + REPEATED_SVALUE (ZERO). */ + if (all_zeroes_p ()) + { + byte_range bytes (0,0); + if (bits.as_byte_range (&bytes)) + { + const svalue *byte_size + = mgr->get_or_create_int_cst (size_type_node, + bytes.m_size_in_bytes.to_uhwi ()); + return mgr->get_or_create_repeated_svalue (type, byte_size, + innermost_sval); + } + } + + /* Fold: + BITS_WITHIN (range, REPEATED_SVALUE (INNERMOST_SVALUE)) + to: + BITS_WITHIN (range - offset, INNERMOST_SVALUE) + if range is fully within one instance of INNERMOST_SVALUE. */ + if (tree innermost_type = innermost_sval->get_type ()) + { + bit_size_t element_bit_size; + if (int_size_in_bits (innermost_type, &element_bit_size) + && element_bit_size > 0) + { + HOST_WIDE_INT start_idx + = (bits.get_start_bit_offset () + / element_bit_size).to_shwi (); + HOST_WIDE_INT last_idx + = (bits.get_last_bit_offset () + / element_bit_size).to_shwi (); + if (start_idx == last_idx) + { + bit_offset_t start_of_element + = start_idx * element_bit_size; + bit_range range_within_element + (bits.m_start_bit_offset - start_of_element, + bits.m_size_in_bits); + return mgr->get_or_create_bits_within (type, + range_within_element, + innermost_sval); + } + } + } + + return NULL; +} + +/* class bits_within_svalue : public svalue. */ + +/* bits_within_svalue'c ctor. */ + +bits_within_svalue::bits_within_svalue (tree type, + const bit_range &bits, + const svalue *inner_svalue) +: svalue (complexity (inner_svalue), type), + m_bits (bits), + m_inner_svalue (inner_svalue) +{ +} + +/* Implementation of svalue::dump_to_pp vfunc for bits_within_svalue. */ + +void +bits_within_svalue::dump_to_pp (pretty_printer *pp, bool simple) const +{ + if (simple) + { + pp_string (pp, "BITS_WITHIN("); + if (get_type ()) + { + print_quoted_type (pp, get_type ()); + pp_string (pp, ", "); + } + m_bits.dump_to_pp (pp); + pp_string (pp, ", inner_val: "); + m_inner_svalue->dump_to_pp (pp, simple); + pp_character (pp, ')'); + } + else + { + pp_string (pp, "bits_within_svalue ("); + if (get_type ()) + { + print_quoted_type (pp, get_type ()); + pp_string (pp, ", "); + } + m_bits.dump_to_pp (pp); + pp_string (pp, ", inner_val: "); + m_inner_svalue->dump_to_pp (pp, simple); + pp_character (pp, ')'); + } +} + +/* Implementation of svalue::maybe_fold_bits_within vfunc + for bits_within_svalue. */ + +const svalue * +bits_within_svalue::maybe_fold_bits_within (tree type, + const bit_range &bits, + region_model_manager *mgr) const +{ + /* Fold: + BITS_WITHIN (range1, BITS_WITHIN (range2, VAL)) + to: + BITS_WITHIN (range1 in range 2, VAL). */ + bit_range offset_bits (m_bits.get_start_bit_offset () + + bits.m_start_bit_offset, + bits.m_size_in_bits); + return mgr->get_or_create_bits_within (type, offset_bits, m_inner_svalue); +} + +/* Implementation of svalue::accept vfunc for bits_within_svalue. */ + +void +bits_within_svalue::accept (visitor *v) const +{ + v->visit_bits_within_svalue (this); + m_inner_svalue->accept (v); +} + +/* Implementation of svalue::implicitly_live_p vfunc for bits_within_svalue. */ + +bool +bits_within_svalue::implicitly_live_p (const svalue_set *live_svalues, + const region_model *model) const +{ + return m_inner_svalue->live_p (live_svalues, model); +} + /* class widening_svalue : public svalue. */ /* Implementation of svalue::dump_to_pp vfunc for widening_svalue. */ @@ -1291,6 +1603,75 @@ compound_svalue::calc_complexity (const binding_map &map) return complexity (num_child_nodes + 1, max_child_depth + 1); } +/* Implementation of svalue::maybe_fold_bits_within vfunc + for compound_svalue. */ + +const svalue * +compound_svalue::maybe_fold_bits_within (tree type, + const bit_range &bits, + region_model_manager *mgr) const +{ + binding_map result_map; + for (auto iter : m_map) + { + const binding_key *key = iter.first; + if (const concrete_binding *conc_key + = key->dyn_cast_concrete_binding ()) + { + /* Ignore concrete bindings outside BITS. */ + if (!conc_key->get_bit_range ().intersects_p (bits)) + continue; + + const svalue *sval = iter.second; + /* Get the position of conc_key relative to BITS. */ + bit_range result_location (conc_key->get_start_bit_offset () + - bits.get_start_bit_offset (), + conc_key->get_size_in_bits ()); + /* If conc_key starts after BITS, trim off leading bits + from the svalue and adjust binding location. */ + if (result_location.m_start_bit_offset < 0) + { + bit_size_t leading_bits_to_drop + = -result_location.m_start_bit_offset; + result_location = bit_range + (0, result_location.m_size_in_bits - leading_bits_to_drop); + bit_range bits_within_sval (leading_bits_to_drop, + result_location.m_size_in_bits); + /* Trim off leading bits from iter_sval. */ + sval = mgr->get_or_create_bits_within (NULL_TREE, + bits_within_sval, + sval); + } + /* If conc_key finishes after BITS, trim off trailing bits + from the svalue and adjust binding location. */ + if (conc_key->get_next_bit_offset () + > bits.get_next_bit_offset ()) + { + bit_size_t trailing_bits_to_drop + = (conc_key->get_next_bit_offset () + - bits.get_next_bit_offset ()); + result_location = bit_range + (result_location.m_start_bit_offset, + result_location.m_size_in_bits - trailing_bits_to_drop); + bit_range bits_within_sval (0, + result_location.m_size_in_bits); + /* Trim off leading bits from iter_sval. */ + sval = mgr->get_or_create_bits_within (NULL_TREE, + bits_within_sval, + sval); + } + const concrete_binding *offset_conc_key + = mgr->get_store_manager ()->get_concrete_binding + (result_location); + result_map.put (offset_conc_key, sval); + } + else + /* If we have any symbolic keys we can't get it as bits. */ + return NULL; + } + return mgr->get_or_create_compound_svalue (type, result_map); +} + /* class conjured_svalue : public svalue. */ /* Implementation of svalue::dump_to_pp vfunc for conjured_svalue. */ diff --git a/gcc/analyzer/svalue.h b/gcc/analyzer/svalue.h index d9e34aa..3965a5f 100644 --- a/gcc/analyzer/svalue.h +++ b/gcc/analyzer/svalue.h @@ -41,6 +41,8 @@ enum svalue_kind SK_UNARYOP, SK_BINOP, SK_SUB, + SK_REPEATED, + SK_BITS_WITHIN, SK_UNMERGEABLE, SK_PLACEHOLDER, SK_WIDENING, @@ -63,6 +65,9 @@ enum svalue_kind unaryop_svalue (SK_UNARYOP): unary operation on another svalue binop_svalue (SK_BINOP): binary operation on two svalues sub_svalue (SK_SUB): the result of accessing a subregion + repeated_svalue (SK_REPEATED): repeating an svalue to fill a larger region + bits_within_svalue (SK_BITS_WITHIN): a range of bits/bytes within a larger + svalue unmergeable_svalue (SK_UNMERGEABLE): a value that is so interesting from a control-flow perspective that it can inhibit state-merging placeholder_svalue (SK_PLACEHOLDER): for use in selftests. @@ -107,6 +112,10 @@ public: dyn_cast_binop_svalue () const { return NULL; } virtual const sub_svalue * dyn_cast_sub_svalue () const { return NULL; } + virtual const repeated_svalue * + dyn_cast_repeated_svalue () const { return NULL; } + virtual const bits_within_svalue * + dyn_cast_bits_within_svalue () const { return NULL; } virtual const unmergeable_svalue * dyn_cast_unmergeable_svalue () const { return NULL; } virtual const widening_svalue * @@ -138,6 +147,16 @@ public: bool involves_p (const svalue *other) const; + const svalue * + extract_bit_range (tree type, + const bit_range &subrange, + region_model_manager *mgr) const; + + virtual const svalue * + maybe_fold_bits_within (tree type, + const bit_range &subrange, + region_model_manager *mgr) const; + protected: svalue (complexity c, tree type) : m_complexity (c), m_type (type) @@ -175,9 +194,9 @@ public: } void mark_deleted () { m_type = reinterpret_cast<tree> (1); } - void mark_empty () { m_type = NULL_TREE; } + void mark_empty () { m_type = reinterpret_cast<tree> (2); } bool is_deleted () const { return m_type == reinterpret_cast<tree> (1); } - bool is_empty () const { return m_type == NULL_TREE; } + bool is_empty () const { return m_type == reinterpret_cast<tree> (2); } tree m_type; const region *m_reg; @@ -222,7 +241,7 @@ is_a_helper <const region_svalue *>::test (const svalue *sval) template <> struct default_hash_traits<region_svalue::key_t> : public member_function_hash_traits<region_svalue::key_t> { - static const bool empty_zero_p = true; + static const bool empty_zero_p = false; }; namespace ana { @@ -253,6 +272,11 @@ public: enum tree_code op, const constant_svalue *rhs); + const svalue * + maybe_fold_bits_within (tree type, + const bit_range &subrange, + region_model_manager *mgr) const FINAL OVERRIDE; + private: tree m_cst_expr; }; @@ -285,6 +309,11 @@ public: void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE; void accept (visitor *v) const FINAL OVERRIDE; + + const svalue * + maybe_fold_bits_within (tree type, + const bit_range &subrange, + region_model_manager *mgr) const FINAL OVERRIDE; }; /* An enum describing a particular kind of "poisoned" value. */ @@ -327,9 +356,9 @@ public: } void mark_deleted () { m_type = reinterpret_cast<tree> (1); } - void mark_empty () { m_type = NULL_TREE; } + void mark_empty () { m_type = reinterpret_cast<tree> (2); } bool is_deleted () const { return m_type == reinterpret_cast<tree> (1); } - bool is_empty () const { return m_type == NULL_TREE; } + bool is_empty () const { return m_type == reinterpret_cast<tree> (2); } enum poison_kind m_kind; tree m_type; @@ -364,7 +393,7 @@ is_a_helper <const poisoned_svalue *>::test (const svalue *sval) template <> struct default_hash_traits<poisoned_svalue::key_t> : public member_function_hash_traits<poisoned_svalue::key_t> { - static const bool empty_zero_p = true; + static const bool empty_zero_p = false; }; namespace ana { @@ -426,9 +455,9 @@ public: } void mark_deleted () { m_type = reinterpret_cast<tree> (1); } - void mark_empty () { m_type = NULL_TREE; } + void mark_empty () { m_type = reinterpret_cast<tree> (2); } bool is_deleted () const { return m_type == reinterpret_cast<tree> (1); } - bool is_empty () const { return m_type == NULL_TREE; } + bool is_empty () const { return m_type == reinterpret_cast<tree> (2); } setjmp_record m_record; tree m_type; @@ -467,7 +496,7 @@ is_a_helper <const setjmp_svalue *>::test (const svalue *sval) template <> struct default_hash_traits<setjmp_svalue::key_t> : public member_function_hash_traits<setjmp_svalue::key_t> { - static const bool empty_zero_p = true; + static const bool empty_zero_p = false; }; namespace ana { @@ -548,9 +577,9 @@ public: } void mark_deleted () { m_type = reinterpret_cast<tree> (1); } - void mark_empty () { m_type = NULL_TREE; } + void mark_empty () { m_type = reinterpret_cast<tree> (2); } bool is_deleted () const { return m_type == reinterpret_cast<tree> (1); } - bool is_empty () const { return m_type == NULL_TREE; } + bool is_empty () const { return m_type == reinterpret_cast<tree> (2); } tree m_type; enum tree_code m_op; @@ -574,6 +603,11 @@ public: enum tree_code get_op () const { return m_op; } const svalue *get_arg () const { return m_arg; } + const svalue * + maybe_fold_bits_within (tree type, + const bit_range &subrange, + region_model_manager *mgr) const FINAL OVERRIDE; + private: enum tree_code m_op; const svalue *m_arg; @@ -592,7 +626,7 @@ is_a_helper <const unaryop_svalue *>::test (const svalue *sval) template <> struct default_hash_traits<unaryop_svalue::key_t> : public member_function_hash_traits<unaryop_svalue::key_t> { - static const bool empty_zero_p = true; + static const bool empty_zero_p = false; }; namespace ana { @@ -630,9 +664,9 @@ public: } void mark_deleted () { m_type = reinterpret_cast<tree> (1); } - void mark_empty () { m_type = NULL_TREE; } + void mark_empty () { m_type = reinterpret_cast<tree> (2); } bool is_deleted () const { return m_type == reinterpret_cast<tree> (1); } - bool is_empty () const { return m_type == NULL_TREE; } + bool is_empty () const { return m_type == reinterpret_cast<tree> (2); } tree m_type; enum tree_code m_op; @@ -683,7 +717,7 @@ is_a_helper <const binop_svalue *>::test (const svalue *sval) template <> struct default_hash_traits<binop_svalue::key_t> : public member_function_hash_traits<binop_svalue::key_t> { - static const bool empty_zero_p = true; + static const bool empty_zero_p = false; }; namespace ana { @@ -719,9 +753,9 @@ public: } void mark_deleted () { m_type = reinterpret_cast<tree> (1); } - void mark_empty () { m_type = NULL_TREE; } + void mark_empty () { m_type = reinterpret_cast<tree> (2); } bool is_deleted () const { return m_type == reinterpret_cast<tree> (1); } - bool is_empty () const { return m_type == NULL_TREE; } + bool is_empty () const { return m_type == reinterpret_cast<tree> (2); } tree m_type; const svalue *m_parent_svalue; @@ -762,7 +796,182 @@ is_a_helper <const sub_svalue *>::test (const svalue *sval) template <> struct default_hash_traits<sub_svalue::key_t> : public member_function_hash_traits<sub_svalue::key_t> { - static const bool empty_zero_p = true; + static const bool empty_zero_p = false; +}; + +namespace ana { + +/* Concrete subclass of svalue representing repeating an inner svalue + (possibly not a whole number of times) to fill a larger region of + type TYPE of size OUTER_SIZE bytes. */ + +class repeated_svalue : public svalue +{ +public: + /* A support class for uniquifying instances of repeated_svalue. */ + struct key_t + { + key_t (tree type, + const svalue *outer_size, + const svalue *inner_svalue) + : m_type (type), m_outer_size (outer_size), m_inner_svalue (inner_svalue) + {} + + hashval_t hash () const + { + inchash::hash hstate; + hstate.add_ptr (m_type); + hstate.add_ptr (m_outer_size); + hstate.add_ptr (m_inner_svalue); + return hstate.end (); + } + + bool operator== (const key_t &other) const + { + return (m_type == other.m_type + && m_outer_size == other.m_outer_size + && m_inner_svalue == other.m_inner_svalue); + } + + void mark_deleted () { m_type = reinterpret_cast<tree> (1); } + void mark_empty () { m_type = reinterpret_cast<tree> (2); } + bool is_deleted () const { return m_type == reinterpret_cast<tree> (1); } + bool is_empty () const { return m_type == reinterpret_cast<tree> (2); } + + tree m_type; + const svalue *m_outer_size; + const svalue *m_inner_svalue; + }; + repeated_svalue (tree type, + const svalue *outer_size, + const svalue *inner_svalue); + + enum svalue_kind get_kind () const FINAL OVERRIDE { return SK_REPEATED; } + const repeated_svalue *dyn_cast_repeated_svalue () const FINAL OVERRIDE + { + return this; + } + + void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE; + void accept (visitor *v) const FINAL OVERRIDE; + + const svalue *get_outer_size () const { return m_outer_size; } + const svalue *get_inner_svalue () const { return m_inner_svalue; } + + bool all_zeroes_p () const; + + const svalue * + maybe_fold_bits_within (tree type, + const bit_range &subrange, + region_model_manager *mgr) const FINAL OVERRIDE; + + private: + const svalue *m_outer_size; + const svalue *m_inner_svalue; +}; + +} // namespace ana + +template <> +template <> +inline bool +is_a_helper <const repeated_svalue *>::test (const svalue *sval) +{ + return sval->get_kind () == SK_REPEATED; +} + +template <> struct default_hash_traits<repeated_svalue::key_t> +: public member_function_hash_traits<repeated_svalue::key_t> +{ + static const bool empty_zero_p = false; +}; + +namespace ana { + +/* A range of bits/bytes within another svalue + e.g. bytes 5-39 of INITIAL_SVALUE(R). + These can be generated for prefixes and suffixes when part of a binding + is clobbered, so that we don't lose too much information. */ + +class bits_within_svalue : public svalue +{ +public: + /* A support class for uniquifying instances of bits_within_svalue. */ + struct key_t + { + key_t (tree type, + const bit_range &bits, + const svalue *inner_svalue) + : m_type (type), m_bits (bits), m_inner_svalue (inner_svalue) + {} + + hashval_t hash () const + { + inchash::hash hstate; + hstate.add_ptr (m_type); + hstate.add_ptr (m_inner_svalue); + return hstate.end (); + } + + bool operator== (const key_t &other) const + { + return (m_type == other.m_type + && m_bits == other.m_bits + && m_inner_svalue == other.m_inner_svalue); + } + + void mark_deleted () { m_type = reinterpret_cast<tree> (1); } + void mark_empty () { m_type = reinterpret_cast<tree> (2); } + bool is_deleted () const { return m_type == reinterpret_cast<tree> (1); } + bool is_empty () const { return m_type == reinterpret_cast<tree> (2); } + + tree m_type; + bit_range m_bits; + const svalue *m_inner_svalue; + }; + bits_within_svalue (tree type, + const bit_range &bits, + const svalue *inner_svalue); + + enum svalue_kind get_kind () const FINAL OVERRIDE { return SK_BITS_WITHIN; } + const bits_within_svalue * + dyn_cast_bits_within_svalue () const FINAL OVERRIDE + { + return this; + } + + void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE; + void accept (visitor *v) const FINAL OVERRIDE; + bool implicitly_live_p (const svalue_set *, + const region_model *) const FINAL OVERRIDE; + + const bit_range &get_bits () const { return m_bits; } + const svalue *get_inner_svalue () const { return m_inner_svalue; } + + const svalue * + maybe_fold_bits_within (tree type, + const bit_range &subrange, + region_model_manager *mgr) const FINAL OVERRIDE; + + private: + const bit_range m_bits; + const svalue *m_inner_svalue; +}; + +} // namespace ana + +template <> +template <> +inline bool +is_a_helper <const bits_within_svalue *>::test (const svalue *sval) +{ + return sval->get_kind () == SK_BITS_WITHIN; +} + +template <> struct default_hash_traits<bits_within_svalue::key_t> +: public member_function_hash_traits<bits_within_svalue::key_t> +{ + static const bool empty_zero_p = false; }; namespace ana { @@ -888,9 +1097,9 @@ public: } void mark_deleted () { m_type = reinterpret_cast<tree> (1); } - void mark_empty () { m_type = NULL_TREE; } + void mark_empty () { m_type = reinterpret_cast<tree> (2); } bool is_deleted () const { return m_type == reinterpret_cast<tree> (1); } - bool is_empty () const { return m_type == NULL_TREE; } + bool is_empty () const { return m_type == reinterpret_cast<tree> (2); } tree m_type; function_point m_point; @@ -952,7 +1161,7 @@ is_a_helper <widening_svalue *>::test (svalue *sval) template <> struct default_hash_traits<widening_svalue::key_t> : public member_function_hash_traits<widening_svalue::key_t> { - static const bool empty_zero_p = true; + static const bool empty_zero_p = false; }; namespace ana { @@ -1000,9 +1209,9 @@ public: } void mark_deleted () { m_type = reinterpret_cast<tree> (1); } - void mark_empty () { m_type = NULL_TREE; } + void mark_empty () { m_type = reinterpret_cast<tree> (2); } bool is_deleted () const { return m_type == reinterpret_cast<tree> (1); } - bool is_empty () const { return m_type == NULL_TREE; } + bool is_empty () const { return m_type == reinterpret_cast<tree> (2); } tree m_type; const binding_map *m_map_ptr; @@ -1029,6 +1238,11 @@ public: return key_t (get_type (), &m_map); } + const svalue * + maybe_fold_bits_within (tree type, + const bit_range &subrange, + region_model_manager *mgr) const FINAL OVERRIDE; + private: static complexity calc_complexity (const binding_map &map); @@ -1048,7 +1262,7 @@ is_a_helper <compound_svalue *>::test (svalue *sval) template <> struct default_hash_traits<compound_svalue::key_t> : public member_function_hash_traits<compound_svalue::key_t> { - static const bool empty_zero_p = true; + static const bool empty_zero_p = false; }; namespace ana { diff --git a/gcc/testsuite/gcc.dg/analyzer/clobbers-1.c b/gcc/testsuite/gcc.dg/analyzer/clobbers-1.c new file mode 100644 index 0000000..824dbd4 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/clobbers-1.c @@ -0,0 +1,98 @@ +#include "analyzer-decls.h" + +struct foo +{ + int i; + int j; +}; + +struct coord +{ + int x; + int y; + int z; +}; + +struct foo g; + +void test_1 (void) +{ + g.i = 42; + if (g.j) + __analyzer_eval (g.j); /* { dg-warning "TRUE" } */ + else + __analyzer_eval (g.j); /* { dg-warning "FALSE" } */ + __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 processed enode" } */ +} + +void test_2 (void) +{ + struct foo f; + f.i = 42; + if (f.j) + __analyzer_eval (f.j); /* { dg-warning "TRUE" } */ + else + __analyzer_eval (f.j); /* { dg-warning "FALSE" } */ + __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 processed enode" } */ +} + +void test_3 (struct foo *p) +{ + struct foo f = *p; + f.i = 42; + if (f.j) + __analyzer_eval (f.j); /* { dg-warning "TRUE" } */ + else + __analyzer_eval (f.j); /* { dg-warning "FALSE" } */ + __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 processed enode" } */ +} + +void test_4 (struct coord *p) +{ + struct coord f = *p; + f.x = 42; + __analyzer_eval (f.y == p->y); /* { dg-warning "TRUE" } */ + __analyzer_eval (f.z == p->z); /* { dg-warning "TRUE" } */ +} + +struct s5 +{ + char arr[8]; +}; + +void test_5 (struct s5 *p) +{ + struct s5 f = *p; + f.arr[3] = 42; + __analyzer_eval (f.arr[0] == p->arr[0]); /* { dg-warning "TRUE" } */ + __analyzer_eval (f.arr[1] == p->arr[1]); /* { dg-warning "TRUE" } */ + __analyzer_eval (f.arr[2] == p->arr[2]); /* { dg-warning "TRUE" } */ + __analyzer_eval (f.arr[3] == 42); /* { dg-warning "TRUE" } */ + __analyzer_eval (f.arr[4] == p->arr[4]); /* { dg-warning "TRUE" } */ + __analyzer_eval (f.arr[5] == p->arr[5]); /* { dg-warning "TRUE" } */ + __analyzer_eval (f.arr[6] == p->arr[6]); /* { dg-warning "TRUE" } */ + __analyzer_eval (f.arr[7] == p->arr[7]); /* { dg-warning "TRUE" } */ +} + +struct s6 +{ + int before; /* Give "arr" a nonzero offset. */ + struct foo arr[4]; + int after; +}; + +void test_6 (struct s6 *p, struct foo *q) +{ + struct s6 f = *p; + f.arr[1] = *q; + __analyzer_eval (f.before == p->before); /* { dg-warning "TRUE" } */ + __analyzer_eval (f.arr[0].i == p->arr[0].i); /* { dg-warning "TRUE" } */ + __analyzer_eval (f.arr[0].j == p->arr[0].j); /* { dg-warning "TRUE" } */ + __analyzer_eval (f.arr[1].i == q->i); /* { dg-warning "TRUE" } */ + __analyzer_eval (f.arr[1].j == q->j); /* { dg-warning "TRUE" } */ + __analyzer_eval (f.arr[2].i == p->arr[2].i); /* { dg-warning "TRUE" } */ + __analyzer_eval (f.arr[2].j == p->arr[2].j); /* { dg-warning "TRUE" } */ + __analyzer_eval (f.arr[3].i == p->arr[3].i); /* { dg-warning "TRUE" } */ + __analyzer_eval (f.arr[3].j == p->arr[3].j); /* { dg-warning "TRUE" } */ + __analyzer_eval (f.after == p->after); /* { dg-warning "TRUE" } */ +} diff --git a/gcc/testsuite/gcc.dg/analyzer/clobbers-2.c b/gcc/testsuite/gcc.dg/analyzer/clobbers-2.c new file mode 100644 index 0000000..9a88349 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/clobbers-2.c @@ -0,0 +1,72 @@ +#include "analyzer-decls.h" + +typedef __SIZE_TYPE__ size_t; +extern void bzero (void *s, size_t n); +extern void *memset(void *s, int c, size_t n); + +void test_1 (void) +{ + char arr[16]; + bzero (arr, sizeof (arr)); + __analyzer_eval (arr[0] == 0); /* { dg-warning "TRUE" } */ + __analyzer_eval (arr[7] == 0); /* { dg-warning "TRUE" } */ + __analyzer_eval (arr[8] == 0); /* { dg-warning "TRUE" } */ + __analyzer_eval (arr[9] == 0); /* { dg-warning "TRUE" } */ + __analyzer_eval (arr[15] == 0); /* { dg-warning "TRUE" } */ + + /* Clobber in the middle (with prefix and suffix). */ + arr[8] = 42; + __analyzer_eval (arr[0] == 0); /* { dg-warning "TRUE" } */ + __analyzer_eval (arr[7] == 0); /* { dg-warning "TRUE" } */ + __analyzer_eval (arr[8] == 0); /* { dg-warning "FALSE" } */ + __analyzer_eval (arr[8] == 42); /* { dg-warning "TRUE" } */ + __analyzer_eval (arr[9] == 0); /* { dg-warning "TRUE" } */ + __analyzer_eval (arr[15] == 0); /* { dg-warning "TRUE" } */ +} + +void test_2 (void) +{ + char arr[16]; + bzero (arr, sizeof (arr)); + __analyzer_eval (arr[0] == 0); /* { dg-warning "TRUE" } */ + __analyzer_eval (arr[1] == 0); /* { dg-warning "TRUE" } */ + __analyzer_eval (arr[15] == 0); /* { dg-warning "TRUE" } */ + + /* Clobber at the front (suffix, but no prefix). */ + arr[0] = 42; + __analyzer_eval (arr[0] == 0); /* { dg-warning "FALSE" } */ + __analyzer_eval (arr[0] == 42); /* { dg-warning "TRUE" } */ + __analyzer_eval (arr[1] == 0); /* { dg-warning "TRUE" } */ + __analyzer_eval (arr[15] == 0); /* { dg-warning "TRUE" } */ +} + +void test_3 (void) +{ + char arr[16]; + bzero (arr, sizeof (arr)); + __analyzer_eval (arr[0] == 0); /* { dg-warning "TRUE" } */ + __analyzer_eval (arr[14] == 0); /* { dg-warning "TRUE" } */ + __analyzer_eval (arr[15] == 0); /* { dg-warning "TRUE" } */ + + /* Clobber at the end (prefix, but no suffix). */ + arr[15] = 42; + __analyzer_eval (arr[0] == 0); /* { dg-warning "TRUE" } */ + __analyzer_eval (arr[14] == 0); /* { dg-warning "TRUE" } */ + __analyzer_eval (arr[15] == 0); /* { dg-warning "FALSE" } */ + __analyzer_eval (arr[15] == 42); /* { dg-warning "TRUE" } */ +} + +void test_4 (void) +{ + char arr[16]; + bzero (arr, sizeof (arr)); + __analyzer_eval (arr[0] == 0); /* { dg-warning "TRUE" } */ + __analyzer_eval (arr[15] == 0); /* { dg-warning "TRUE" } */ + + /* Exact overlap, no prefix or suffix. */ + memset (arr, 1, 16); + __analyzer_eval (arr[0] == 0); /* { dg-warning "FALSE" } */ + __analyzer_eval (arr[15] == 0); /* { dg-warning "FALSE" } */ + __analyzer_eval (arr[0] == 1); /* { dg-warning "TRUE" } */ + __analyzer_eval (arr[15] == 1); /* { dg-warning "TRUE" } */ +} diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-1.c b/gcc/testsuite/gcc.dg/analyzer/data-model-1.c index 4a62a0e..34932da 100644 --- a/gcc/testsuite/gcc.dg/analyzer/data-model-1.c +++ b/gcc/testsuite/gcc.dg/analyzer/data-model-1.c @@ -503,9 +503,7 @@ void test_26 (struct coord *p, struct coord *q) the dest value. */ *p = *q; __analyzer_eval (p->x); /* { dg-warning "UNKNOWN" } */ - __analyzer_eval (p->y == 17); /* { dg-warning "TRUE" "desired" { xfail *-*-* } } */ - /* { dg-warning "UNKNOWN" "status quo" { target *-*-* } .-1 } */ - // TODO(xfail): should have been overwritten with q->y + __analyzer_eval (p->y == 17); /* { dg-warning "TRUE" } */ __analyzer_eval (q->x); /* { dg-warning "UNKNOWN" } */ __analyzer_eval (q->y == 17); /* { dg-warning "TRUE" "desired" { xfail *-*-* } } */ @@ -522,19 +520,11 @@ void test_27 (struct coord *p) void test_28 (struct coord *p) { memset (p, 0, sizeof (struct coord) * 10); - __analyzer_eval (p[0].x == 0); /* { dg-warning "TRUE" "desired" { xfail *-*-* } } */ - /* { dg-warning "UNKNOWN" "status quo" { target *-*-* } .-1 } */ - // TODO(xfail): - __analyzer_eval (p[0].y == 0); /* { dg-warning "TRUE" "desired" { xfail *-*-* } } */ - /* { dg-warning "UNKNOWN" "status quo" { target *-*-* } .-1 } */ - // TODO(xfail): + __analyzer_eval (p[0].x == 0); /* { dg-warning "TRUE" } */ + __analyzer_eval (p[0].y == 0); /* { dg-warning "TRUE" } */ - __analyzer_eval (p[9].x == 0); /* { dg-warning "TRUE" "desired" { xfail *-*-* } } */ - /* { dg-warning "UNKNOWN" "status quo" { target *-*-* } .-1 } */ - // TODO(xfail): - __analyzer_eval (p[9].y == 0); /* { dg-warning "TRUE" "desired" { xfail *-*-* } } */ - /* { dg-warning "UNKNOWN" "status quo" { target *-*-* } .-1 } */ - // TODO(xfail): + __analyzer_eval (p[9].x == 0); /* { dg-warning "TRUE" } */ + __analyzer_eval (p[9].y == 0); /* { dg-warning "TRUE" } */ __analyzer_eval (p[10].x == 0); /* { dg-warning "UNKNOWN" } */ __analyzer_eval (p[10].y == 0); /* { dg-warning "UNKNOWN" } */ @@ -1035,8 +1025,8 @@ void test_52 (struct big b) { struct big d; memcpy (&d, &b, sizeof (struct big)); - __analyzer_eval (b.ia[0] == d.ia[0]); /* { dg-warning "TRUE" "desired" { xfail *-*-* } } */ - /* { dg-warning "UNKNOWN" "status quo" { target *-*-* } .-1 } */ + __analyzer_eval (b.ia[0] == d.ia[0]); /* { dg-warning "TRUE" } */ + __analyzer_eval (b.ia[1023] == d.ia[1023]); /* { dg-warning "TRUE" } */ } void test_53 (const char *msg) diff --git a/gcc/testsuite/gcc.dg/analyzer/explode-1.c b/gcc/testsuite/gcc.dg/analyzer/explode-1.c index 6b62e8e..f48408e 100644 --- a/gcc/testsuite/gcc.dg/analyzer/explode-1.c +++ b/gcc/testsuite/gcc.dg/analyzer/explode-1.c @@ -12,7 +12,7 @@ void test (void) { void *p0, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8; void **pp; - while (get ()) + while (get ()) /* { dg-warning "leak" } */ { switch (get ()) { diff --git a/gcc/testsuite/gcc.dg/analyzer/memset-1.c b/gcc/testsuite/gcc.dg/analyzer/memset-1.c index 5748aa1..94c5a1b 100644 --- a/gcc/testsuite/gcc.dg/analyzer/memset-1.c +++ b/gcc/testsuite/gcc.dg/analyzer/memset-1.c @@ -36,22 +36,16 @@ void test_3 (int val) { char buf[256]; memset (buf, 'A', 256); - /* We currently merely mark such regions as "unknown", so querying - values within them yields UNKNOWN when ideally it would be TRUE. */ - __analyzer_eval (buf[42] == 'A'); /* { dg-warning "TRUE" "known nonzero" { xfail *-*-* } } */ - /* { dg-bogus "UNKNOWN" "status quo" { xfail *-*-* } .-1 } */ + __analyzer_eval (buf[42] == 'A'); /* { dg-warning "TRUE" } */ } /* A "memset" with unknown value. */ -void test_4 (int val) +void test_4 (char val) { char buf[256]; memset (buf, val, 256); - /* We currently merely mark such regions as "unknown", so querying - values within them yields UNKNOWN when ideally it would be TRUE. */ - __analyzer_eval (buf[42] == (char)val); /* { dg-warning "TRUE" "known nonzero" { xfail *-*-* } } */ - /* { dg-bogus "UNKNOWN" "status quo" { xfail *-*-* } .-1 } */ + __analyzer_eval (buf[42] == (char)val); /* { dg-warning "TRUE" } */ } /* A "memset" with unknown num bytes. */ @@ -98,6 +92,14 @@ void test_6 (int val) __analyzer_eval (buf[42] == 'A'); /* { dg-warning "TRUE" } */ } +void test_6b (int val) +{ + char buf[256]; + memset (buf, 'A', sizeof (buf)); + memset (buf, 'B', get_zero ()); + __analyzer_eval (buf[42] == 'A'); /* { dg-warning "TRUE" } */ +} + /* A "memset" of known size that's not the full buffer. */ void test_7 (void) @@ -105,10 +107,96 @@ void test_7 (void) char buf[256]; buf[128] = 'A'; memset (buf, 0, 128); - /* We currently merely mark the whole region as "unknown", so querying - values within them yields UNKNOWN. */ - __analyzer_eval (buf[127] == '\0'); /* { dg-warning "TRUE" "known nonzero" { xfail *-*-* } } */ - /* { dg-bogus "UNKNOWN" "status quo" { xfail *-*-* } .-1 } */ - __analyzer_eval (buf[128] == 'A'); /* { dg-warning "TRUE" "known nonzero" { xfail *-*-* } } */ - /* { dg-bogus "UNKNOWN" "status quo" { xfail *-*-* } .-1 } */ + __analyzer_eval (buf[0] == '\0'); /* { dg-warning "TRUE" } */ + __analyzer_eval (buf[127] == '\0'); /* { dg-warning "TRUE" } */ + __analyzer_eval (buf[128] == 'A'); /* { dg-warning "TRUE" } */ +} + +void test_8 (void) +{ + char buf[20]; + memset (buf + 0, 0, 1); + memset (buf + 1, 1, 1); + memset (buf + 2, 2, 1); + memset (buf + 3, 3, 1); + memset (buf + 4, 4, 2); + memset (buf + 6, 6, 2); + memset (buf + 8, 8, 4); + memset (buf + 12, 12, 8); + __analyzer_eval (buf[0] == 0); /* { dg-warning "TRUE" } */ + __analyzer_eval (buf[1] == 1); /* { dg-warning "TRUE" } */ + __analyzer_eval (buf[2] == 2); /* { dg-warning "TRUE" } */ + __analyzer_eval (buf[3] == 3); /* { dg-warning "TRUE" } */ + __analyzer_eval (buf[4] == 4); /* { dg-warning "TRUE" } */ + __analyzer_eval (buf[5] == 4); /* { dg-warning "TRUE" } */ + __analyzer_eval (buf[6] == 6); /* { dg-warning "TRUE" } */ + __analyzer_eval (buf[7] == 6); /* { dg-warning "TRUE" } */ + __analyzer_eval (buf[8] == 8); /* { dg-warning "TRUE" } */ + __analyzer_eval (buf[9] == 8); /* { dg-warning "TRUE" } */ + __analyzer_eval (buf[10] == 8); /* { dg-warning "TRUE" } */ + __analyzer_eval (buf[11] == 8); /* { dg-warning "TRUE" } */ + __analyzer_eval (buf[12] == 12); /* { dg-warning "TRUE" } */ + __analyzer_eval (buf[13] == 12); /* { dg-warning "TRUE" } */ + __analyzer_eval (buf[14] == 12); /* { dg-warning "TRUE" } */ + __analyzer_eval (buf[15] == 12); /* { dg-warning "TRUE" } */ + __analyzer_eval (buf[16] == 12); /* { dg-warning "TRUE" } */ + __analyzer_eval (buf[17] == 12); /* { dg-warning "TRUE" } */ + __analyzer_eval (buf[18] == 12); /* { dg-warning "TRUE" } */ + __analyzer_eval (buf[19] == 12); /* { dg-warning "TRUE" } */ +} + +/* Various overlapping memset calls with different sizes and values. */ + +void test_9 (void) +{ + char buf[8]; + memset (buf, 0, 8); + __analyzer_eval (buf[0] == 0); /* { dg-warning "TRUE" } */ + __analyzer_eval (buf[1] == 0); /* { dg-warning "TRUE" } */ + __analyzer_eval (buf[2] == 0); /* { dg-warning "TRUE" } */ + __analyzer_eval (buf[3] == 0); /* { dg-warning "TRUE" } */ + __analyzer_eval (buf[4] == 0); /* { dg-warning "TRUE" } */ + __analyzer_eval (buf[5] == 0); /* { dg-warning "TRUE" } */ + __analyzer_eval (buf[6] == 0); /* { dg-warning "TRUE" } */ + __analyzer_eval (buf[7] == 0); /* { dg-warning "TRUE" } */ + + memset (buf + 1, 1, 4); + __analyzer_eval (buf[0] == 0); /* { dg-warning "TRUE" } */ + __analyzer_eval (buf[1] == 1); /* { dg-warning "TRUE" } */ + __analyzer_eval (buf[2] == 1); /* { dg-warning "TRUE" } */ + __analyzer_eval (buf[3] == 1); /* { dg-warning "TRUE" } */ + __analyzer_eval (buf[4] == 1); /* { dg-warning "TRUE" } */ + __analyzer_eval (buf[5] == 0); /* { dg-warning "TRUE" } */ + __analyzer_eval (buf[6] == 0); /* { dg-warning "TRUE" } */ + __analyzer_eval (buf[7] == 0); /* { dg-warning "TRUE" } */ + + memset (buf + 2, 2, 4); + __analyzer_eval (buf[0] == 0); /* { dg-warning "TRUE" } */ + __analyzer_eval (buf[1] == 1); /* { dg-warning "TRUE" } */ + __analyzer_eval (buf[2] == 2); /* { dg-warning "TRUE" } */ + __analyzer_eval (buf[3] == 2); /* { dg-warning "TRUE" } */ + __analyzer_eval (buf[4] == 2); /* { dg-warning "TRUE" } */ + __analyzer_eval (buf[5] == 2); /* { dg-warning "TRUE" } */ + __analyzer_eval (buf[6] == 0); /* { dg-warning "TRUE" } */ + __analyzer_eval (buf[7] == 0); /* { dg-warning "TRUE" } */ + + memset (buf + 4, 3, 3); + __analyzer_eval (buf[0] == 0); /* { dg-warning "TRUE" } */ + __analyzer_eval (buf[1] == 1); /* { dg-warning "TRUE" } */ + __analyzer_eval (buf[2] == 2); /* { dg-warning "TRUE" } */ + __analyzer_eval (buf[3] == 2); /* { dg-warning "TRUE" } */ + __analyzer_eval (buf[4] == 3); /* { dg-warning "TRUE" } */ + __analyzer_eval (buf[5] == 3); /* { dg-warning "TRUE" } */ + __analyzer_eval (buf[6] == 3); /* { dg-warning "TRUE" } */ + __analyzer_eval (buf[7] == 0); /* { dg-warning "TRUE" } */ + + memset (buf + 0, 4, 3); + __analyzer_eval (buf[0] == 4); /* { dg-warning "TRUE" } */ + __analyzer_eval (buf[1] == 4); /* { dg-warning "TRUE" } */ + __analyzer_eval (buf[2] == 4); /* { dg-warning "TRUE" } */ + __analyzer_eval (buf[3] == 2); /* { dg-warning "TRUE" } */ + __analyzer_eval (buf[4] == 3); /* { dg-warning "TRUE" } */ + __analyzer_eval (buf[5] == 3); /* { dg-warning "TRUE" } */ + __analyzer_eval (buf[6] == 3); /* { dg-warning "TRUE" } */ + __analyzer_eval (buf[7] == 0); /* { dg-warning "TRUE" } */ } diff --git a/gcc/testsuite/gcc.dg/analyzer/memset-CVE-2017-18549-1.c b/gcc/testsuite/gcc.dg/analyzer/memset-CVE-2017-18549-1.c new file mode 100644 index 0000000..9dd1139 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/memset-CVE-2017-18549-1.c @@ -0,0 +1,107 @@ +/* This is a very simplified version of CVE-2017-18549, + a use of uninitialized padding values affecting the Linux kernel + (and thus GPLv2). + + It was fixed by e.g. 342ffc26693b528648bdc9377e51e4f2450b4860 on linux-4.13.y + in linux-stable. */ + +#include "analyzer-decls.h" +#include <string.h> + +typedef unsigned int __u32; +typedef unsigned int u32; +typedef unsigned char u8; + +/* Adapted from include/uapi/linux/types.h */ + +#define __bitwise +typedef __u32 __bitwise __le32; + +/* Adapted from drivers/scsi/aacraid/aacraid.h */ + +#define AAC_SENSE_BUFFERSIZE 30 + +struct aac_srb_reply +{ + __le32 status; + __le32 srb_status; + __le32 scsi_status; + __le32 data_xfer_length; + __le32 sense_data_size; + u8 sense_data[AAC_SENSE_BUFFERSIZE]; + + /* Manually added to help verify the fix. */ + u8 padding[2]; +}; + +#define ST_OK 0 +#define SRB_STATUS_SUCCESS 0x01 + +/* Adapted from drivers/scsi/aacraid/commctrl.c */ + +static int aac_send_raw_srb(/* [...snip...] */) +{ + u32 byte_count = 0; + + /* [...snip...] */ + + struct aac_srb_reply reply; + + reply.status = ST_OK; + + /* [...snip...] */ + + reply.srb_status = SRB_STATUS_SUCCESS; + reply.scsi_status = 0; + reply.data_xfer_length = byte_count; + reply.sense_data_size = 0; + memset(reply.sense_data, 0, AAC_SENSE_BUFFERSIZE); + + /* [...snip...] */ + + __analyzer_eval (reply.status == ST_OK); /* { dg-warning "TRUE" } */ + __analyzer_eval (reply.srb_status == SRB_STATUS_SUCCESS); /* { dg-warning "TRUE" } */ + __analyzer_eval (reply.scsi_status == 0); /* { dg-warning "TRUE" } */ + __analyzer_eval (reply.data_xfer_length == byte_count); /* { dg-warning "TRUE" } */ + __analyzer_eval (reply.sense_data_size == 0); /* { dg-warning "TRUE" } */ + __analyzer_eval (reply.sense_data[0] == 0); /* { dg-warning "TRUE" } */ + __analyzer_eval (reply.sense_data[AAC_SENSE_BUFFERSIZE - 1] == 0); /* { dg-warning "TRUE" } */ + /* TODO: the following should be detected as uninitialized, when + that diagnostic is reimplemented. */ + __analyzer_eval (reply.padding[0] == 0); /* { dg-warning "UNKNOWN" } */ + __analyzer_eval (reply.padding[1] == 0); /* { dg-warning "UNKNOWN" } */ +} + +static int aac_send_raw_srb_fixed(/* [...snip...] */) +{ + u32 byte_count = 0; + + /* [...snip...] */ + + struct aac_srb_reply reply; + + /* This is the fix. */ + memset(&reply, 0, sizeof(reply)); + + reply.status = ST_OK; + + /* [...snip...] */ + + reply.srb_status = SRB_STATUS_SUCCESS; + reply.scsi_status = 0; + reply.data_xfer_length = byte_count; + reply.sense_data_size = 0; + memset(reply.sense_data, 0, AAC_SENSE_BUFFERSIZE); + + /* [...snip...] */ + + __analyzer_eval (reply.status == ST_OK); /* { dg-warning "TRUE" } */ + __analyzer_eval (reply.srb_status == SRB_STATUS_SUCCESS); /* { dg-warning "TRUE" } */ + __analyzer_eval (reply.scsi_status == 0); /* { dg-warning "TRUE" } */ + __analyzer_eval (reply.data_xfer_length == byte_count); /* { dg-warning "TRUE" } */ + __analyzer_eval (reply.sense_data_size == 0); /* { dg-warning "TRUE" } */ + __analyzer_eval (reply.sense_data[0] == 0); /* { dg-warning "TRUE" } */ + __analyzer_eval (reply.sense_data[AAC_SENSE_BUFFERSIZE - 1] == 0); /* { dg-warning "TRUE" } */ + __analyzer_eval (reply.padding[0] == 0); /* { dg-warning "TRUE" } */ + __analyzer_eval (reply.padding[1] == 0); /* { dg-warning "TRUE" } */ +} diff --git a/gcc/testsuite/gcc.dg/analyzer/symbolic-8.c b/gcc/testsuite/gcc.dg/analyzer/symbolic-8.c new file mode 100644 index 0000000..f9c3596 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/symbolic-8.c @@ -0,0 +1,11 @@ +/* Merger where "arr" has two different symbolic bindings. */ + +void test (int i, int j, int flag) +{ + int arr[16]; + + if (flag) + arr[i] = 42; + else + arr[j] = 17; +} |