Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 22b04af

Browse files
authored
Merge pull request #11658 from MathiasVP/uncertain-writes
C++: Flow through uncertain writes
2 parents bb25651 + a161ddd commit 22b04af

9 files changed

Lines changed: 114 additions & 37 deletions

File tree

cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -718,7 +718,7 @@ class UninitializedNode extends Node {
718718
UninitializedNode() {
719719
exists(Ssa::Def def |
720720
def.getValue().asInstruction() instanceof UninitializedInstruction and
721-
Ssa::nodeToDefOrUse(this, def) and
721+
Ssa::nodeToDefOrUse(this, def, _) and
722722
v = def.getSourceVariable().getBaseVariable().(Ssa::BaseIRVariable).getIRVariable().getAst()
723723
)
724724
}

cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaInternals.qll

Lines changed: 60 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -291,23 +291,27 @@ predicate outNodeHasAddressAndIndex(
291291
out.getIndirectionIndex() = indirectionIndex
292292
}
293293

294-
private predicate defToNode(Node nodeFrom, Def def) {
295-
nodeHasOperand(nodeFrom, def.getValue().asOperand(), def.getIndirectionIndex())
296-
or
297-
nodeHasInstruction(nodeFrom, def.getValue().asInstruction(), def.getIndirectionIndex())
294+
private predicate defToNode(Node nodeFrom, Def def, boolean uncertain) {
295+
(
296+
nodeHasOperand(nodeFrom, def.getValue().asOperand(), def.getIndirectionIndex())
297+
or
298+
nodeHasInstruction(nodeFrom, def.getValue().asInstruction(), def.getIndirectionIndex())
299+
) and
300+
if def.isCertain() then uncertain = false else uncertain = true
298301
}
299302

300303
/**
301304
* INTERNAL: Do not use.
302305
*
303306
* Holds if `nodeFrom` is the node that correspond to the definition or use `defOrUse`.
304307
*/
305-
predicate nodeToDefOrUse(Node nodeFrom, SsaDefOrUse defOrUse) {
308+
predicate nodeToDefOrUse(Node nodeFrom, SsaDefOrUse defOrUse, boolean uncertain) {
306309
// Node -> Def
307-
defToNode(nodeFrom, defOrUse)
310+
defToNode(nodeFrom, defOrUse, uncertain)
308311
or
309312
// Node -> Use
310-
useToNode(defOrUse, nodeFrom)
313+
useToNode(defOrUse, nodeFrom) and
314+
uncertain = false
311315
}
312316

313317
/**
@@ -316,7 +320,7 @@ predicate nodeToDefOrUse(Node nodeFrom, SsaDefOrUse defOrUse) {
316320
*/
317321
private predicate indirectConversionFlowStep(Node nFrom, Node nTo) {
318322
not exists(UseOrPhi defOrUse |
319-
nodeToDefOrUse(nTo, defOrUse) and
323+
nodeToDefOrUse(nTo, defOrUse, _) and
320324
adjacentDefRead(defOrUse, _)
321325
) and
322326
exists(Operand op1, Operand op2, int indirectionIndex, Instruction instr |
@@ -342,31 +346,60 @@ private predicate indirectConversionFlowStep(Node nFrom, Node nTo) {
342346
* So this predicate recurses back along conversions and `PointerArithmeticInstruction`s to find the
343347
* first use that has provides use-use flow, and uses that target as the target of the `nodeFrom`.
344348
*/
345-
private predicate adjustForPointerArith(Node nodeFrom, UseOrPhi use) {
349+
private predicate adjustForPointerArith(Node nodeFrom, UseOrPhi use, boolean uncertain) {
346350
nodeFrom = any(PostUpdateNode pun).getPreUpdateNode() and
347351
exists(DefOrUse defOrUse, Node adjusted |
348352
indirectConversionFlowStep*(adjusted, nodeFrom) and
349-
nodeToDefOrUse(adjusted, defOrUse) and
353+
nodeToDefOrUse(adjusted, defOrUse, uncertain) and
350354
adjacentDefRead(defOrUse, use)
351355
)
352356
}
353357

354-
/** Holds if there is def-use or use-use flow from `nodeFrom` to `nodeTo`. */
355-
predicate ssaFlow(Node nodeFrom, Node nodeTo) {
358+
private predicate ssaFlowImpl(Node nodeFrom, Node nodeTo, boolean uncertain) {
356359
// `nodeFrom = any(PostUpdateNode pun).getPreUpdateNode()` is implied by adjustedForPointerArith.
357360
exists(UseOrPhi use |
358-
adjustForPointerArith(nodeFrom, use) and
361+
adjustForPointerArith(nodeFrom, use, uncertain) and
359362
useToNode(use, nodeTo)
360363
)
361364
or
362365
not nodeFrom = any(PostUpdateNode pun).getPreUpdateNode() and
363366
exists(DefOrUse defOrUse1, UseOrPhi use |
364-
nodeToDefOrUse(nodeFrom, defOrUse1) and
367+
nodeToDefOrUse(nodeFrom, defOrUse1, uncertain) and
365368
adjacentDefRead(defOrUse1, use) and
366369
useToNode(use, nodeTo)
367370
)
368371
}
369372

373+
/**
374+
* Holds if `def` is the corresponding definition of
375+
* the SSA library's `definition`.
376+
*/
377+
private predicate ssaDefinition(Def def, Definition definition) {
378+
exists(IRBlock block, int i, SourceVariable sv |
379+
def.hasIndexInBlock(block, i, sv) and
380+
definition.definesAt(sv, block, i)
381+
)
382+
}
383+
384+
/** Gets a node that represents the prior definition of `node`. */
385+
private Node getAPriorDefinition(Node node) {
386+
exists(Def def, Definition definition, Definition inp, Def input |
387+
defToNode(node, def, true) and
388+
ssaDefinition(def, definition) and
389+
uncertainWriteDefinitionInput(pragma[only_bind_into](definition), pragma[only_bind_into](inp)) and
390+
ssaDefinition(input, inp) and
391+
defToNode(result, input, _)
392+
)
393+
}
394+
395+
/** Holds if there is def-use or use-use flow from `nodeFrom` to `nodeTo`. */
396+
predicate ssaFlow(Node nodeFrom, Node nodeTo) {
397+
exists(Node nFrom, boolean uncertain |
398+
ssaFlowImpl(nFrom, nodeTo, uncertain) and
399+
if uncertain = true then nodeFrom = [nFrom, getAPriorDefinition(nFrom)] else nodeFrom = nFrom
400+
)
401+
}
402+
370403
/**
371404
* Holds if `use` is a use of `sv` and is a next adjacent use of `phi` in
372405
* index `i1` in basic block `bb1`.
@@ -460,6 +493,11 @@ module SsaCached {
460493
predicate lastRefRedef(Definition def, IRBlock bb, int i, Definition next) {
461494
SsaImpl::lastRefRedef(def, bb, i, next)
462495
}
496+
497+
cached
498+
predicate uncertainWriteDefinitionInput(SsaImpl::UncertainWriteDefinition def, Definition inp) {
499+
SsaImpl::uncertainWriteDefinitionInput(def, inp)
500+
}
463501
}
464502

465503
cached
@@ -498,6 +536,10 @@ class DefOrUse extends TDefOrUse, SsaDefOrUse {
498536
final SourceVariable getSourceVariable() { result = defOrUse.getSourceVariable() }
499537

500538
override string toString() { result = defOrUse.toString() }
539+
540+
predicate hasIndexInBlock(IRBlock block, int index, SourceVariable sv) {
541+
defOrUse.hasIndexInBlock(block, index, sv)
542+
}
501543
}
502544

503545
class Phi extends TPhi, SsaDefOrUse {
@@ -541,6 +583,8 @@ class Def extends DefOrUse {
541583
}
542584

543585
Node0Impl getValue() { result = defOrUse.getValue() }
586+
587+
predicate isCertain() { defOrUse.isCertain() }
544588
}
545589

546590
private module SsaImpl = SsaImplCommon::Make<SsaInput>;
@@ -549,4 +593,6 @@ class PhiNode = SsaImpl::PhiNode;
549593

550594
class Definition = SsaImpl::Definition;
551595

596+
class UncertainWriteDefinition = SsaImpl::UncertainWriteDefinition;
597+
552598
import SsaCached

cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaInternalsCommon.qll

Lines changed: 33 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -428,9 +428,12 @@ private module Cached {
428428
boolean certain, Node0Impl value, Operand address, BaseSourceVariableInstruction base, int ind,
429429
int indirectionIndex
430430
) {
431-
exists(int ind0, CppType type, int lower, int upper |
432-
isWrite(value, address, certain) and
433-
isDefImpl(address, base, ind0) and
431+
exists(
432+
boolean writeIsCertain, boolean addressIsCertain, int ind0, CppType type, int lower, int upper
433+
|
434+
isWrite(value, address, writeIsCertain) and
435+
isDefImpl(address, base, ind0, addressIsCertain) and
436+
certain = writeIsCertain.booleanAnd(addressIsCertain) and
434437
type = getLanguageType(address) and
435438
upper = countIndirectionsForCppType(type) and
436439
ind = ind0 + [lower .. upper] and
@@ -439,34 +442,47 @@ private module Cached {
439442
)
440443
}
441444

445+
/**
446+
* Holds if the address computed by `operand` is guarenteed to write
447+
* to a specific address.
448+
*/
449+
private predicate isCertainAddress(Operand operand) {
450+
operand.getDef() instanceof VariableAddressInstruction
451+
or
452+
operand.getType() instanceof Cpp::ReferenceType
453+
}
454+
442455
/**
443456
* Holds if `address` is a use of an SSA variable rooted at `base`, and the
444457
* path from `base` to `address` passes through `ind` load-like instructions.
445458
*
446459
* Note: Unlike `isUseImpl`, this predicate recurses through pointer-arithmetic
447460
* instructions.
448461
*/
449-
private predicate isDefImpl(Operand address, BaseSourceVariableInstruction base, int ind) {
462+
private predicate isDefImpl(
463+
Operand operand, BaseSourceVariableInstruction base, int ind, boolean certain
464+
) {
450465
DataFlowImplCommon::forceCachingInSameStage() and
451466
ind = 0 and
452-
address = base.getAUse()
467+
operand = base.getAUse() and
468+
(if isCertainAddress(operand) then certain = true else certain = false)
453469
or
454-
exists(Operand mid, Instruction instr |
455-
isDefImpl(mid, base, ind) and
456-
instr = address.getDef() and
457-
conversionFlow(mid, instr, _)
470+
exists(Operand mid, Instruction instr, boolean certain0, boolean isPointerArith |
471+
isDefImpl(mid, base, ind, certain0) and
472+
instr = operand.getDef() and
473+
conversionFlow(mid, instr, isPointerArith) and
474+
if isPointerArith = true then certain = false else certain = certain0
458475
)
459476
or
460-
exists(int ind0 |
461-
exists(Operand operand |
462-
isDereference(address.getDef(), operand) and
463-
isDefImpl(operand, base, ind - 1)
464-
)
465-
or
466-
isDefImpl(address.getDef().(InitializeParameterInstruction).getAnOperand(), base, ind0)
477+
exists(Operand address, boolean certain0 |
478+
isDereference(operand.getDef(), address) and
479+
isDefImpl(address, base, ind - 1, certain0)
467480
|
468-
ind0 = ind - 1
481+
if isCertainAddress(operand) then certain = certain0 else certain = false
469482
)
483+
or
484+
isDefImpl(operand.getDef().(InitializeParameterInstruction).getAnOperand(), base, ind - 1, _) and
485+
certain = true
470486
}
471487
}
472488

cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/ssa0/SsaInternals.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ private newtype TDefOrUseImpl =
3232
TDefImpl(Operand address) { isDef(_, _, address, _, _, _) } or
3333
TUseImpl(Operand operand) {
3434
isUse(_, operand, _, _, _) and
35-
not isDef(_, _, operand, _, _, _)
35+
not isDef(true, _, operand, _, _, _)
3636
}
3737

3838
abstract private class DefOrUseImpl extends TDefOrUseImpl {

cpp/ql/test/library-tests/dataflow/dataflow-tests/clang.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,5 +48,5 @@ void following_pointers(
4848

4949
int stackArray[2] = { source(), source() };
5050
stackArray[0] = source();
51-
sink(stackArray); // $ ast,ir
51+
sink(stackArray); // $ ast ir ir=49:35 ir=50:19
5252
}

cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-consistency.expected

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,10 @@ postWithInFlow
9292
| test.cpp:506:3:506:4 | * ... [post update] | PostUpdateNode should not be the target of local flow. |
9393
| test.cpp:506:4:506:4 | p [inner post update] | PostUpdateNode should not be the target of local flow. |
9494
| test.cpp:512:35:512:35 | x [inner post update] | PostUpdateNode should not be the target of local flow. |
95+
| test.cpp:519:3:519:12 | stackArray [inner post update] | PostUpdateNode should not be the target of local flow. |
96+
| test.cpp:519:3:519:15 | access to array [post update] | PostUpdateNode should not be the target of local flow. |
97+
| test.cpp:520:3:520:12 | stackArray [inner post update] | PostUpdateNode should not be the target of local flow. |
98+
| test.cpp:520:3:520:15 | access to array [post update] | PostUpdateNode should not be the target of local flow. |
9599
viableImplInCallContextTooLarge
96100
uniqueParameterNodeAtPosition
97101
uniqueParameterNodePosition

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -512,3 +512,11 @@ void viaOutparamMissingReturn() {
512512
intOutparamSourceMissingReturn(&x);
513513
sink(x); // $ ast MISSING: ir
514514
}
515+
516+
void uncertain_definition() {
517+
int stackArray[2];
518+
int clean = 0;
519+
stackArray[0] = source();
520+
stackArray[1] = clean;
521+
sink(stackArray[0]); // $ ast=519:19 ir SPURIOUS: ast=517:7
522+
}

cpp/ql/test/library-tests/dataflow/dataflow-tests/uninitialized.expected

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,6 @@
3434
| test.cpp:448:7:448:11 | local | test.cpp:449:18:449:22 | local |
3535
| test.cpp:448:7:448:11 | local | test.cpp:450:8:450:12 | local |
3636
| test.cpp:448:7:448:11 | local | test.cpp:451:9:451:13 | local |
37+
| test.cpp:517:7:517:16 | stackArray | test.cpp:519:3:519:12 | stackArray |
38+
| test.cpp:517:7:517:16 | stackArray | test.cpp:520:3:520:12 | stackArray |
39+
| test.cpp:517:7:517:16 | stackArray | test.cpp:521:8:521:17 | stackArray |

cpp/ql/test/library-tests/dataflow/source-sink-tests/sources-and-sinks.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ char *secure_getenv(const char *name);
33
wchar_t *_wgetenv(const wchar_t *name);
44

55
void test_getenv() {
6-
void *var1 = getenv("VAR"); // $ local_source
7-
void *var2 = secure_getenv("VAR"); // $ local_source
8-
void *var3 = _wgetenv(L"VAR"); // $ local_source
6+
void *var1 = getenv("VAR"); // $ local_source=6:18 local_source=6:18
7+
void *var2 = secure_getenv("VAR"); // $ local_source=7:18 local_source=7:18
8+
void *var3 = _wgetenv(L"VAR"); // $ local_source=8:18 local_source=8:18
99
}
1010

1111
int send(int, const void*, int, int);

0 commit comments

Comments
 (0)