Skip to content

Commit ef751d0

Browse files
Warn about bitwise shift with negative shift amount (UB in C) (#1989)
* Initial plan * Add warnings for bitwise shift with negative shift amount (UB in C) Agent-Logs-Url: https://github.com/goblint/analyzer/sessions/c81d432d-7783-4469-9c48-85d6bb3d1531 Co-authored-by: michael-schwarz <13812333+michael-schwarz@users.noreply.github.com> * Flatten nested match in shift_amount_negcheck for readability Agent-Logs-Url: https://github.com/goblint/analyzer/sessions/c81d432d-7783-4469-9c48-85d6bb3d1531 Co-authored-by: michael-schwarz <13812333+michael-schwarz@users.noreply.github.com> * Use Behavior.Undefined.other category for shift-by-negative warnings Agent-Logs-Url: https://github.com/goblint/analyzer/sessions/c81d432d-7783-4469-9c48-85d6bb3d1531 Co-authored-by: michael-schwarz <13812333+michael-schwarz@users.noreply.github.com> * Refactor shift neg check to deduplicate code; fix test to use unsigned int Agent-Logs-Url: https://github.com/goblint/analyzer/sessions/1dec7b36-67b9-4835-abab-afb1ff9c852e Co-authored-by: michael-schwarz <13812333+michael-schwarz@users.noreply.github.com> * Fix test: use bounded non-negative interval [0,5] instead of unsigned int top Agent-Logs-Url: https://github.com/goblint/analyzer/sessions/daf39d5f-96e8-4887-9ade-7004d5fcc994 Co-authored-by: michael-schwarz <13812333+michael-schwarz@users.noreply.github.com> * Use ID.ge/ID.lt instead of ID.minimal/ID.maximal in check_shift_neg Agent-Logs-Url: https://github.com/goblint/analyzer/sessions/fd400683-08ae-4a0a-b4ff-458d87ba80f8 Co-authored-by: michael-schwarz <13812333+michael-schwarz@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: michael-schwarz <13812333+michael-schwarz@users.noreply.github.com> Co-authored-by: Michael Schwarz <michael.schwarz93@gmail.com>
1 parent 63a8c09 commit ef751d0

2 files changed

Lines changed: 62 additions & 3 deletions

File tree

src/analyses/base.ml

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,23 @@ struct
223223
| Bot -> Bot
224224
| _ -> VD.top ()
225225

226-
let binop_ID (result_ik: Cil.ikind) = function
226+
let binop_ID (result_ik: Cil.ikind) =
227+
(* Check the shift amount for negative values (undefined behavior in C), emitting
228+
appropriate warnings/errors based on bounds analysis. *)
229+
let check_shift_neg dir y =
230+
let ik = ID.ikind y in
231+
let zero = ID.of_int ik Z.zero in
232+
match ID.ge y zero, ID.lt y zero with
233+
| Some true, _ ->
234+
Checks.safe Checks.Category.InvalidShift
235+
| _, Some true ->
236+
M.error ~category:M.Category.Behavior.Undefined.other ~tags:[CWE 758] "Shift-%s by negative amount is undefined behavior" dir;
237+
Checks.error Checks.Category.InvalidShift "Shift-%s by negative amount is undefined behavior" dir
238+
| _ ->
239+
M.warn ~category:M.Category.Behavior.Undefined.other ~tags:[CWE 758] "Shift-%s by possibly negative amount may be undefined behavior" dir;
240+
Checks.warn Checks.Category.InvalidShift "Shift-%s by possibly negative amount may be undefined behavior" dir
241+
in
242+
function
227243
| PlusA -> ID.add
228244
| MinusA -> ID.sub
229245
| Mult -> ID.mul
@@ -265,8 +281,14 @@ struct
265281
| BAnd -> ID.logand
266282
| BOr -> ID.logor
267283
| BXor -> ID.logxor
268-
| Shiftlt -> ID.shift_left
269-
| Shiftrt -> ID.shift_right
284+
| Shiftlt ->
285+
fun x y ->
286+
check_shift_neg "left" y;
287+
ID.shift_left x y
288+
| Shiftrt ->
289+
fun x y ->
290+
check_shift_neg "right" y;
291+
ID.shift_right x y
270292
| LAnd -> id_binary_log (&&) ~annihilator:false result_ik
271293
| LOr -> id_binary_log (||) ~annihilator:true result_ik
272294
| b -> (fun x y -> (ID.top_of result_ik))
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// PARAM: --enable ana.int.interval
2+
#include <goblint.h>
3+
4+
int main() {
5+
int res;
6+
int top;
7+
int neg;
8+
9+
// Definitely negative shift amount: error
10+
res = 8 << -2; //WARN
11+
12+
// Definitely negative shift amount in right shift: error
13+
res = 8 >> -1; //WARN
14+
15+
// Non-negative shift amount: no warning
16+
res = 8 << 2; //NOWARN
17+
res = 8 >> 2; //NOWARN
18+
19+
// Zero shift amount: no warning
20+
res = 8 << 0; //NOWARN
21+
res = 8 >> 0; //NOWARN
22+
23+
// Possibly negative shift amount: warn
24+
// (top is uninitialized, representing a non-deterministic value — a common convention in goblint tests)
25+
if (top) { neg = -1; } else { neg = 1; }
26+
res = 8 << neg; //WARN
27+
res = 8 >> neg; //WARN
28+
29+
// Provably non-negative, bounded interval: no warning
30+
// (pos_shift is [0,5] — non-negative and within valid shift range)
31+
int pos_shift = 0;
32+
if (top) { pos_shift = 5; }
33+
res = 8 << pos_shift; //NOWARN
34+
res = 8 >> pos_shift; //NOWARN
35+
36+
return 0;
37+
}

0 commit comments

Comments
 (0)