-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathvolatile-in-PG_TRY.ql
More file actions
102 lines (93 loc) · 3.52 KB
/
volatile-in-PG_TRY.ql
File metadata and controls
102 lines (93 loc) · 3.52 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
/**
* @name Find missing volatile qualifier in PG_TRY()
* @description Flags locals modified in PG_TRY and read in PG_CATCH without
* `volatile`, except when the catch block's first use of the
* variable is a plain `=` LHS and the RHS does not read that
* variable (safe re-init heuristic below).
* @kind problem
* @problem.severity warning
* @id cpp/example/missing-volatile-in-pg-try
*/
import cpp
import lib.pg_try_catch
/*
* False-positive filter: a variable may be written in PG_TRY and appear in
* PG_CATCH, but the catch block only assigns a fresh value before any read
* (e.g. `ct = lfirst(...)` then uses `ct`), so the catch path does not depend
* on the longjmp-clobbered value from the try block.
*/
/** True if `va` lies in the catch block's source span (fast vs `getEnclosingElement*`). */
predicate varAccessUnderCatch(Stmt catchBlock, VariableAccess va) {
catchBlock.getLocation().subsumes(va.getLocation())
}
/** Lexicographic source order within one file (line, then column). */
predicate lexBefore(VariableAccess a, VariableAccess b) {
exists(Location la, Location lb |
la = a.getLocation() and
lb = b.getLocation() and
la.getFile() = lb.getFile() and
(
la.getStartLine() < lb.getStartLine()
or
la.getStartLine() = lb.getStartLine() and
la.getStartColumn() < lb.getStartColumn()
)
)
}
/**
* `first` is the `VariableAccess` to `var` in `catchBlock` with minimum
* (line, column) among all such accesses in that block.
*/
predicate isLexicographicallyFirstVarAccessInCatch(
Stmt catchBlock, LocalVariable var, VariableAccess first
) {
first.getTarget() = var and
varAccessUnderCatch(catchBlock, first) and
not exists(VariableAccess other |
other.getTarget() = var and
other != first and
varAccessUnderCatch(catchBlock, other) and
lexBefore(other, first)
)
}
/**
* Holds when the first `VariableAccess` to `var` in the catch block is the LHS
* of a plain `=` and the RHS does not read `var` (so no `ct = f(ct)` case).
*/
predicate catchReinitsVarWithPlainAssignBeforeAnyRead(Stmt catchBlock, LocalVariable var) {
exists(VariableAccess first, AssignExpr ae |
isLexicographicallyFirstVarAccessInCatch(catchBlock, var, first) and
ae.getLValue() = first and
not exists(VariableAccess rhs |
rhs.getTarget() = var and
rhs != first and
rhs.getEnclosingElement*() = ae.getRValue()
)
)
}
predicate varMaybeModified(Stmt stmt, VariableAccess va) {
va = stmt.getAChild*().(Assignment).getLValue().(VariableAccess)
}
predicate varReadAccess(Stmt stmt, VariableAccess va) {
(
va = stmt.getAChild*().(FunctionCall).getAnArgument().(VariableAccess) and
va.isRValue()
or
va = stmt.getAChild*().(Assignment).getRValue().(VariableAccess)
)
}
from Stmt tryBlock, Stmt catchBlock, LocalVariable var, VariableAccess va1, VariableAccess va2
where
pgTryCatchBlocks(tryBlock, catchBlock) and
va1 = var.getAnAccess() and
varMaybeModified(tryBlock, va1) and
va2 = var.getAnAccess() and
varReadAccess(catchBlock, va2) and
not var.isVolatile() and
/* Catch block re-assigns before any read; no stale value from PG_TRY. */
not catchReinitsVarWithPlainAssignBeforeAnyRead(catchBlock, var)
select var,
"Object being modified in the PG_TRY() (" + va1.getLocation().getStartLine() + "," +
va1.getLocation().getStartColumn() + ") block and read in the PG_CATCH() (" +
va2.getLocation().getStartLine() + "," + va2.getLocation().getStartColumn() +
") block should be qualified with volatile"