Skip to content

Commit eeb1372

Browse files
committed
Byte updates using bit fields require extension to full bytes
A byte update with an update value that has a size that's not a multiple of bytes must not overwrite bits that are not in the update value. We previously generated extractbits expressions that would go out of bounds, which tripped up the SMT solvers. No longer generating these out-of-bounds expressions makes the SMT solvers happy on the Promotion3 test, which exercised this feature.
1 parent 4b8ac7c commit eeb1372

File tree

3 files changed

+43
-4
lines changed

3 files changed

+43
-4
lines changed

regression/cbmc/Promotion3/test.desc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
CORE broken-smt-backend
1+
CORE
22
main.c
33

44
^EXIT=0$

regression/cbmc/havoc_slice/test_struct_b_slice.desc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
CORE broken-smt-backend
1+
CORE
22
test_struct_b_slice.c
33

44
^EXIT=10$

src/solvers/lowering/byte_operators.cpp

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2278,20 +2278,59 @@ exprt lower_byte_update(const byte_update_exprt &src, const namespacet &ns)
22782278
// place.
22792279
// 4) Construct a new object.
22802280
optionalt<exprt> non_const_update_bound;
2281-
auto update_size_expr_opt = size_of_expr(src.value().type(), ns);
2281+
// update value, may require extension to full bytes
2282+
exprt update_value = src.value();
2283+
auto update_size_expr_opt = size_of_expr(update_value.type(), ns);
22822284
CHECK_RETURN(update_size_expr_opt.has_value());
22832285
simplify(update_size_expr_opt.value(), ns);
22842286

22852287
if(!update_size_expr_opt.value().is_constant())
22862288
non_const_update_bound = *update_size_expr_opt;
2289+
else
2290+
{
2291+
auto update_bits = pointer_offset_bits(update_value.type(), ns);
2292+
// If the following invariant fails, then the type was only found to be
2293+
// constant via simplification. Such instances should be fixed at the place
2294+
// introducing these constant-but-not-constant_exprt type sizes.
2295+
DATA_INVARIANT(
2296+
update_bits.has_value(), "constant size-of should imply fixed bit width");
2297+
const size_t update_bits_int = numeric_cast_v<size_t>(*update_bits);
2298+
2299+
if(update_bits_int % 8 != 0)
2300+
{
2301+
DATA_INVARIANT(
2302+
can_cast_type<bitvector_typet>(update_value.type()),
2303+
"non-byte aligned type expected to be a bitvector type");
2304+
size_t n_extra_bits = 8 - update_bits_int % 8;
2305+
2306+
endianness_mapt endianness_map(
2307+
src.op().type(), src.id() == ID_byte_update_little_endian, ns);
2308+
const auto bounds = map_bounds(
2309+
endianness_map, update_bits_int, update_bits_int + n_extra_bits - 1);
2310+
extractbits_exprt extra_bits{
2311+
src.op(), bounds.ub, bounds.lb, bv_typet{n_extra_bits}};
2312+
2313+
update_value =
2314+
concatenation_exprt{typecast_exprt::conditional_cast(
2315+
update_value, bv_typet{update_bits_int}),
2316+
extra_bits,
2317+
bitvector_typet{update_value.type().id(),
2318+
update_bits_int + n_extra_bits}};
2319+
}
2320+
else
2321+
{
2322+
update_size_expr_opt =
2323+
from_integer(update_bits_int / 8, update_size_expr_opt->type());
2324+
}
2325+
}
22872326

22882327
const irep_idt extract_opcode = src.id() == ID_byte_update_little_endian
22892328
? ID_byte_extract_little_endian
22902329
: ID_byte_extract_big_endian;
22912330

22922331
const byte_extract_exprt byte_extract_expr{
22932332
extract_opcode,
2294-
src.value(),
2333+
update_value,
22952334
from_integer(0, src.offset().type()),
22962335
array_typet{bv_typet{8}, *update_size_expr_opt}};
22972336

0 commit comments

Comments
 (0)