Skip to content

Commit c5950d2

Browse files
committed
C++: IR: Result of x in x++ is now the Load
Previously, the `Load` would be associated with the `CrementOperation` rather than its operand, which gave surprising results when mapping taint sinks back to `Expr`. The changes in `raw_ir.expected` are to add `Copy` operations on the `x++` in code like `y = x++`. This is now needed because the result that `x++` would otherwise have (the Load) no longer belongs to the `++` expression. Copies are inserted to ensure that all expressions are associated with an `Instruction` result. The changes in `*aliased_ssa_ir.expected` appear to be just wobble.
1 parent ed3ed5f commit c5950d2

9 files changed

Lines changed: 137 additions & 115 deletions

File tree

cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/InstructionTag.qll

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ newtype TInstructionTag =
1717
AssignOperationOpTag() or
1818
AssignOperationConvertResultTag() or
1919
AssignmentStoreTag() or
20-
CrementLoadTag() or
2120
CrementConstantTag() or
2221
CrementOpTag() or
2322
CrementStoreTag() or
@@ -104,8 +103,6 @@ string getInstructionTagId(TInstructionTag tag) {
104103
or
105104
tag = AssignmentStoreTag() and result = "AssignStore"
106105
or
107-
tag = CrementLoadTag() and result = "CrementLoad"
108-
or
109106
tag = CrementConstantTag() and result = "CrementConst"
110107
or
111108
tag = CrementOpTag() and result = "CrementOp"

cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedElement.qll

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ private predicate usedAsCondition(Expr expr) {
208208
* AST as an lvalue-to-rvalue conversion, but the IR represents both a function
209209
* lvalue and a function pointer prvalue the same.
210210
*/
211-
predicate ignoreLoad(Expr expr) {
211+
private predicate ignoreLoad(Expr expr) {
212212
expr.hasLValueToRValueConversion() and
213213
(
214214
expr instanceof ThisExpr or
@@ -220,6 +220,32 @@ predicate ignoreLoad(Expr expr) {
220220
)
221221
}
222222

223+
/**
224+
* Holds if `expr` should have a load on it because it will be loaded as part
225+
* of the translation of its parent. We want to associate this load with `expr`
226+
* itself rather than its parent since in practical applications like data flow
227+
* we maintain that the value of the `x` in `x++` should be what's loaded from
228+
* `x`.
229+
*/
230+
private predicate needsLoadForParentExpr(Expr expr) {
231+
exists(CrementOperation crement | expr = crement.getOperand().getFullyConverted())
232+
}
233+
234+
/**
235+
* Holds if `expr` should have a `TranslatedLoad` on it.
236+
*/
237+
predicate hasTranslatedLoad(Expr expr) {
238+
(
239+
expr.hasLValueToRValueConversion()
240+
or
241+
needsLoadForParentExpr(expr)
242+
) and
243+
not ignoreExpr(expr) and
244+
not isNativeCondition(expr) and
245+
not isFlexibleCondition(expr) and
246+
not ignoreLoad(expr)
247+
}
248+
223249
newtype TTranslatedElement =
224250
// An expression that is not being consumed as a condition
225251
TTranslatedValueExpr(Expr expr) {
@@ -229,21 +255,12 @@ newtype TTranslatedElement =
229255
} or
230256
// A separate element to handle the lvalue-to-rvalue conversion step of an
231257
// expression.
232-
TTranslatedLoad(Expr expr) {
233-
not ignoreExpr(expr) and
234-
not isNativeCondition(expr) and
235-
not isFlexibleCondition(expr) and
236-
expr.hasLValueToRValueConversion() and
237-
not ignoreLoad(expr)
238-
} or
258+
TTranslatedLoad(Expr expr) { hasTranslatedLoad(expr) } or
259+
// For expressions that would not otherwise generate an instruction.
239260
TTranslatedResultCopy(Expr expr) {
240261
not ignoreExpr(expr) and
241262
exprNeedsCopyIfNotLoaded(expr) and
242-
// Doesn't have a TTranslatedLoad
243-
not (
244-
expr.hasLValueToRValueConversion() and
245-
not ignoreLoad(expr)
246-
)
263+
not hasTranslatedLoad(expr)
247264
} or
248265
// An expression most naturally translated as control flow.
249266
TTranslatedNativeCondition(Expr expr) {

cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedExpr.qll

Lines changed: 34 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,12 @@ abstract class TranslatedExpr extends TranslatedElement {
6363
* Holds if the result of this `TranslatedExpr` is a glvalue.
6464
*/
6565
predicate isResultGLValue() {
66-
// This implementation is overridden in `TranslatedCoreExpr` to mark them
67-
// as glvalues if they have loads on them. It's not overridden in
68-
// `TranslatedResultCopy` since result copies never have loads.
66+
// This implementation is overridden in `TranslatedCoreExpr` to mark them as
67+
// glvalues if they have loads on them. It's also overridden in
68+
// `TranslatedLoad` to always mark loads as glvalues since a
69+
// `TranslatedLoad` may have been created as a result of
70+
// `needsLoadForParentExpr`. It's not overridden in `TranslatedResultCopy`
71+
// since result copies never have loads.
6972
expr.isGLValueCategory()
7073
}
7174

@@ -103,18 +106,13 @@ abstract class TranslatedCoreExpr extends TranslatedExpr {
103106
or
104107
// If this TranslatedExpr doesn't produce the result, then it must represent
105108
// a glvalue that is then loaded by a TranslatedLoad.
106-
hasLoad()
107-
}
108-
109-
final predicate hasLoad() {
110-
expr.hasLValueToRValueConversion() and
111-
not ignoreLoad(expr)
109+
hasTranslatedLoad(expr)
112110
}
113111

114112
final override predicate producesExprResult() {
115113
// If there's no load, then this is the only TranslatedExpr for this
116114
// expression.
117-
not hasLoad() and
115+
not hasTranslatedLoad(expr) and
118116
// If there's a result copy, then this expression's result is the copy.
119117
not exprNeedsCopyIfNotLoaded(expr)
120118
}
@@ -270,6 +268,8 @@ class TranslatedLoad extends TranslatedExpr, TTranslatedLoad {
270268
resultType = getResultType()
271269
}
272270

271+
override predicate isResultGLValue() { none() }
272+
273273
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
274274
tag = LoadTag() and
275275
result = getParent().getChildSuccessor(this) and
@@ -298,7 +298,7 @@ class TranslatedLoad extends TranslatedExpr, TTranslatedLoad {
298298
any()
299299
}
300300

301-
private TranslatedCoreExpr getOperand() { result.getExpr() = expr }
301+
TranslatedCoreExpr getOperand() { result.getExpr() = expr }
302302
}
303303

304304
/**
@@ -387,7 +387,7 @@ private int getElementSize(Type type) {
387387
abstract class TranslatedCrementOperation extends TranslatedNonConstantExpr {
388388
override CrementOperation expr;
389389

390-
final override TranslatedElement getChild(int id) { id = 0 and result = getOperand() }
390+
final override TranslatedElement getChild(int id) { id = 0 and result = getLoadedOperand() }
391391

392392
final override string getInstructionConstantValue(InstructionTag tag) {
393393
tag = CrementConstantTag() and
@@ -416,10 +416,6 @@ abstract class TranslatedCrementOperation extends TranslatedNonConstantExpr {
416416
}
417417

418418
final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
419-
tag = CrementLoadTag() and
420-
opcode instanceof Opcode::Load and
421-
resultType = getTypeForPRValue(expr.getType())
422-
or
423419
tag = CrementConstantTag() and
424420
opcode instanceof Opcode::Constant and
425421
resultType = getConstantType()
@@ -434,19 +430,10 @@ abstract class TranslatedCrementOperation extends TranslatedNonConstantExpr {
434430
}
435431

436432
final override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
437-
tag = CrementLoadTag() and
438-
(
439-
operandTag instanceof AddressOperandTag and
440-
result = getOperand().getResult()
441-
or
442-
operandTag instanceof LoadOperandTag and
443-
result = getEnclosingFunction().getUnmodeledDefinitionInstruction()
444-
)
445-
or
446433
tag = CrementOpTag() and
447434
(
448435
operandTag instanceof LeftOperandTag and
449-
result = getInstruction(CrementLoadTag())
436+
result = getLoadedOperand().getResult()
450437
or
451438
operandTag instanceof RightOperandTag and
452439
result = getInstruction(CrementConstantTag())
@@ -455,21 +442,20 @@ abstract class TranslatedCrementOperation extends TranslatedNonConstantExpr {
455442
tag = CrementStoreTag() and
456443
(
457444
operandTag instanceof AddressOperandTag and
458-
result = getOperand().getResult()
445+
result = getUnloadedOperand().getResult()
459446
or
460447
operandTag instanceof StoreValueOperandTag and
461448
result = getInstruction(CrementOpTag())
462449
)
463450
}
464451

465-
final override Instruction getFirstInstruction() { result = getOperand().getFirstInstruction() }
452+
final override Instruction getFirstInstruction() {
453+
result = getLoadedOperand().getFirstInstruction()
454+
}
466455

467456
final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
468457
kind instanceof GotoEdge and
469458
(
470-
tag = CrementLoadTag() and
471-
result = getInstruction(CrementConstantTag())
472-
or
473459
tag = CrementConstantTag() and
474460
result = getInstruction(CrementOpTag())
475461
or
@@ -482,7 +468,7 @@ abstract class TranslatedCrementOperation extends TranslatedNonConstantExpr {
482468
}
483469

484470
final override Instruction getChildSuccessor(TranslatedElement child) {
485-
child = getOperand() and result = getInstruction(CrementLoadTag())
471+
child = getLoadedOperand() and result = getInstruction(CrementConstantTag())
486472
}
487473

488474
final override int getInstructionElementSize(InstructionTag tag) {
@@ -494,10 +480,20 @@ abstract class TranslatedCrementOperation extends TranslatedNonConstantExpr {
494480
result = getElementSize(expr.getType())
495481
}
496482

497-
final TranslatedExpr getOperand() {
483+
/**
484+
* Gets the `TranslatedLoad` on the `e` in this `e++`, which is the element
485+
* that holds the value to be cremented. It's guaranteed that there's a load
486+
* on `e` because of the `needsLoadForParentExpr` predicate.
487+
*/
488+
final TranslatedLoad getLoadedOperand() {
498489
result = getTranslatedExpr(expr.getOperand().getFullyConverted())
499490
}
500491

492+
/**
493+
* Gets the address to which the result of this crement will be stored.
494+
*/
495+
final TranslatedExpr getUnloadedOperand() { result = getLoadedOperand().getOperand() }
496+
501497
final Opcode getOpcode() {
502498
exists(Type resultType |
503499
resultType = expr.getUnspecifiedType() and
@@ -534,17 +530,14 @@ class TranslatedPrefixCrementOperation extends TranslatedCrementOperation {
534530
else
535531
// This is C++, where the result is an lvalue for the operand, and that
536532
// lvalue is not being loaded as part of this expression.
537-
result = getOperand().getResult()
533+
result = getUnloadedOperand().getResult()
538534
}
539535
}
540536

541537
class TranslatedPostfixCrementOperation extends TranslatedCrementOperation {
542538
override PostfixCrementOperation expr;
543539

544-
override Instruction getResult() {
545-
// The result is a prvalue copy of the original value
546-
result = getInstruction(CrementLoadTag())
547-
}
540+
override Instruction getResult() { result = getLoadedOperand().getResult() }
548541
}
549542

550543
/**
@@ -2476,6 +2469,9 @@ predicate exprNeedsCopyIfNotLoaded(Expr expr) {
24762469
expr instanceof PrefixCrementOperation and
24772470
not expr.isPRValueCategory() // is C++
24782471
or
2472+
// Because the load is on the `e` in `e++`.
2473+
expr instanceof PostfixCrementOperation
2474+
or
24792475
expr instanceof PointerDereferenceExpr
24802476
or
24812477
expr instanceof AddressOfExpr

cpp/ql/test/library-tests/dataflow/crement/crements.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
void test_crement() {
22
int x1 = 0;
3-
++x1; // flow [NOT DETECTED]
3+
++x1;
44

55
int x2 = 0;
6-
x2++; // flow [NOT DETECTED]
6+
x2++;
77

88
int x3 = 0;
99
x3 -= 1; // flow [NOT DETECTED]
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
1+
| crements.cpp:3:5:3:6 | x1 |
2+
| crements.cpp:6:3:6:4 | x2 |
13
| crements.cpp:15:8:15:9 | x5 |

cpp/ql/test/library-tests/ir/ir/raw_ir.expected

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -509,15 +509,17 @@ ir.cpp:
509509
# 103| r103_3(int) = Constant[1] :
510510
# 103| r103_4(int) = Add : r103_2, r103_3
511511
# 103| mu103_5(int) = Store : &:r103_1, r103_4
512-
# 103| r103_6(glval<int>) = VariableAddress[y] :
513-
# 103| mu103_7(int) = Store : &:r103_6, r103_2
512+
# 103| r103_6(int) = CopyValue : r103_2
513+
# 103| r103_7(glval<int>) = VariableAddress[y] :
514+
# 103| mu103_8(int) = Store : &:r103_7, r103_6
514515
# 104| r104_1(glval<int>) = VariableAddress[x] :
515516
# 104| r104_2(int) = Load : &:r104_1, ~mu98_3
516517
# 104| r104_3(int) = Constant[1] :
517518
# 104| r104_4(int) = Sub : r104_2, r104_3
518519
# 104| mu104_5(int) = Store : &:r104_1, r104_4
519-
# 104| r104_6(glval<int>) = VariableAddress[y] :
520-
# 104| mu104_7(int) = Store : &:r104_6, r104_2
520+
# 104| r104_6(int) = CopyValue : r104_2
521+
# 104| r104_7(glval<int>) = VariableAddress[y] :
522+
# 104| mu104_8(int) = Store : &:r104_7, r104_6
521523
# 105| v105_1(void) = NoOp :
522524
# 98| v98_6(void) = ReturnVoid :
523525
# 98| v98_7(void) = UnmodeledUse : mu*
@@ -727,15 +729,17 @@ ir.cpp:
727729
# 149| r149_3(float) = Constant[1.0] :
728730
# 149| r149_4(float) = Add : r149_2, r149_3
729731
# 149| mu149_5(float) = Store : &:r149_1, r149_4
730-
# 149| r149_6(glval<float>) = VariableAddress[y] :
731-
# 149| mu149_7(float) = Store : &:r149_6, r149_2
732+
# 149| r149_6(float) = CopyValue : r149_2
733+
# 149| r149_7(glval<float>) = VariableAddress[y] :
734+
# 149| mu149_8(float) = Store : &:r149_7, r149_6
732735
# 150| r150_1(glval<float>) = VariableAddress[x] :
733736
# 150| r150_2(float) = Load : &:r150_1, ~mu144_3
734737
# 150| r150_3(float) = Constant[1.0] :
735738
# 150| r150_4(float) = Sub : r150_2, r150_3
736739
# 150| mu150_5(float) = Store : &:r150_1, r150_4
737-
# 150| r150_6(glval<float>) = VariableAddress[y] :
738-
# 150| mu150_7(float) = Store : &:r150_6, r150_2
740+
# 150| r150_6(float) = CopyValue : r150_2
741+
# 150| r150_7(glval<float>) = VariableAddress[y] :
742+
# 150| mu150_8(float) = Store : &:r150_7, r150_6
739743
# 151| v151_1(void) = NoOp :
740744
# 144| v144_6(void) = ReturnVoid :
741745
# 144| v144_7(void) = UnmodeledUse : mu*
@@ -1037,15 +1041,17 @@ ir.cpp:
10371041
# 209| r209_3(int) = Constant[1] :
10381042
# 209| r209_4(int *) = PointerAdd[4] : r209_2, r209_3
10391043
# 209| mu209_5(int *) = Store : &:r209_1, r209_4
1040-
# 209| r209_6(glval<int *>) = VariableAddress[q] :
1041-
# 209| mu209_7(int *) = Store : &:r209_6, r209_2
1044+
# 209| r209_6(int *) = CopyValue : r209_2
1045+
# 209| r209_7(glval<int *>) = VariableAddress[q] :
1046+
# 209| mu209_8(int *) = Store : &:r209_7, r209_6
10421047
# 210| r210_1(glval<int *>) = VariableAddress[p] :
10431048
# 210| r210_2(int *) = Load : &:r210_1, ~mu204_3
10441049
# 210| r210_3(int) = Constant[1] :
10451050
# 210| r210_4(int *) = PointerSub[4] : r210_2, r210_3
10461051
# 210| mu210_5(int *) = Store : &:r210_1, r210_4
1047-
# 210| r210_6(glval<int *>) = VariableAddress[q] :
1048-
# 210| mu210_7(int *) = Store : &:r210_6, r210_2
1052+
# 210| r210_6(int *) = CopyValue : r210_2
1053+
# 210| r210_7(glval<int *>) = VariableAddress[q] :
1054+
# 210| mu210_8(int *) = Store : &:r210_7, r210_6
10491055
# 211| v211_1(void) = NoOp :
10501056
# 204| v204_8(void) = ReturnIndirection : &:r204_6, ~mu204_3
10511057
# 204| v204_9(void) = ReturnVoid :

0 commit comments

Comments
 (0)