Skip to content

Commit 8c07428

Browse files
committed
Fix byte-update flattening
This fixes byte update when applied to arrays with elements larger than a byte, which was previously unsound.
1 parent 002af82 commit 8c07428

File tree

2 files changed

+106
-38
lines changed

2 files changed

+106
-38
lines changed

src/solvers/flattening/flatten_byte_operators.cpp

+104-37
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,8 @@ Function: flatten_byte_update
190190

191191
exprt flatten_byte_update(
192192
const byte_update_exprt &src,
193-
const namespacet &ns)
193+
const namespacet &ns,
194+
bool negative_offset)
194195
{
195196
assert(src.operands().size()==3);
196197

@@ -265,44 +266,95 @@ exprt flatten_byte_update(
265266
{
266267
exprt result=src.op0();
267268

268-
for(mp_integer i=0; i<element_size; ++i)
269+
// Number of potentially affected array cells:
270+
mp_integer num_elements=
271+
element_size/sub_size+((element_size%sub_size==0)?1:2);
272+
273+
const auto& offset_type=ns.follow(src.op1().type());
274+
exprt zero_offset=from_integer(0, offset_type);
275+
276+
exprt sub_size_expr=from_integer(sub_size, offset_type);
277+
exprt element_size_expr=from_integer(element_size, offset_type);
278+
279+
// First potentially affected cell:
280+
div_exprt first_cell(src.op1(), sub_size_expr);
281+
282+
for(mp_integer i=0; i<num_elements; ++i)
269283
{
270-
exprt new_value;
284+
plus_exprt cell_index(first_cell, from_integer(i, offset_type));
285+
mult_exprt cell_offset(cell_index, sub_size_expr);
286+
index_exprt old_cell_value(src.op0(), cell_index, subtype);
287+
bool is_first_cell=i==0;
288+
bool is_last_cell=i==num_elements-1;
271289

272-
if(element_size==1)
290+
exprt new_value;
291+
exprt stored_value_offset;
292+
if(element_size<=sub_size)
293+
{
273294
new_value=src.op2();
295+
stored_value_offset=zero_offset;
296+
}
274297
else
275298
{
276-
exprt i_expr=from_integer(i, ns.follow(src.op1().type()));
277-
299+
// Offset to retrieve from the stored value: make sure not to
300+
// ask for anything out of range; in the first- or last-cell cases
301+
// we will adjust the offset in the byte_update call below.
302+
if(is_first_cell)
303+
stored_value_offset=zero_offset;
304+
else if(is_last_cell)
305+
stored_value_offset=
306+
from_integer(element_size-sub_size, offset_type);
307+
else
308+
stored_value_offset=minus_exprt(cell_offset, src.op1());
309+
310+
auto extract_opcode=
311+
src.id()==ID_byte_update_little_endian ?
312+
ID_byte_extract_little_endian :
313+
src.id()==ID_byte_update_big_endian ?
314+
ID_byte_extract_big_endian :
315+
throw "unexpected src.id() in flatten_byte_update";
278316
byte_extract_exprt byte_extract_expr(
279-
src.id()==ID_byte_update_little_endian?ID_byte_extract_little_endian:
280-
src.id()==ID_byte_update_big_endian?ID_byte_extract_big_endian:
281-
throw "unexpected src.id() in flatten_byte_update",
282-
array_type.subtype());
317+
extract_opcode,
318+
element_size<sub_size ? src.op2().type() : subtype);
283319

284320
byte_extract_expr.op()=src.op2();
285-
byte_extract_expr.offset()=i_expr;
321+
byte_extract_expr.offset()=stored_value_offset;
286322

287-
new_value=byte_extract_expr;
323+
new_value=flatten_byte_extract(byte_extract_expr, ns);
288324
}
289325

290-
div_exprt div_offset(src.op1(), from_integer(sub_size, src.op1().type()));
291-
mod_exprt mod_offset(src.op1(), from_integer(sub_size, src.op1().type()));
292-
293-
index_exprt index_expr(src.op0(), div_offset, array_type.subtype());
326+
// Where does the value we just extracted align in this cell?
327+
// This value might be negative, indicating only the most-significant
328+
// bytes should be written, to the least-significant bytes of the
329+
// target array cell.
330+
exprt overwrite_offset;
331+
if(is_first_cell)
332+
overwrite_offset=mod_exprt(src.op1(), sub_size_expr);
333+
else if(is_last_cell)
334+
{
335+
// This is intentionally negated; passing is_last_cell
336+
// to flatten below leads to it being negated again later.
337+
overwrite_offset=
338+
minus_exprt(
339+
minus_exprt(cell_offset, src.op1()),
340+
stored_value_offset);
341+
}
342+
else
343+
overwrite_offset=zero_offset;
294344

295-
byte_update_exprt byte_update_expr(src.id(), array_type.subtype());
296-
byte_update_expr.op()=index_expr;
297-
byte_update_expr.offset()=mod_offset;
298-
byte_update_expr.value()=new_value;
345+
byte_update_exprt byte_update_expr(
346+
src.id(),
347+
old_cell_value,
348+
overwrite_offset,
349+
new_value);
299350

300-
// Call recurisvely, the array is gone!
351+
// Call recursively, the array is gone!
352+
// The last parameter indicates that the
301353
exprt flattened_byte_update_expr=
302-
flatten_byte_update(byte_update_expr, ns);
354+
flatten_byte_update(byte_update_expr, ns, is_last_cell);
303355

304356
with_exprt with_expr(
305-
result, div_offset, flattened_byte_update_expr);
357+
result, cell_index, flattened_byte_update_expr);
306358

307359
result=with_expr;
308360
}
@@ -332,17 +384,7 @@ exprt flatten_byte_update(
332384

333385
// build mask
334386
exprt mask=
335-
bitnot_exprt(
336-
from_integer(power(2, element_size*8)-1, unsignedbv_typet(width)));
337-
338-
const typet &offset_type=ns.follow(src.op1().type());
339-
mult_exprt offset_times_eight(src.op1(), from_integer(8, offset_type));
340-
341-
// shift the mask
342-
shl_exprt shl_expr(mask, offset_times_eight);
343-
344-
// do the 'AND'
345-
bitand_exprt bitand_expr(src.op0(), mask);
387+
from_integer(power(2, element_size*8)-1, unsignedbv_typet(width));
346388

347389
// zero-extend the value, but only if needed
348390
exprt value_extended;
@@ -354,10 +396,35 @@ exprt flatten_byte_update(
354396
else
355397
value_extended=src.op2();
356398

357-
// shift the value
358-
shl_exprt value_shifted(value_extended, offset_times_eight);
399+
const typet &offset_type=ns.follow(src.op1().type());
400+
mult_exprt offset_times_eight(src.op1(), from_integer(8, offset_type));
401+
402+
binary_predicate_exprt offset_ge_zero(
403+
offset_times_eight,
404+
ID_ge,
405+
from_integer(0, offset_type));
406+
407+
// Shift the mask and value.
408+
// Note either shift might discard some of the new value's bits.
409+
exprt mask_shifted;
410+
exprt value_shifted;
411+
if(negative_offset)
412+
{
413+
// In this case we shift right, to mask out higher addresses
414+
// rather than left to mask out lower ones as usual.
415+
mask_shifted=lshr_exprt(mask, offset_times_eight);
416+
value_shifted=lshr_exprt(value_extended, offset_times_eight);
417+
}
418+
else
419+
{
420+
mask_shifted=shl_exprt(mask, offset_times_eight);
421+
value_shifted=shl_exprt(value_extended, offset_times_eight);
422+
}
423+
424+
// original_bits &= ~mask
425+
bitand_exprt bitand_expr(src.op0(), bitnot_exprt(mask_shifted));
359426

360-
// do the 'OR'
427+
// original_bits |= newvalue
361428
bitor_exprt bitor_expr(bitand_expr, value_shifted);
362429

363430
return bitor_expr;

src/solvers/flattening/flatten_byte_operators.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ exprt flatten_byte_extract(
2020

2121
exprt flatten_byte_update(
2222
const byte_update_exprt &src,
23-
const namespacet &ns);
23+
const namespacet &ns,
24+
bool negative_offset=false);
2425

2526
exprt flatten_byte_operators(const exprt &src, const namespacet &ns);
2627

0 commit comments

Comments
 (0)