Skip to content

Commit 8b7bed6

Browse files
authored
Merge pull request #6633 from tautschnig/bugfixes/pointer-arith-overflow
Pointer overflow checks should detect overflow in offset multiplication
2 parents 874dae5 + 419837e commit 8b7bed6

File tree

3 files changed

+125
-78
lines changed

3 files changed

+125
-78
lines changed
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#include <stdint.h>
2+
3+
int main()
4+
{
5+
int32_t i[5];
6+
// Offset 1 resulted in spuriously failing to detect an overflow in pointer
7+
// arithmetic in the next line for PTRDIFF_MAX * 4 + 4 = 0 in wrap-around
8+
// semantics. Any other offset would yield the expected failure.
9+
int32_t *p = &i[1];
10+
int32_t *q = p + PTRDIFF_MAX;
11+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
CORE
2+
main.c
3+
--pointer-overflow-check
4+
^\[main.overflow.1\] line 10 arithmetic overflow on signed \* in (0x)?[0-9a-fA-F]+l* \* \(signed (long (long )?)?int\)4ul*: FAILURE$
5+
^\*\* 1 of \d+ failed
6+
^VERIFICATION FAILED$
7+
^EXIT=10$
8+
^SIGNAL=0$
9+
--
10+
^warning: ignoring

src/analyses/goto_check_c.cpp

Lines changed: 104 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,82 @@ class goto_check_ct
337337
named_check_statust match_named_check(const irep_idt &named_check) const;
338338
};
339339

340+
/// Allows to:
341+
/// - override a Boolean flag with a new value via `set_flag`
342+
/// - set a Boolean flag to false via `disable_flag`, such that
343+
/// previous `set_flag` are overridden and future `set_flag` are ignored.
344+
///
345+
/// A flag's initial value (before any `set_flag` or `disable_flag`) is restored
346+
/// when the entire object goes out of scope.
347+
class flag_overridet
348+
{
349+
public:
350+
explicit flag_overridet(const source_locationt &source_location)
351+
: source_location(source_location)
352+
{
353+
}
354+
355+
/// \brief Store the current value of \p flag and
356+
/// then set its value to \p new_value.
357+
///
358+
/// - calling `set_flag` after `disable_flag` is a no-op
359+
/// - calling `set_flag` twice triggers an INVARIANT
360+
void set_flag(bool &flag, bool new_value, const irep_idt &flag_name)
361+
{
362+
// make this a no-op if the flag is disabled
363+
if(disabled_flags.find(&flag) != disabled_flags.end())
364+
return;
365+
366+
// detect double sets
367+
INVARIANT(
368+
flags_to_reset.find(&flag) == flags_to_reset.end(),
369+
"Flag " + id2string(flag_name) + " set twice at \n" +
370+
source_location.as_string());
371+
if(flag != new_value)
372+
{
373+
flags_to_reset[&flag] = flag;
374+
flag = new_value;
375+
}
376+
}
377+
378+
/// Sets the given flag to false, overriding any previous value.
379+
///
380+
/// - calling `disable_flag` after `set_flag` overrides the set value
381+
/// - calling `disable_flag` twice triggers an INVARIANT
382+
void disable_flag(bool &flag, const irep_idt &flag_name)
383+
{
384+
INVARIANT(
385+
disabled_flags.find(&flag) == disabled_flags.end(),
386+
"Flag " + id2string(flag_name) + " disabled twice at \n" +
387+
source_location.as_string());
388+
389+
disabled_flags.insert(&flag);
390+
391+
// If the flag has not already been set,
392+
// we store its current value in the reset map.
393+
// Otherwise, the reset map already holds
394+
// the initial value we want to reset it to, keep it as is.
395+
if(flags_to_reset.find(&flag) == flags_to_reset.end())
396+
flags_to_reset[&flag] = flag;
397+
398+
// set the flag to false in all cases.
399+
flag = false;
400+
}
401+
402+
/// \brief Restore the values of all flags that have been
403+
/// modified via `set_flag`.
404+
~flag_overridet()
405+
{
406+
for(const auto &flag_pair : flags_to_reset)
407+
*flag_pair.first = flag_pair.second;
408+
}
409+
410+
private:
411+
const source_locationt &source_location;
412+
std::map<bool *, bool> flags_to_reset;
413+
std::set<bool *> disabled_flags;
414+
};
415+
340416
static exprt implication(exprt lhs, exprt rhs)
341417
{
342418
// rewrite a => (b => c) to (a && b) => c
@@ -1267,6 +1343,32 @@ void goto_check_ct::pointer_overflow_check(
12671343
expr.operands().size() == 2,
12681344
"pointer arithmetic expected to have exactly 2 operands");
12691345

1346+
// multiplying the offset by the object size must not result in arithmetic
1347+
// overflow
1348+
const typet &object_type = to_pointer_type(expr.type()).base_type();
1349+
if(object_type.id() != ID_empty)
1350+
{
1351+
auto size_of_expr_opt = size_of_expr(object_type, ns);
1352+
CHECK_RETURN(size_of_expr_opt.has_value());
1353+
exprt object_size = size_of_expr_opt.value();
1354+
1355+
const binary_exprt &binary_expr = to_binary_expr(expr);
1356+
exprt offset_operand = binary_expr.lhs().type().id() == ID_pointer
1357+
? binary_expr.rhs()
1358+
: binary_expr.lhs();
1359+
mult_exprt mul{
1360+
offset_operand,
1361+
typecast_exprt::conditional_cast(object_size, offset_operand.type())};
1362+
mul.add_source_location() = offset_operand.source_location();
1363+
1364+
flag_overridet override_overflow(offset_operand.source_location());
1365+
override_overflow.set_flag(
1366+
enable_signed_overflow_check, true, "signed_overflow_check");
1367+
override_overflow.set_flag(
1368+
enable_unsigned_overflow_check, true, "unsigned_overflow_check");
1369+
integer_overflow_check(mul, guard);
1370+
}
1371+
12701372
// the result must be within object bounds or one past the end
12711373
const auto size = from_integer(0, size_type());
12721374
auto conditions = get_pointer_dereferenceable_conditions(expr, size);
@@ -1927,82 +2029,6 @@ optionalt<exprt> goto_check_ct::rw_ok_check(exprt expr)
19272029
return {};
19282030
}
19292031

1930-
/// Allows to:
1931-
/// - override a Boolean flag with a new value via `set_flag`
1932-
/// - set a Boolean flag to false via `disable_flag`, such that
1933-
/// previous `set_flag` are overridden and future `set_flag` are ignored.
1934-
///
1935-
/// A flag's initial value (before any `set_flag` or `disable_flag`) is restored
1936-
/// when the entire object goes out of scope.
1937-
class flag_resett
1938-
{
1939-
public:
1940-
explicit flag_resett(const goto_programt::instructiont &_instruction)
1941-
: instruction(_instruction)
1942-
{
1943-
}
1944-
1945-
/// \brief Store the current value of \p flag and
1946-
/// then set its value to \p new_value.
1947-
///
1948-
/// - calling `set_flag` after `disable_flag` is a no-op
1949-
/// - calling `set_flag` twice triggers an INVARIANT
1950-
void set_flag(bool &flag, bool new_value, const irep_idt &flag_name)
1951-
{
1952-
// make this a no-op if the flag is disabled
1953-
if(disabled_flags.find(&flag) != disabled_flags.end())
1954-
return;
1955-
1956-
// detect double sets
1957-
INVARIANT(
1958-
flags_to_reset.find(&flag) == flags_to_reset.end(),
1959-
"Flag " + id2string(flag_name) + " set twice at \n" +
1960-
instruction.source_location().pretty());
1961-
if(flag != new_value)
1962-
{
1963-
flags_to_reset[&flag] = flag;
1964-
flag = new_value;
1965-
}
1966-
}
1967-
1968-
/// Sets the given flag to false, overriding any previous value.
1969-
///
1970-
/// - calling `disable_flag` after `set_flag` overrides the set value
1971-
/// - calling `disable_flag` twice triggers an INVARIANT
1972-
void disable_flag(bool &flag, const irep_idt &flag_name)
1973-
{
1974-
INVARIANT(
1975-
disabled_flags.find(&flag) == disabled_flags.end(),
1976-
"Flag " + id2string(flag_name) + " disabled twice at \n" +
1977-
instruction.source_location().pretty());
1978-
1979-
disabled_flags.insert(&flag);
1980-
1981-
// If the flag has not already been set,
1982-
// we store its current value in the reset map.
1983-
// Otherwise, the reset map already holds
1984-
// the initial value we want to reset it to, keep it as is.
1985-
if(flags_to_reset.find(&flag) == flags_to_reset.end())
1986-
flags_to_reset[&flag] = flag;
1987-
1988-
// set the flag to false in all cases.
1989-
flag = false;
1990-
}
1991-
1992-
/// \brief Restore the values of all flags that have been
1993-
/// modified via `set_flag`.
1994-
~flag_resett()
1995-
{
1996-
for(const auto &flag_pair : flags_to_reset)
1997-
*flag_pair.first = flag_pair.second;
1998-
}
1999-
2000-
private:
2001-
const goto_programt::instructiont &instruction;
2002-
std::map<bool *, bool> flags_to_reset;
2003-
std::set<bool *> disabled_flags;
2004-
};
2005-
20062032
void goto_check_ct::goto_check(
20072033
const irep_idt &function_identifier,
20082034
goto_functiont &goto_function)
@@ -2027,7 +2053,7 @@ void goto_check_ct::goto_check(
20272053
current_target = it;
20282054
goto_programt::instructiont &i = *it;
20292055

2030-
flag_resett resetter(i);
2056+
flag_overridet resetter(i.source_location());
20312057
const auto &pragmas = i.source_location().get_pragmas();
20322058
for(const auto &d : pragmas)
20332059
{
@@ -2110,7 +2136,7 @@ void goto_check_ct::goto_check(
21102136
// Disable enum range checks for left-hand sides as their values are yet
21112137
// to be set (by this assignment).
21122138
{
2113-
flag_resett resetter(i);
2139+
flag_overridet resetter(i.source_location());
21142140
resetter.disable_flag(enable_enum_range_check, "enum_range_check");
21152141
check(assign_lhs);
21162142
}

0 commit comments

Comments
 (0)