@@ -18,7 +18,7 @@ use cbmc::MachineModel;
18
18
use cbmc:: { btree_string_map, InternString , InternedString } ;
19
19
use num:: bigint:: BigInt ;
20
20
use rustc_abi:: FieldIdx ;
21
- use rustc_index:: vec :: IndexVec ;
21
+ use rustc_index:: IndexVec ;
22
22
use rustc_middle:: mir:: { AggregateKind , BinOp , CastKind , NullOp , Operand , Place , Rvalue , UnOp } ;
23
23
use rustc_middle:: ty:: adjustment:: PointerCast ;
24
24
use rustc_middle:: ty:: layout:: LayoutOf ;
@@ -294,6 +294,7 @@ impl<'tcx> GotocCtx<'tcx> {
294
294
295
295
fn codegen_rvalue_binary_op (
296
296
& mut self ,
297
+ ty : Ty < ' tcx > ,
297
298
op : & BinOp ,
298
299
e1 : & Operand < ' tcx > ,
299
300
e2 : & Operand < ' tcx > ,
@@ -317,7 +318,32 @@ impl<'tcx> GotocCtx<'tcx> {
317
318
BinOp :: Offset => {
318
319
let ce1 = self . codegen_operand ( e1) ;
319
320
let ce2 = self . codegen_operand ( e2) ;
320
- ce1. plus ( ce2)
321
+
322
+ // Check that computing `offset` in bytes would not overflow
323
+ let ( offset_bytes, bytes_overflow_check) = self . count_in_bytes (
324
+ ce2. clone ( ) . cast_to ( Type :: ssize_t ( ) ) ,
325
+ ty,
326
+ Type :: ssize_t ( ) ,
327
+ "offset" ,
328
+ loc,
329
+ ) ;
330
+
331
+ // Check that the computation would not overflow an `isize` which is UB:
332
+ // https://doc.rust-lang.org/std/primitive.pointer.html#method.offset
333
+ // These checks may allow a wrapping-around behavior in CBMC:
334
+ // https://github.com/model-checking/kani/issues/1150
335
+ let overflow_res = ce1. clone ( ) . cast_to ( Type :: ssize_t ( ) ) . add_overflow ( offset_bytes) ;
336
+ let overflow_check = self . codegen_assert_assume (
337
+ overflow_res. overflowed . not ( ) ,
338
+ PropertyClass :: ArithmeticOverflow ,
339
+ "attempt to compute offset which would overflow" ,
340
+ loc,
341
+ ) ;
342
+ let res = ce1. clone ( ) . plus ( ce2) ;
343
+ Expr :: statement_expression (
344
+ vec ! [ bytes_overflow_check, overflow_check, res. as_stmt( loc) ] ,
345
+ ce1. typ ( ) . clone ( ) ,
346
+ )
321
347
}
322
348
}
323
349
}
@@ -548,7 +574,7 @@ impl<'tcx> GotocCtx<'tcx> {
548
574
self . codegen_operand ( operand) . transmute_to ( goto_typ, & self . symbol_table )
549
575
}
550
576
Rvalue :: BinaryOp ( op, box ( ref e1, ref e2) ) => {
551
- self . codegen_rvalue_binary_op ( op, e1, e2, loc)
577
+ self . codegen_rvalue_binary_op ( res_ty , op, e1, e2, loc)
552
578
}
553
579
Rvalue :: CheckedBinaryOp ( op, box ( ref e1, ref e2) ) => {
554
580
self . codegen_rvalue_checked_binary_op ( op, e1, e2, res_ty)
@@ -560,6 +586,10 @@ impl<'tcx> GotocCtx<'tcx> {
560
586
NullOp :: SizeOf => Expr :: int_constant ( layout. size . bytes_usize ( ) , Type :: size_t ( ) )
561
587
. with_size_of_annotation ( self . codegen_ty ( t) ) ,
562
588
NullOp :: AlignOf => Expr :: int_constant ( layout. align . abi . bytes ( ) , Type :: size_t ( ) ) ,
589
+ NullOp :: OffsetOf ( fields) => Expr :: int_constant (
590
+ layout. offset_of_subfield ( self , fields. iter ( ) . map ( |f| f. index ( ) ) ) . bytes ( ) ,
591
+ Type :: size_t ( ) ,
592
+ ) ,
563
593
}
564
594
}
565
595
Rvalue :: ShallowInitBox ( ref operand, content_ty) => {
0 commit comments