Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions cpp/ql/src/Critical/MissingCheckScanf.ql
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@
import cpp
import semmle.code.cpp.commons.Scanf
import semmle.code.cpp.controlflow.Guards
import semmle.code.cpp.controlflow.IRGuards as IRGuards
import semmle.code.cpp.dataflow.new.DataFlow::DataFlow
import semmle.code.cpp.ir.IR
import semmle.code.cpp.ir.ValueNumbering
import semmle.code.cpp.valuenumbering.GlobalValueNumbering
import ScanfChecks
import ScanfToUseFlow::PathGraph
Expand Down Expand Up @@ -103,6 +105,10 @@ module ScanfToUseConfig implements ConfigSig {

predicate isSink(Node sink) { isSink(sink, _) }

predicate isBarrier(Node node) {
node = BarrierGuard<scanfChecksOutput/3>::getAnIndirectBarrierNode(1)
}

predicate isBarrierOut(Node n) {
// We disable flow out of sinks to reduce result duplication
isSink(n, _)
Expand Down Expand Up @@ -143,6 +149,45 @@ int getMinimumGuardConstant(ScanfFunctionCall call, int index) {
)
}

/**
* Holds if `output` addresses a local non-reference variable whose other lvalue uses are
* assignments, increments, or output arguments of `scanf`-like calls.
* Aliased reads are not always tracked by this configuration, so retaining
* the successful flow for other address uses avoids hiding an unsafe read.
*/
private predicate isUnaliasedScanfOutput(Expr output) {
exists(LocalVariable variable, AddressOfExpr address |
output.getUnconverted() = address and
address.getOperand().(VariableAccess).getTarget() = variable and
not variable.getType().getUnspecifiedType() instanceof ReferenceType and
forall(VariableAccess access |
access.getTarget() = variable and access.isUsedAsLValue() and not access.isModified()
|
exists(ScanfFunctionCall call, int index |
call.getOutputArgument(index).getUnconverted() = access.getParent()
)
)
)
}

/** Holds if `guard` ensures that `output` was written when it evaluates to `branch`. */
private predicate scanfChecksOutput(IRGuards::IRGuardCondition guard, Expr output, boolean branch) {
exists(
ScanfFunctionCall call, int index, CallInstruction instruction, int minGuard,
IRGuards::GuardValue value
|
value.asBooleanValue() = branch and
output = call.getOutputArgument(index).getFullyConverted() and
isUnaliasedScanfOutput(output) and
instruction.getUnconvertedResultExpression() = call and
minGuard = getMinimumGuardConstant(call, index)
|
guard.comparesEq(valueNumber(instruction).getAUse(), any(int k | minGuard <= k), true, value)
or
guard.comparesLt(valueNumber(instruction).getAUse(), any(int k | minGuard <= k), false, value)
)
}

/**
* Holds the access to `e` isn't guarded by a check that ensures that `call` returned
* at least `minGuard`.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* The `cpp/missing-check-scanf` query produces fewer false positives when an unaliased local output variable is overwritten with a default value if a `scanf`-like call fails.
Loading