Skip to content

Commit 14db788

Browse files
committed
C++: Ensure that 'x' in 'if(!x) is also an AST-based GuardCondition.
1 parent b5897e5 commit 14db788

File tree

1 file changed

+120
-15
lines changed

1 file changed

+120
-15
lines changed

cpp/ql/lib/semmle/code/cpp/controlflow/IRGuards.qll

Lines changed: 120 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,125 @@ private class GuardConditionFromBinaryLogicalOperator extends GuardConditionImpl
233233
}
234234
}
235235

236+
/**
237+
* Holds if `ir` controls `block`, meaning that `block` is only
238+
* entered if the value of this condition is `v`. This helper
239+
* predicate does not necessarily hold for binary logical operations like
240+
* `&&` and `||`. See the detailed explanation on predicate `controls`.
241+
*/
242+
private predicate controlsBlock(IRGuardCondition ir, BasicBlock controlled, AbstractValue v) {
243+
exists(IRBlock irb |
244+
ir.valueControls(irb, v) and
245+
nonExcludedIRAndBasicBlock(irb, controlled) and
246+
not isUnreachedBlock(irb)
247+
)
248+
}
249+
250+
private class GuardConditionFromNotExpr extends GuardConditionImpl {
251+
IRGuardCondition ir;
252+
253+
GuardConditionFromNotExpr() {
254+
// When `!` is applied to an integer (such as `x`) the generated IR looks
255+
// like:
256+
// ```
257+
// r1(glval<int>) = VariableAddress[myInt] :
258+
// r2(int) = Load[x] : &:r1, m1_6
259+
// r3(int) = Constant[0] :
260+
// r4(bool) = CompareEQ : r2, r3
261+
// ```
262+
// And so the `IRGuardCondition` for an expression such as `if(!x)` is the
263+
// `CompareEQ` instruction. However, users often expect the `x` to also
264+
// be a guard condition. But from the perspective of the IR the `x` is just
265+
// the left-hand side of a comparison against 0 so it's not included as a
266+
// normal `IRGuardCondition`. So to align with user expectations we make
267+
// that `x` a `GuardCondition`.
268+
exists(NotExpr notExpr, Type t |
269+
this = notExpr.getOperand() and
270+
t = this.getUnspecifiedType() and
271+
not t instanceof BoolType and
272+
ir.getUnconvertedResultExpression() = notExpr
273+
)
274+
}
275+
276+
override predicate valueControls(BasicBlock controlled, AbstractValue v) {
277+
// This condition must determine the flow of control; that is, this
278+
// node must be a top-level condition.
279+
controlsBlock(ir, controlled, v.getDualValue())
280+
}
281+
282+
pragma[inline]
283+
override predicate comparesLt(Expr left, Expr right, int k, boolean isLessThan, boolean testIsTrue) {
284+
exists(Instruction li, Instruction ri |
285+
li.getUnconvertedResultExpression() = left and
286+
ri.getUnconvertedResultExpression() = right and
287+
ir.comparesLt(li.getAUse(), ri.getAUse(), k, isLessThan, testIsTrue.booleanNot())
288+
)
289+
}
290+
291+
pragma[inline]
292+
override predicate comparesLt(Expr e, int k, boolean isLessThan, AbstractValue value) {
293+
exists(Instruction i |
294+
i.getUnconvertedResultExpression() = e and
295+
ir.comparesLt(i.getAUse(), k, isLessThan, value.getDualValue())
296+
)
297+
}
298+
299+
pragma[inline]
300+
override predicate ensuresLt(Expr left, Expr right, int k, BasicBlock block, boolean isLessThan) {
301+
exists(Instruction li, Instruction ri, boolean testIsTrue |
302+
li.getUnconvertedResultExpression() = left and
303+
ri.getUnconvertedResultExpression() = right and
304+
ir.comparesLt(li.getAUse(), ri.getAUse(), k, isLessThan, testIsTrue.booleanNot()) and
305+
this.controls(block, testIsTrue)
306+
)
307+
}
308+
309+
pragma[inline]
310+
override predicate ensuresLt(Expr e, int k, BasicBlock block, boolean isLessThan) {
311+
exists(Instruction i, AbstractValue value |
312+
i.getUnconvertedResultExpression() = e and
313+
ir.comparesLt(i.getAUse(), k, isLessThan, value.getDualValue()) and
314+
this.valueControls(block, value)
315+
)
316+
}
317+
318+
pragma[inline]
319+
override predicate comparesEq(Expr left, Expr right, int k, boolean areEqual, boolean testIsTrue) {
320+
exists(Instruction li, Instruction ri |
321+
li.getUnconvertedResultExpression() = left and
322+
ri.getUnconvertedResultExpression() = right and
323+
ir.comparesEq(li.getAUse(), ri.getAUse(), k, areEqual, testIsTrue.booleanNot())
324+
)
325+
}
326+
327+
pragma[inline]
328+
override predicate ensuresEq(Expr left, Expr right, int k, BasicBlock block, boolean areEqual) {
329+
exists(Instruction li, Instruction ri, boolean testIsTrue |
330+
li.getUnconvertedResultExpression() = left and
331+
ri.getUnconvertedResultExpression() = right and
332+
ir.comparesEq(li.getAUse(), ri.getAUse(), k, areEqual, testIsTrue.booleanNot()) and
333+
this.controls(block, testIsTrue)
334+
)
335+
}
336+
337+
pragma[inline]
338+
override predicate comparesEq(Expr e, int k, boolean areEqual, AbstractValue value) {
339+
exists(Instruction i |
340+
i.getUnconvertedResultExpression() = e and
341+
ir.comparesEq(i.getAUse(), k, areEqual, value.getDualValue())
342+
)
343+
}
344+
345+
pragma[inline]
346+
override predicate ensuresEq(Expr e, int k, BasicBlock block, boolean areEqual) {
347+
exists(Instruction i, AbstractValue value |
348+
i.getUnconvertedResultExpression() = e and
349+
ir.comparesEq(i.getAUse(), k, areEqual, value.getDualValue()) and
350+
this.valueControls(block, value)
351+
)
352+
}
353+
}
354+
236355
/**
237356
* A Boolean condition in the AST that guards one or more basic blocks and has a corresponding IR
238357
* instruction.
@@ -245,7 +364,7 @@ private class GuardConditionFromIR extends GuardConditionImpl {
245364
override predicate valueControls(BasicBlock controlled, AbstractValue v) {
246365
// This condition must determine the flow of control; that is, this
247366
// node must be a top-level condition.
248-
this.controlsBlock(controlled, v)
367+
controlsBlock(ir, controlled, v)
249368
}
250369

251370
pragma[inline]
@@ -319,20 +438,6 @@ private class GuardConditionFromIR extends GuardConditionImpl {
319438
this.valueControls(block, value)
320439
)
321440
}
322-
323-
/**
324-
* Holds if this condition controls `block`, meaning that `block` is only
325-
* entered if the value of this condition is `v`. This helper
326-
* predicate does not necessarily hold for binary logical operations like
327-
* `&&` and `||`. See the detailed explanation on predicate `controls`.
328-
*/
329-
private predicate controlsBlock(BasicBlock controlled, AbstractValue v) {
330-
exists(IRBlock irb |
331-
ir.valueControls(irb, v) and
332-
nonExcludedIRAndBasicBlock(irb, controlled) and
333-
not isUnreachedBlock(irb)
334-
)
335-
}
336441
}
337442

338443
private predicate excludeAsControlledInstruction(Instruction instr) {

0 commit comments

Comments
 (0)