mirror of
https://github.com/github/codeql.git
synced 2025-12-24 04:36:35 +01:00
Merge branch 'main' into atorralba/promote-groovy-injection
This commit is contained in:
@@ -17,3 +17,9 @@
|
||||
/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll @github/codeql-java @github/codeql-go
|
||||
/java/ql/src/semmle/code/java/dataflow/internal/tainttracking1/TaintTrackingImpl.qll @github/codeql-java @github/codeql-go
|
||||
/java/ql/src/semmle/code/java/dataflow/internal/tainttracking2/TaintTrackingImpl.qll @github/codeql-java @github/codeql-go
|
||||
|
||||
# CodeQL tools and associated docs
|
||||
/docs/codeql-cli/ @github/codeql-cli-reviewers
|
||||
/docs/codeql-for-visual-studio-code/ @github/codeql-vscode-reviewers
|
||||
/docs/ql-language-reference/ @github/codeql-frontend-reviewers
|
||||
/docs/query-*-style-guide.md @github/codeql-analysis-reviewers
|
||||
@@ -1,4 +0,0 @@
|
||||
lgtm,codescanning
|
||||
* The `StackVariableReachability` library now ignores some paths that contain an infeasible combination
|
||||
of conditionals. These improvements primarily affect the queries `cpp/uninitialized-local` and
|
||||
`cpp/use-after-free`.
|
||||
3
cpp/change-notes/2021-07-13-cleartext-storage-file.md
Normal file
3
cpp/change-notes/2021-07-13-cleartext-storage-file.md
Normal file
@@ -0,0 +1,3 @@
|
||||
lgtm,codescanning
|
||||
* The "Cleartext storage of sensitive information in file" (cpp/cleartext-storage-file) query now uses dataflow to produce additional results.
|
||||
* Heuristics in the SensitiveExprs.qll library have been improved, making the "Cleartext storage of sensitive information in file" (cpp/cleartext-storage-file), "Cleartext storage of sensitive information in buffer" (cpp/cleartext-storage-buffer) and "Cleartext storage of sensitive information in an SQLite" (cpp/cleartext-storage-database) queries more accurate.
|
||||
2
cpp/change-notes/2021-07-20-toctou-race-condition.md
Normal file
2
cpp/change-notes/2021-07-20-toctou-race-condition.md
Normal file
@@ -0,0 +1,2 @@
|
||||
lgtm,codescanning
|
||||
* Improvements have been made to the `cpp/toctou-race-condition` query, both to find more correct results and fewer false positive results.
|
||||
@@ -0,0 +1,2 @@
|
||||
lgtm,codescanning
|
||||
* Virtual function specifiers are now accessible via the new predicates on `Function` (`.isDeclaredVirtual`, `.isOverride`, and `.isFinal`).
|
||||
@@ -41,7 +41,7 @@ DeclStmt declWithNoInit(LocalVariable v) {
|
||||
result.getADeclaration() = v and
|
||||
not exists(v.getInitializer()) and
|
||||
/* The type of the variable is not stack-allocated. */
|
||||
not allocatedType(v.getType())
|
||||
exists(Type t | t = v.getType() | not allocatedType(t))
|
||||
}
|
||||
|
||||
class UninitialisedLocalReachability extends StackVariableReachability {
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @security-severity 7.5
|
||||
* @precision medium
|
||||
* @precision high
|
||||
* @id cpp/cleartext-storage-file
|
||||
* @tags security
|
||||
* external/cwe/cwe-313
|
||||
@@ -14,10 +14,40 @@
|
||||
import cpp
|
||||
import semmle.code.cpp.security.SensitiveExprs
|
||||
import semmle.code.cpp.security.FileWrite
|
||||
import semmle.code.cpp.dataflow.DataFlow
|
||||
import semmle.code.cpp.valuenumbering.GlobalValueNumbering
|
||||
|
||||
from FileWrite w, SensitiveExpr source, Expr dest
|
||||
/**
|
||||
* An operation on a filename.
|
||||
*/
|
||||
predicate filenameOperation(FunctionCall op, Expr path) {
|
||||
exists(string name | name = op.getTarget().getName() |
|
||||
name =
|
||||
[
|
||||
"remove", "unlink", "rmdir", "rename", "fopen", "open", "freopen", "_open", "_wopen",
|
||||
"_wfopen", "_fsopen", "_wfsopen", "chmod", "chown", "stat", "lstat", "fstat", "access",
|
||||
"_access", "_waccess", "_access_s", "_waccess_s"
|
||||
] and
|
||||
path = op.getArgument(0)
|
||||
or
|
||||
name = ["fopen_s", "wfopen_s", "rename"] and
|
||||
path = op.getArgument(1)
|
||||
)
|
||||
}
|
||||
|
||||
predicate isFileName(GVN gvn) {
|
||||
exists(FunctionCall op, Expr path |
|
||||
filenameOperation(op, path) and
|
||||
gvn = globalValueNumber(path)
|
||||
)
|
||||
}
|
||||
|
||||
from FileWrite w, SensitiveExpr source, Expr mid, Expr dest
|
||||
where
|
||||
source = w.getASource() and
|
||||
dest = w.getDest()
|
||||
DataFlow::localFlow(DataFlow::exprNode(source), DataFlow::exprNode(mid)) and
|
||||
mid = w.getASource() and
|
||||
dest = w.getDest() and
|
||||
not isFileName(globalValueNumber(source)) and // file names are not passwords
|
||||
not exists(string convChar | convChar = w.getSourceConvChar(mid) | not convChar = ["s", "S"]) // ignore things written with other conversion characters
|
||||
select w, "This write into file '" + dest.toString() + "' may contain unencrypted data from $@",
|
||||
source, "this source."
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @security-severity 7.7
|
||||
* @precision medium
|
||||
* @precision high
|
||||
* @id cpp/toctou-race-condition
|
||||
* @tags security
|
||||
* external/cwe/cwe-367
|
||||
@@ -16,59 +16,60 @@ import cpp
|
||||
import semmle.code.cpp.controlflow.Guards
|
||||
|
||||
/**
|
||||
* An operation on a filename.
|
||||
* An operation on a filename that is likely to modify the corresponding file
|
||||
* and may return an indication of success.
|
||||
*
|
||||
* Note: we're not interested in operations on file descriptors, as they
|
||||
* are better behaved.
|
||||
* Note: we're not interested in operations where the file is specified by a
|
||||
* descriptor, rather than a filename, as they are better behaved. We are
|
||||
* interested in functions that take a filename and return a file descriptor,
|
||||
* however.
|
||||
*/
|
||||
FunctionCall filenameOperation(Expr path) {
|
||||
exists(string name | name = result.getTarget().getName() |
|
||||
(
|
||||
name = "remove" or
|
||||
name = "unlink" or
|
||||
name = "rmdir" or
|
||||
name = "rename" or
|
||||
name = "chmod" or
|
||||
name = "chown" or
|
||||
name = "fopen" or
|
||||
name = "open" or
|
||||
name = "freopen" or
|
||||
name = "_open" or
|
||||
name = "_wopen" or
|
||||
name = "_wfopen"
|
||||
) and
|
||||
name =
|
||||
[
|
||||
"remove", "unlink", "rmdir", "rename", "fopen", "open", "freopen", "_open", "_wopen",
|
||||
"_wfopen", "_fsopen", "_wfsopen"
|
||||
] and
|
||||
result.getArgument(0) = path
|
||||
or
|
||||
(
|
||||
name = "fopen_s" or
|
||||
name = "wfopen_s"
|
||||
) and
|
||||
name = ["fopen_s", "wfopen_s", "rename"] and
|
||||
result.getArgument(1) = path
|
||||
)
|
||||
or
|
||||
result = sensitiveFilenameOperation(path)
|
||||
}
|
||||
|
||||
/**
|
||||
* An operation on a filename that is likely to modify the security properties
|
||||
* of the corresponding file and may return an indication of success.
|
||||
*/
|
||||
FunctionCall sensitiveFilenameOperation(Expr path) {
|
||||
exists(string name | name = result.getTarget().getName() |
|
||||
name = ["chmod", "chown"] and
|
||||
result.getArgument(0) = path
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A use of `access` (or similar) on a filename.
|
||||
* An operation on a filename that returns information in the return value but
|
||||
* does not modify the corresponding file. For example, `access`.
|
||||
*/
|
||||
FunctionCall accessCheck(Expr path) {
|
||||
exists(string name | name = result.getTarget().getName() |
|
||||
name = "access" or
|
||||
name = "_access" or
|
||||
name = "_waccess" or
|
||||
name = "_access_s" or
|
||||
name = "_waccess_s"
|
||||
name = ["access", "_access", "_waccess", "_access_s", "_waccess_s"]
|
||||
) and
|
||||
path = result.getArgument(0)
|
||||
}
|
||||
|
||||
/**
|
||||
* A use of `stat` (or similar) on a filename.
|
||||
* An operation on a filename that returns information via a pointer argument
|
||||
* and any return value, but does not modify the corresponding file. For
|
||||
* example, `stat`.
|
||||
*/
|
||||
FunctionCall stat(Expr path, Expr buf) {
|
||||
exists(string name | name = result.getTarget().getName() |
|
||||
name = "stat" or
|
||||
name = "lstat" or
|
||||
name = "fstat" or
|
||||
name = ["stat", "lstat", "fstat"] or
|
||||
name.matches("\\_stat%") or
|
||||
name.matches("\\_wstat%")
|
||||
) and
|
||||
@@ -77,7 +78,7 @@ FunctionCall stat(Expr path, Expr buf) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `use` points to `source`, either by being the same or by
|
||||
* Holds if `use` refers to `source`, either by being the same or by
|
||||
* one step of variable indirection.
|
||||
*/
|
||||
predicate referenceTo(Expr source, Expr use) {
|
||||
@@ -88,36 +89,45 @@ predicate referenceTo(Expr source, Expr use) {
|
||||
)
|
||||
}
|
||||
|
||||
from FunctionCall fc, Expr check, Expr checkUse, Expr opUse
|
||||
from Expr check, Expr checkPath, FunctionCall use, Expr usePath
|
||||
where
|
||||
// checkUse looks like a check on a filename
|
||||
// `check` looks like a check on a filename
|
||||
(
|
||||
(
|
||||
// either:
|
||||
// an access check
|
||||
check = accessCheck(checkUse)
|
||||
check = accessCheck(checkPath)
|
||||
or
|
||||
// a stat
|
||||
check = stat(checkUse, _)
|
||||
or
|
||||
// another filename operation (null pointers can indicate errors)
|
||||
check = filenameOperation(checkUse)
|
||||
check = stat(checkPath, _)
|
||||
or
|
||||
// access to a member variable on the stat buf
|
||||
// (morally, this should be a use-use pair, but it seems unlikely
|
||||
// that this variable will get reused in practice)
|
||||
exists(Variable buf | exists(stat(checkUse, buf.getAnAccess())) |
|
||||
check.(VariableAccess).getQualifier() = buf.getAnAccess()
|
||||
exists(Expr call, Expr e, Variable v |
|
||||
call = stat(checkPath, e) and
|
||||
e.getAChild*().(VariableAccess).getTarget() = v and
|
||||
check.(VariableAccess).getTarget() = v and
|
||||
not e.getAChild*() = check // the call that writes to the pointer is not where the pointer is checked.
|
||||
)
|
||||
) and
|
||||
// checkUse and opUse refer to the same SSA variable
|
||||
exists(SsaDefinition def, StackVariable v | def.getAUse(v) = checkUse and def.getAUse(v) = opUse) and
|
||||
// opUse looks like an operation on a filename
|
||||
fc = filenameOperation(opUse) and
|
||||
// the return value of check is used (possibly with one step of
|
||||
// variable indirection) in a guard which controls fc
|
||||
// `op` looks like an operation on a filename
|
||||
use = filenameOperation(usePath)
|
||||
or
|
||||
// another filename operation (null pointers can indicate errors)
|
||||
check = filenameOperation(checkPath) and
|
||||
// `op` looks like a sensitive operation on a filename
|
||||
use = sensitiveFilenameOperation(usePath)
|
||||
) and
|
||||
// `checkPath` and `usePath` refer to the same SSA variable
|
||||
exists(SsaDefinition def, StackVariable v |
|
||||
def.getAUse(v) = checkPath and def.getAUse(v) = usePath
|
||||
) and
|
||||
// the return value of `check` is used (possibly with one step of
|
||||
// variable indirection) in a guard which controls `use`
|
||||
exists(GuardCondition guard | referenceTo(check, guard.getAChild*()) |
|
||||
guard.controls(fc.(ControlFlowNode).getBasicBlock(), _)
|
||||
guard.controls(use.(ControlFlowNode).getBasicBlock(), _)
|
||||
)
|
||||
select fc,
|
||||
select use,
|
||||
"The $@ being operated upon was previously $@, but the underlying file may have been changed since then.",
|
||||
opUse, "filename", check, "checked"
|
||||
usePath, "filename", check, "checked"
|
||||
|
||||
@@ -82,9 +82,23 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
|
||||
/** Holds if this function is inline. */
|
||||
predicate isInline() { this.hasSpecifier("inline") }
|
||||
|
||||
/** Holds if this function is virtual. */
|
||||
/**
|
||||
* Holds if this function is virtual.
|
||||
*
|
||||
* Unlike `isDeclaredVirtual()`, `isVirtual()` holds even if the function
|
||||
* is not explicitly declared with the `virtual` specifier.
|
||||
*/
|
||||
predicate isVirtual() { this.hasSpecifier("virtual") }
|
||||
|
||||
/** Holds if this function is declared with the `virtual` specifier. */
|
||||
predicate isDeclaredVirtual() { this.hasSpecifier("declared_virtual") }
|
||||
|
||||
/** Holds if this function is declared with the `override` specifier. */
|
||||
predicate isOverride() { this.hasSpecifier("override") }
|
||||
|
||||
/** Holds if this function is declared with the `final` specifier. */
|
||||
predicate isFinal() { this.hasSpecifier("final") }
|
||||
|
||||
/**
|
||||
* Holds if this function is deleted.
|
||||
* This may be because it was explicitly deleted with an `= delete`
|
||||
|
||||
@@ -208,7 +208,7 @@ private predicate bbSuccessorEntryReachesDefOrUse(
|
||||
boolean skipsFirstLoopAlwaysTrueUponEntry
|
||||
) {
|
||||
exists(BasicBlock succ, boolean succSkipsFirstLoopAlwaysTrueUponEntry |
|
||||
bbSuccessorEntryReachesLoopInvariant0(bb, succ, skipsFirstLoopAlwaysTrueUponEntry,
|
||||
bbSuccessorEntryReachesLoopInvariant(bb, succ, skipsFirstLoopAlwaysTrueUponEntry,
|
||||
succSkipsFirstLoopAlwaysTrueUponEntry)
|
||||
|
|
||||
bbEntryReachesDefOrUseLocally(succ, v, defOrUse) and
|
||||
|
||||
@@ -3,250 +3,7 @@
|
||||
* reachability involving stack variables.
|
||||
*/
|
||||
|
||||
private import semmle.code.cpp.controlflow.Guards
|
||||
private import semmle.code.cpp.valuenumbering.GlobalValueNumbering
|
||||
|
||||
/** A `GuardCondition` which appear in a control-flow path to a sink. */
|
||||
abstract private class LogicalGuardCondition extends GuardCondition {
|
||||
LogicalGuardCondition() {
|
||||
// Either the `GuardCondition` is part of the path from a source to a sink
|
||||
revBbSuccessorEntryReaches0(_, this.getBasicBlock(), _, _, _)
|
||||
or
|
||||
// or it controls the basic block that contains the source node.
|
||||
this.controls(any(BasicBlock bb | fwdBbEntryReachesLocally(bb, _, _, _)), _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the truth of this logical expression having value `wholeIsTrue`
|
||||
* implies that the truth of the child expression `part` has truth value `partIsTrue`.
|
||||
*/
|
||||
abstract predicate impliesCondition(
|
||||
LogicalGuardCondition e, boolean testIsTrue, boolean condIsTrue
|
||||
);
|
||||
}
|
||||
|
||||
private class BinaryLogicalGuardCondition extends LogicalGuardCondition, BinaryLogicalOperation {
|
||||
override predicate impliesCondition(
|
||||
LogicalGuardCondition e, boolean testIsTrue, boolean condIsTrue
|
||||
) {
|
||||
this.impliesValue(e, testIsTrue, condIsTrue)
|
||||
}
|
||||
}
|
||||
|
||||
private class VariableGuardCondition extends LogicalGuardCondition, VariableAccess {
|
||||
override predicate impliesCondition(
|
||||
LogicalGuardCondition e, boolean testIsTrue, boolean condIsTrue
|
||||
) {
|
||||
this = e and
|
||||
(
|
||||
testIsTrue = true and condIsTrue = true
|
||||
or
|
||||
testIsTrue = false and condIsTrue = false
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class NotGuardCondition extends LogicalGuardCondition, NotExpr {
|
||||
override predicate impliesCondition(
|
||||
LogicalGuardCondition e, boolean testIsTrue, boolean condIsTrue
|
||||
) {
|
||||
e = this.getOperand() and
|
||||
(
|
||||
testIsTrue = true and
|
||||
condIsTrue = false
|
||||
or
|
||||
testIsTrue = false and
|
||||
condIsTrue = true
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private newtype TCondition =
|
||||
MkCondition(LogicalGuardCondition guard, boolean testIsTrue) { testIsTrue = [false, true] }
|
||||
|
||||
private class Condition extends MkCondition {
|
||||
boolean testIsTrue;
|
||||
LogicalGuardCondition guard;
|
||||
|
||||
Condition() { this = MkCondition(guard, testIsTrue) }
|
||||
|
||||
/**
|
||||
* Holds if this condition having the value `this.getTruthValue()` implies that `cond` has truth
|
||||
* value `cond.getTruthValue()`.
|
||||
*/
|
||||
string toString() { result = guard.toString() + " == " + testIsTrue.toString() }
|
||||
|
||||
/** Gets the value of this `Condition`. */
|
||||
boolean getTruthValue() { result = testIsTrue }
|
||||
|
||||
LogicalGuardCondition getCondition() { result = guard }
|
||||
|
||||
pragma[nomagic]
|
||||
predicate impliesCondition(Condition cond) {
|
||||
exists(LogicalGuardCondition other |
|
||||
other = cond.getCondition() and
|
||||
this.getCondition()
|
||||
.impliesCondition(globalValueNumber(other).getAnExpr(),
|
||||
pragma[only_bind_into](pragma[only_bind_out](testIsTrue)),
|
||||
pragma[only_bind_into](pragma[only_bind_out](cond.getTruthValue())))
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the negated expression represented by this `Condition`, if any. */
|
||||
private Condition negate() {
|
||||
result.getCondition() = guard and
|
||||
result.getTruthValue() = testIsTrue.booleanNot()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this condition having the value `this.getTruthValue()` implies that `cond` cannot have
|
||||
* the truth value `cond.getTruthValue()`.
|
||||
*/
|
||||
final predicate refutesCondition(Condition cond) { this.impliesCondition(cond.negate()) }
|
||||
|
||||
/** Gets the `Location` of the expression that generated this `Condition`. */
|
||||
Location getLocation() { result = guard.getLocation() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a `Condition` that controls `b`. That is, to enter `b` the condition must hold.
|
||||
*/
|
||||
private Condition getADirectCondition(BasicBlock b) {
|
||||
result.getCondition().controls(b, result.getTruthValue())
|
||||
}
|
||||
|
||||
/**
|
||||
* Like the shared dataflow library, the reachability analysis is split into two stages:
|
||||
* In the first stage, we compute an overapproximation of the possible control-flow paths where we don't
|
||||
* reason about path conditions. This stage is split into phases: A forward phase (computed by the
|
||||
* predicates prefixes with `fwd`), and a reverse phase (computed by the predicates prefixed with `rev`).
|
||||
*
|
||||
* The forward phease computes the set of control-flow nodes reachable from a given `source` and `v` such
|
||||
* that `config.isSource(source, v)` holds.
|
||||
*
|
||||
* See the QLDoc on `revBbSuccessorEntryReaches0` for a description of what the reverse phase computes.
|
||||
*/
|
||||
private predicate fwdBbSuccessorEntryReaches0(
|
||||
ControlFlowNode source, BasicBlock bb, SemanticStackVariable v,
|
||||
boolean skipsFirstLoopAlwaysTrueUponEntry, StackVariableReachability config
|
||||
) {
|
||||
fwdBbEntryReachesLocally(bb, v, source, config) and
|
||||
skipsFirstLoopAlwaysTrueUponEntry = false
|
||||
or
|
||||
exists(BasicBlock pred, boolean predSkipsFirstLoopAlwaysTrueUponEntry |
|
||||
bbSuccessorEntryReachesLoopInvariant0(pred, bb, predSkipsFirstLoopAlwaysTrueUponEntry,
|
||||
skipsFirstLoopAlwaysTrueUponEntry)
|
||||
|
|
||||
// Note we cannot filter out barriers at this point.
|
||||
// See the comment in `revBbSuccessorEntryReaches0` for an explanation why,
|
||||
fwdBbSuccessorEntryReaches0(source, pred, v, predSkipsFirstLoopAlwaysTrueUponEntry, config)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* The second phase of the first stages computes, for each `source` and `v` pair such
|
||||
* that `config.isSource(source, v)`, which sinks are reachable from that `(source, v)` pair.
|
||||
*/
|
||||
private predicate revBbSuccessorEntryReaches0(
|
||||
ControlFlowNode source, BasicBlock bb, SemanticStackVariable v,
|
||||
boolean skipsFirstLoopAlwaysTrueUponEntry, StackVariableReachability config
|
||||
) {
|
||||
exists(BasicBlock succ, boolean succSkipsFirstLoopAlwaysTrueUponEntry |
|
||||
fwdBbSuccessorEntryReaches0(source, bb, v, skipsFirstLoopAlwaysTrueUponEntry, config) and
|
||||
bbSuccessorEntryReachesLoopInvariant0(bb, succ, skipsFirstLoopAlwaysTrueUponEntry,
|
||||
succSkipsFirstLoopAlwaysTrueUponEntry)
|
||||
|
|
||||
revBbEntryReachesLocally(succ, v, _, config) and
|
||||
succSkipsFirstLoopAlwaysTrueUponEntry = false
|
||||
or
|
||||
// Note: We cannot rule out a successor block that contain a barrier here (like we do later in
|
||||
// `bbSuccessorEntryReaches`) as we might later discover that the only way to get through a piece of
|
||||
// code is through that barrier, and we want to discover this in
|
||||
// `bbSuccessorEntryReachesLoopInvariant`. As an example, consider this piece of code:
|
||||
// ```
|
||||
// if(b) { (1) source(); }
|
||||
// (2) if(b) { (3) barrier(); }
|
||||
// (4) sink();
|
||||
// ```
|
||||
// here, we want the successor relation to contain:
|
||||
// 1 -> {2}, 2 -> {3, 4}
|
||||
// since the second stage will deduce that the edge (2) -> (3) is unconditional (as b is always true
|
||||
// if we start at `source()`), and so there is actually no path from (1) to (4) without going through
|
||||
// a barrier.
|
||||
revBbSuccessorEntryReaches0(source, succ, v, succSkipsFirstLoopAlwaysTrueUponEntry, config)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate successorExitsLoop(BasicBlock pred, BasicBlock succ, Loop loop) {
|
||||
pred.getASuccessor() = succ and
|
||||
bbDominates(loop.getStmt(), pred) and
|
||||
not bbDominates(loop.getStmt(), succ)
|
||||
}
|
||||
|
||||
private predicate successorExitsFirstDisjunct(BasicBlock pred, BasicBlock succ) {
|
||||
exists(LogicalOrExpr orExpr | orExpr instanceof GuardCondition |
|
||||
pred.getAFalseSuccessor() = succ and
|
||||
pred.contains(orExpr.getLeftOperand())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* When we exit a loop, we filter out the conditions that arise from the loop's guard.
|
||||
* To see why this is necessary, consider this example:
|
||||
* ```
|
||||
* (1) source();
|
||||
* while (b) { (2) ... }
|
||||
* (3) sink();
|
||||
* ```
|
||||
* If we keep all the conditions when we transition from (2) to (3) we learn that `b` is true at
|
||||
* (3), but since we exited the loop we also learn that `b` is false at 3.
|
||||
* Thus, when we transition from (2) to (3) we discard all those conditions that are true at (2),
|
||||
* but NOT true at (3).
|
||||
*/
|
||||
private predicate isLoopCondition(LogicalGuardCondition cond, BasicBlock pred, BasicBlock bb) {
|
||||
exists(Loop loop, boolean testIsTrue | successorExitsLoop(pred, bb, loop) |
|
||||
// the resulting `Condition` holds inside the loop
|
||||
cond.controls(pred, testIsTrue) and
|
||||
// but not prior to the loop.
|
||||
not cond.controls(loop.getBasicBlock(), testIsTrue)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* When we leave the first disjunct we throw away the condition that says the the first disjunct is
|
||||
* false. To see why this is necessary, consider this example:
|
||||
* ```
|
||||
* if((1) b1 || (2) b2) { (3) ... }
|
||||
* ```
|
||||
* it holds that `b1 == false` controls (2), and since (2) steps to (3) we learn that `b1 == false `
|
||||
* holds at (3). So we filter out the conditions that we learn from leaving taking the false
|
||||
* branch in a disjunction.
|
||||
*/
|
||||
private predicate isDisjunctionCondition(LogicalGuardCondition cond, BasicBlock pred, BasicBlock bb) {
|
||||
exists(boolean testIsTrue | successorExitsFirstDisjunct(pred, bb) |
|
||||
// the resulting `Condition` holds after evaluating the left-hand side
|
||||
cond.controls(bb, testIsTrue) and
|
||||
// but not before evaluating the left-hand side.
|
||||
not cond.controls(pred, testIsTrue)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate isLoopVariantCondition(LogicalGuardCondition cond, BasicBlock pred, BasicBlock bb) {
|
||||
exists(Loop loop |
|
||||
bb.getEnd() = loop.getCondition() and
|
||||
pred.getASuccessor() = bb and
|
||||
bbDominates(bb, pred) and
|
||||
loopVariant(cond.getAChild*(), loop)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate loopVariant(VariableAccess e, Loop loop) {
|
||||
exists(SsaDefinition d | d.getAUse(e.getTarget()) = e |
|
||||
d.getAnUltimateDefiningValue(e.getTarget()) = loop.getCondition().getAChild*() or
|
||||
d.getAnUltimateDefiningValue(e.getTarget()).getEnclosingStmt().getParent*() = loop.getStmt() or
|
||||
d.getAnUltimateDefiningValue(e.getTarget()) = loop.(ForStmt).getUpdate().getAChild*()
|
||||
)
|
||||
}
|
||||
import cpp
|
||||
|
||||
/**
|
||||
* A reachability analysis for control-flow nodes involving stack variables.
|
||||
@@ -303,8 +60,7 @@ abstract class StackVariableReachability extends string {
|
||||
* ```
|
||||
*
|
||||
* In addition to using a better performing implementation, this analysis
|
||||
* accounts for loops where the condition is provably true upon entry, and discards paths that require
|
||||
* an infeasible combination of guard conditions (for example, `if(b) { ... }` and `if(!b) { ... }`).
|
||||
* accounts for loops where the condition is provably true upon entry.
|
||||
*/
|
||||
predicate reaches(ControlFlowNode source, SemanticStackVariable v, ControlFlowNode sink) {
|
||||
/*
|
||||
@@ -324,184 +80,46 @@ abstract class StackVariableReachability extends string {
|
||||
j > i and
|
||||
sink = bb.getNode(j) and
|
||||
isSink(sink, v) and
|
||||
not isBarrier(bb.getNode(pragma[only_bind_into]([i + 1 .. j - 1])), v)
|
||||
not exists(int k | isBarrier(bb.getNode(k), v) | k in [i + 1 .. j - 1])
|
||||
)
|
||||
or
|
||||
not exists(int k | isBarrier(bb.getNode(k), v) | k > i) and
|
||||
bbSuccessorEntryReaches(source, bb, v, sink, _)
|
||||
)
|
||||
}
|
||||
|
||||
private Condition getASinkCondition(SemanticStackVariable v) {
|
||||
exists(BasicBlock bb |
|
||||
revBbEntryReachesLocally(bb, v, _, this) and
|
||||
result.getCondition().controls(bb, result.getTruthValue())
|
||||
)
|
||||
}
|
||||
|
||||
private Condition getABarrierCondition(SemanticStackVariable v) {
|
||||
exists(BasicBlock bb |
|
||||
isBarrier(bb.getANode(), v) and
|
||||
result.getCondition().controls(bb, result.getTruthValue())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a condition with a known truth value in `bb` when the control-flow starts at the source
|
||||
* node `source` and we're tracking reachability using variable `v` (that is,
|
||||
* `this.isSource(source, v)` holds).
|
||||
*
|
||||
* This predicate is `pragma[noopt]` as it seems difficult to get the correct join order for the
|
||||
* recursive case otherwise:
|
||||
* revBbSuccessorEntryReaches0(bb) -> getASuccessor -> prev_delta ->
|
||||
* revBbSuccessorEntryReaches0(pred) -> {isLoopCondition, isDisjunctionCondition, isLoopVariantCondition}
|
||||
*/
|
||||
pragma[noopt]
|
||||
private Condition getACondition(ControlFlowNode source, SemanticStackVariable v, BasicBlock bb) {
|
||||
revBbSuccessorEntryReaches0(source, bb, v, _, this) and
|
||||
(
|
||||
result = getADirectCondition(bb) and
|
||||
(
|
||||
exists(Condition c |
|
||||
c = getASinkCondition(v) and
|
||||
result.refutesCondition(c)
|
||||
)
|
||||
or
|
||||
exists(Condition c |
|
||||
c = getABarrierCondition(v) and
|
||||
result.impliesCondition(c)
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(BasicBlock pred |
|
||||
pred.getASuccessor() = bb and
|
||||
result = getACondition(source, v, pred) and
|
||||
revBbSuccessorEntryReaches0(source, pred, v, _, this) and
|
||||
exists(LogicalGuardCondition c | c = result.getCondition() |
|
||||
not isLoopCondition(c, pred, bb) and
|
||||
not isDisjunctionCondition(c, pred, bb) and
|
||||
not isLoopVariantCondition(c, pred, bb)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate bbSuccessorEntryReachesLoopInvariantSucc(
|
||||
ControlFlowNode source, BasicBlock pred, SemanticStackVariable v, BasicBlock succ,
|
||||
boolean predSkipsFirstLoopAlwaysTrueUponEntry
|
||||
) {
|
||||
revBbSuccessorEntryReaches0(source, pragma[only_bind_into](pred), v,
|
||||
predSkipsFirstLoopAlwaysTrueUponEntry, this) and
|
||||
pred.getASuccessor() = succ
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate bbSuccessorEntryReachesLoopInvariantCand(
|
||||
ControlFlowNode source, BasicBlock pred, SemanticStackVariable v, BasicBlock succ,
|
||||
boolean predSkipsFirstLoopAlwaysTrueUponEntry, boolean succSkipsFirstLoopAlwaysTrueUponEntry
|
||||
) {
|
||||
bbSuccessorEntryReachesLoopInvariantSucc(source, pragma[only_bind_into](pred), v, succ,
|
||||
predSkipsFirstLoopAlwaysTrueUponEntry) and
|
||||
bbSuccessorEntryReachesLoopInvariant0(pred, succ, predSkipsFirstLoopAlwaysTrueUponEntry,
|
||||
succSkipsFirstLoopAlwaysTrueUponEntry)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `pred`, `succ`, `predSkipsFirstLoopAlwaysTrueUponEntry` and
|
||||
* `succSkipsFirstLoopAlwaysTrueUponEntry` satisfy the loop invariants specified in the QLDoc
|
||||
* for `bbSuccessorEntryReachesLoopInvariant0`.
|
||||
*
|
||||
* In addition, this predicate:
|
||||
* 1. Rules out successor blocks that are unreachable due to contradictory path conditions.
|
||||
* 2. Refines the successor relation when the edge `pred -> succ` is a conditional edge whose truth
|
||||
* value is known.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate bbSuccessorEntryReachesLoopInvariant(
|
||||
ControlFlowNode source, BasicBlock pred, SemanticStackVariable v, BasicBlock succ,
|
||||
boolean predSkipsFirstLoopAlwaysTrueUponEntry, boolean succSkipsFirstLoopAlwaysTrueUponEntry
|
||||
) {
|
||||
bbSuccessorEntryReachesLoopInvariantCand(source, pred, v, succ,
|
||||
predSkipsFirstLoopAlwaysTrueUponEntry, succSkipsFirstLoopAlwaysTrueUponEntry) and
|
||||
not exists(Condition cond, Condition direct |
|
||||
cond = getACondition(source, v, pred) and
|
||||
direct = pragma[only_bind_out](getADirectCondition(succ)) and
|
||||
cond.refutesCondition(direct)
|
||||
) and
|
||||
(
|
||||
// If we picked the successor edge corresponding to a condition being true, there must not be
|
||||
// another path condition that refutes that the condition is true.
|
||||
not exists(Condition cond | cond = getACondition(source, v, pred) |
|
||||
succ = pred.getATrueSuccessor() and
|
||||
cond.refutesCondition(pragma[only_bind_out](MkCondition(pred.getEnd(), true)))
|
||||
) and
|
||||
// If we picked the successor edge corresponding to a condition being false, there must not be
|
||||
// another path condition that refutes that the condition is false.
|
||||
not exists(Condition cond | cond = getACondition(source, v, pred) |
|
||||
succ = pred.getAFalseSuccessor() and
|
||||
cond.refutesCondition(pragma[only_bind_out](MkCondition(pred.getEnd(), false)))
|
||||
)
|
||||
bbSuccessorEntryReaches(bb, v, sink, _)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate bbSuccessorEntryReaches(
|
||||
ControlFlowNode source, BasicBlock bb, SemanticStackVariable v, ControlFlowNode node,
|
||||
BasicBlock bb, SemanticStackVariable v, ControlFlowNode node,
|
||||
boolean skipsFirstLoopAlwaysTrueUponEntry
|
||||
) {
|
||||
exists(BasicBlock succ, boolean succSkipsFirstLoopAlwaysTrueUponEntry |
|
||||
bbSuccessorEntryReachesLoopInvariant(source, bb, v, succ, skipsFirstLoopAlwaysTrueUponEntry,
|
||||
bbSuccessorEntryReachesLoopInvariant(bb, succ, skipsFirstLoopAlwaysTrueUponEntry,
|
||||
succSkipsFirstLoopAlwaysTrueUponEntry)
|
||||
|
|
||||
revBbEntryReachesLocally(succ, v, node, this) and
|
||||
bbEntryReachesLocally(succ, v, node) and
|
||||
succSkipsFirstLoopAlwaysTrueUponEntry = false
|
||||
or
|
||||
bbSuccessorEntryReachesLoopInvariant(source, bb, v, succ, skipsFirstLoopAlwaysTrueUponEntry,
|
||||
succSkipsFirstLoopAlwaysTrueUponEntry) and
|
||||
not isBarrier(pragma[only_bind_out](succ.getANode()), v) and
|
||||
pragma[only_bind_into](this)
|
||||
.bbSuccessorEntryReaches(source, succ, v, node, succSkipsFirstLoopAlwaysTrueUponEntry)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private predicate fwdBbEntryReachesLocally(
|
||||
BasicBlock bb, SemanticStackVariable v, ControlFlowNode node, StackVariableReachability config
|
||||
) {
|
||||
exists(int n |
|
||||
node = bb.getNode(n) and
|
||||
config.isSource(node, v) and
|
||||
(
|
||||
not exists(lastBarrierIndexIn(bb, v, config))
|
||||
or
|
||||
lastBarrierIndexIn(bb, v, config) <= n
|
||||
)
|
||||
not isBarrier(succ.getNode(_), v) and
|
||||
bbSuccessorEntryReaches(succ, v, node, succSkipsFirstLoopAlwaysTrueUponEntry)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate revBbEntryReachesLocally(
|
||||
BasicBlock bb, SemanticStackVariable v, ControlFlowNode node, StackVariableReachability config
|
||||
private predicate bbEntryReachesLocally(
|
||||
BasicBlock bb, SemanticStackVariable v, ControlFlowNode node
|
||||
) {
|
||||
exists(int n |
|
||||
node = bb.getNode(n) and
|
||||
config.isSink(node, v)
|
||||
isSink(node, v)
|
||||
|
|
||||
not exists(firstBarrierIndexIn(bb, v, config))
|
||||
not exists(this.firstBarrierIndexIn(bb, v))
|
||||
or
|
||||
n <= firstBarrierIndexIn(bb, v, config)
|
||||
n <= this.firstBarrierIndexIn(bb, v)
|
||||
)
|
||||
}
|
||||
|
||||
private int firstBarrierIndexIn(
|
||||
BasicBlock bb, SemanticStackVariable v, StackVariableReachability config
|
||||
) {
|
||||
result = min(int m | config.isBarrier(bb.getNode(m), v))
|
||||
private int firstBarrierIndexIn(BasicBlock bb, SemanticStackVariable v) {
|
||||
result = min(int m | isBarrier(bb.getNode(m), v))
|
||||
}
|
||||
|
||||
private int lastBarrierIndexIn(
|
||||
BasicBlock bb, SemanticStackVariable v, StackVariableReachability config
|
||||
) {
|
||||
result = max(int m | config.isBarrier(bb.getNode(m), v))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -564,7 +182,7 @@ private predicate bbLoopConditionAlwaysTrueUponEntrySuccessor(
|
||||
* is provably true upon entry, then `succ` is not allowed to skip
|
||||
* that loop (`succSkipsFirstLoopAlwaysTrueUponEntry = false`).
|
||||
*/
|
||||
predicate bbSuccessorEntryReachesLoopInvariant0(
|
||||
predicate bbSuccessorEntryReachesLoopInvariant(
|
||||
BasicBlock pred, BasicBlock succ, boolean predSkipsFirstLoopAlwaysTrueUponEntry,
|
||||
boolean succSkipsFirstLoopAlwaysTrueUponEntry
|
||||
) {
|
||||
@@ -678,52 +296,10 @@ abstract class StackVariableReachabilityWithReassignment extends StackVariableRe
|
||||
)
|
||||
}
|
||||
|
||||
private predicate bbSuccessorEntryReaches(
|
||||
BasicBlock bb, SemanticStackVariable v, ControlFlowNode node,
|
||||
boolean skipsFirstLoopAlwaysTrueUponEntry
|
||||
) {
|
||||
exists(BasicBlock succ, boolean succSkipsFirstLoopAlwaysTrueUponEntry |
|
||||
bbSuccessorEntryReachesLoopInvariant0(bb, succ, skipsFirstLoopAlwaysTrueUponEntry,
|
||||
succSkipsFirstLoopAlwaysTrueUponEntry)
|
||||
|
|
||||
revBbEntryReachesLocally(succ, v, node, this) and
|
||||
succSkipsFirstLoopAlwaysTrueUponEntry = false
|
||||
or
|
||||
not isBarrier(succ.getNode(_), v) and
|
||||
bbSuccessorEntryReaches(succ, v, node, succSkipsFirstLoopAlwaysTrueUponEntry)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate reaches0(ControlFlowNode source, SemanticStackVariable v, ControlFlowNode sink) {
|
||||
/*
|
||||
* Implementation detail: the predicates in this class are a generalization of
|
||||
* those in DefinitionsAndUses.qll, and should be kept in sync.
|
||||
*
|
||||
* Unfortunately, caching of abstract predicates does not work well, so the
|
||||
* predicates in DefinitionsAndUses.qll cannot use this library.
|
||||
*/
|
||||
|
||||
exists(BasicBlock bb, int i |
|
||||
isSource(source, v) and
|
||||
bb.getNode(i) = source and
|
||||
not bb.isUnreachable()
|
||||
|
|
||||
exists(int j |
|
||||
j > i and
|
||||
sink = bb.getNode(j) and
|
||||
isSink(sink, v) and
|
||||
not isBarrier(bb.getNode(pragma[only_bind_into]([i + 1 .. j - 1])), v)
|
||||
)
|
||||
or
|
||||
not exists(int k | isBarrier(bb.getNode(k), v) | k > i) and
|
||||
bbSuccessorEntryReaches(bb, v, sink, _)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate reassignment(
|
||||
ControlFlowNode source, SemanticStackVariable v, ControlFlowNode def, SemanticStackVariable v0
|
||||
) {
|
||||
reaches0(source, v, def) and
|
||||
StackVariableReachability.super.reaches(source, v, def) and
|
||||
exprDefinition(v0, def, v.getAnAccess())
|
||||
}
|
||||
|
||||
@@ -789,11 +365,11 @@ abstract class StackVariableReachabilityExt extends string {
|
||||
boolean skipsFirstLoopAlwaysTrueUponEntry
|
||||
) {
|
||||
exists(BasicBlock succ, boolean succSkipsFirstLoopAlwaysTrueUponEntry |
|
||||
bbSuccessorEntryReachesLoopInvariant0(bb, succ, skipsFirstLoopAlwaysTrueUponEntry,
|
||||
bbSuccessorEntryReachesLoopInvariant(bb, succ, skipsFirstLoopAlwaysTrueUponEntry,
|
||||
succSkipsFirstLoopAlwaysTrueUponEntry) and
|
||||
not isBarrier(source, bb.getEnd(), succ.getStart(), v)
|
||||
|
|
||||
this.bbEntryReachesLocally(source, succ, v, node) and
|
||||
bbEntryReachesLocally(source, succ, v, node) and
|
||||
succSkipsFirstLoopAlwaysTrueUponEntry = false
|
||||
or
|
||||
not exists(int k | isBarrier(source, succ.getNode(k), succ.getNode(k + 1), v)) and
|
||||
|
||||
@@ -735,7 +735,12 @@ private module FieldFlow {
|
||||
private class FieldConfiguration extends Configuration {
|
||||
FieldConfiguration() { this = "FieldConfiguration" }
|
||||
|
||||
override predicate isSource(Node source) { storeStep(source, _, _) }
|
||||
override predicate isSource(Node source) {
|
||||
storeStep(source, _, _)
|
||||
or
|
||||
// Also mark `foo(a.b);` as a source when `a.b` may be overwritten by `foo`.
|
||||
readStep(_, _, any(Node node | node.asExpr() = source.asDefiningArgument()))
|
||||
}
|
||||
|
||||
override predicate isSink(Node sink) { readStep(_, _, sink) }
|
||||
|
||||
|
||||
@@ -19,6 +19,15 @@ class FileWrite extends Expr {
|
||||
* Gets the expression for the object being written to.
|
||||
*/
|
||||
Expr getDest() { fileWrite(this, _, result) }
|
||||
|
||||
/**
|
||||
* Gets the conversion character for this write, if it exists and is known. For example in the following code the write of `value1` has conversion character `"s"`, whereas the write of `value2` has no conversion specifier.
|
||||
* ```
|
||||
* fprintf(file, "%s", value1);
|
||||
* stream << value2;
|
||||
* ```
|
||||
*/
|
||||
string getSourceConvChar(Expr source) { fileWriteWithConvChar(this, source, result) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -150,3 +159,20 @@ private predicate fileWrite(Call write, Expr source, Expr dest) {
|
||||
// file stream using '<<', 'put' or 'write'
|
||||
fileStreamChain(write, source, dest)
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the function call is a write to a file from 'source' with
|
||||
* conversion character 'conv'. Does not hold if there isn't a conversion
|
||||
* character, or if it is unknown (for example the format string is not a
|
||||
* constant).
|
||||
*/
|
||||
private predicate fileWriteWithConvChar(FormattingFunctionCall ffc, Expr source, string conv) {
|
||||
// fprintf
|
||||
exists(FormattingFunction f, int n |
|
||||
f = ffc.getTarget() and
|
||||
source = ffc.getFormatArgument(n)
|
||||
|
|
||||
exists(f.getOutputParameterIndex(true)) and
|
||||
conv = ffc.(FormattingFunctionCall).getFormat().(FormatLiteral).getConversionChar(n)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.controlflow.Dominance
|
||||
// `GlobalValueNumbering` is only imported to prevent IR re-evaluation.
|
||||
private import semmle.code.cpp.valuenumbering.GlobalValueNumbering
|
||||
import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
|
||||
import semmle.code.cpp.rangeanalysis.RangeAnalysisUtils
|
||||
|
||||
|
||||
@@ -14,14 +14,13 @@ private predicate suspicious(string s) {
|
||||
(
|
||||
s.matches("%password%") or
|
||||
s.matches("%passwd%") or
|
||||
s.matches("%account%") or
|
||||
s.matches("%accnt%") or
|
||||
s.matches("%trusted%")
|
||||
) and
|
||||
not (
|
||||
s.matches("%hashed%") or
|
||||
s.matches("%encrypted%") or
|
||||
s.matches("%crypt%")
|
||||
s.matches("%hash%") or
|
||||
s.matches("%crypt%") or
|
||||
s.matches("%file%") or
|
||||
s.matches("%path%")
|
||||
)
|
||||
}
|
||||
|
||||
@@ -29,14 +28,20 @@ private predicate suspicious(string s) {
|
||||
* A variable that might contain a password or other sensitive information.
|
||||
*/
|
||||
class SensitiveVariable extends Variable {
|
||||
SensitiveVariable() { suspicious(getName().toLowerCase()) }
|
||||
SensitiveVariable() {
|
||||
suspicious(getName().toLowerCase()) and
|
||||
not this.getUnspecifiedType() instanceof IntegralType
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A function that might return a password or other sensitive information.
|
||||
*/
|
||||
class SensitiveFunction extends Function {
|
||||
SensitiveFunction() { suspicious(getName().toLowerCase()) }
|
||||
SensitiveFunction() {
|
||||
suspicious(getName().toLowerCase()) and
|
||||
not this.getUnspecifiedType() instanceof IntegralType
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -79,6 +79,7 @@
|
||||
| file://:0:0:0:0 | declaration of 1st parameter |
|
||||
| file://:0:0:0:0 | declared_constexpr |
|
||||
| file://:0:0:0:0 | declared_constinit |
|
||||
| file://:0:0:0:0 | declared_virtual |
|
||||
| file://:0:0:0:0 | decltype(nullptr) |
|
||||
| file://:0:0:0:0 | definition of fp_offset |
|
||||
| file://:0:0:0:0 | definition of gp_offset |
|
||||
@@ -91,6 +92,7 @@
|
||||
| file://:0:0:0:0 | explicit |
|
||||
| file://:0:0:0:0 | extern |
|
||||
| file://:0:0:0:0 | far |
|
||||
| file://:0:0:0:0 | final |
|
||||
| file://:0:0:0:0 | float |
|
||||
| file://:0:0:0:0 | forceinline |
|
||||
| file://:0:0:0:0 | fp_offset |
|
||||
|
||||
@@ -49,6 +49,7 @@
|
||||
| file://:0:0:0:0 | const __va_list_tag & |
|
||||
| file://:0:0:0:0 | declared_constexpr |
|
||||
| file://:0:0:0:0 | declared_constinit |
|
||||
| file://:0:0:0:0 | declared_virtual |
|
||||
| file://:0:0:0:0 | decltype(nullptr) |
|
||||
| file://:0:0:0:0 | definition of <error> |
|
||||
| file://:0:0:0:0 | definition of fp_offset |
|
||||
@@ -62,6 +63,7 @@
|
||||
| file://:0:0:0:0 | explicit |
|
||||
| file://:0:0:0:0 | extern |
|
||||
| file://:0:0:0:0 | far |
|
||||
| file://:0:0:0:0 | final |
|
||||
| file://:0:0:0:0 | float |
|
||||
| file://:0:0:0:0 | forceinline |
|
||||
| file://:0:0:0:0 | fp_offset |
|
||||
|
||||
@@ -70,3 +70,8 @@
|
||||
| test.cpp:391:11:391:13 | tmp | test.cpp:391:10:391:13 | & ... |
|
||||
| test.cpp:391:17:391:23 | source1 | test.cpp:391:10:391:13 | ref arg & ... |
|
||||
| test.cpp:391:17:391:23 | source1 | test.cpp:391:16:391:23 | & ... |
|
||||
| test.cpp:480:67:480:67 | s | test.cpp:481:21:481:21 | s |
|
||||
| test.cpp:480:67:480:67 | s | test.cpp:482:20:482:20 | s |
|
||||
| test.cpp:481:21:481:21 | s [post update] | test.cpp:482:20:482:20 | s |
|
||||
| test.cpp:481:24:481:30 | ref arg content | test.cpp:482:23:482:29 | content |
|
||||
| test.cpp:482:23:482:29 | content | test.cpp:483:9:483:17 | p_content |
|
||||
|
||||
@@ -470,3 +470,15 @@ void viaOutparam() {
|
||||
intOutparamSource(&x);
|
||||
sink(x); // $ ast,ir
|
||||
}
|
||||
|
||||
void writes_to_content(void*);
|
||||
|
||||
struct MyStruct {
|
||||
int* content;
|
||||
};
|
||||
|
||||
void local_field_flow_def_by_ref_steps_with_local_flow(MyStruct * s) {
|
||||
writes_to_content(s->content);
|
||||
int* p_content = s->content;
|
||||
sink(*p_content);
|
||||
}
|
||||
@@ -496,9 +496,13 @@
|
||||
| map.cpp:49:7:49:7 | f [post update] | map.cpp:51:7:51:7 | f | |
|
||||
| map.cpp:49:7:49:7 | f [post update] | map.cpp:53:30:53:30 | f | |
|
||||
| map.cpp:49:7:49:7 | f [post update] | map.cpp:59:6:59:6 | f | |
|
||||
| map.cpp:49:9:49:13 | ref arg first | map.cpp:54:9:54:13 | first | |
|
||||
| map.cpp:49:9:49:13 | ref arg first | map.cpp:60:9:60:13 | first | |
|
||||
| map.cpp:50:7:50:7 | f [post update] | map.cpp:51:7:51:7 | f | |
|
||||
| map.cpp:50:7:50:7 | f [post update] | map.cpp:53:30:53:30 | f | |
|
||||
| map.cpp:50:7:50:7 | f [post update] | map.cpp:59:6:59:6 | f | |
|
||||
| map.cpp:50:9:50:14 | ref arg second | map.cpp:55:9:55:14 | second | |
|
||||
| map.cpp:50:9:50:14 | ref arg second | map.cpp:61:9:61:14 | second | |
|
||||
| map.cpp:53:30:53:30 | f | map.cpp:54:7:54:7 | g | |
|
||||
| map.cpp:53:30:53:30 | f | map.cpp:55:7:55:7 | g | |
|
||||
| map.cpp:53:30:53:30 | f | map.cpp:56:7:56:7 | g | |
|
||||
@@ -3395,6 +3399,7 @@
|
||||
| smart_pointer.cpp:125:20:125:20 | call to operator-> [post update] | smart_pointer.cpp:125:18:125:19 | ref arg p1 | TAINT |
|
||||
| smart_pointer.cpp:125:22:125:22 | q | smart_pointer.cpp:125:18:125:22 | call to shared_ptr | |
|
||||
| smart_pointer.cpp:125:22:125:22 | ref arg q | smart_pointer.cpp:125:22:125:22 | q [inner post update] | |
|
||||
| smart_pointer.cpp:125:22:125:22 | ref arg q | smart_pointer.cpp:126:12:126:12 | q | |
|
||||
| smart_pointer.cpp:126:8:126:9 | p1 | smart_pointer.cpp:126:10:126:10 | call to operator-> | |
|
||||
| smart_pointer.cpp:126:8:126:9 | ref arg p1 | smart_pointer.cpp:124:48:124:49 | p1 | |
|
||||
| smart_pointer.cpp:126:10:126:10 | call to operator-> [post update] | smart_pointer.cpp:126:8:126:9 | ref arg p1 | TAINT |
|
||||
@@ -3432,6 +3437,7 @@
|
||||
| smart_pointer.cpp:133:23:133:24 | ref arg p1 | smart_pointer.cpp:132:53:132:54 | p1 | |
|
||||
| smart_pointer.cpp:133:23:133:24 | ref arg p1 | smart_pointer.cpp:134:8:134:9 | p1 | |
|
||||
| smart_pointer.cpp:133:25:133:25 | call to operator-> [post update] | smart_pointer.cpp:133:23:133:24 | ref arg p1 | TAINT |
|
||||
| smart_pointer.cpp:133:27:133:27 | ref arg q | smart_pointer.cpp:134:12:134:12 | q | |
|
||||
| smart_pointer.cpp:134:8:134:9 | p1 | smart_pointer.cpp:134:10:134:10 | call to operator-> | |
|
||||
| smart_pointer.cpp:134:8:134:9 | ref arg p1 | smart_pointer.cpp:132:53:132:54 | p1 | |
|
||||
| smart_pointer.cpp:134:10:134:10 | call to operator-> [post update] | smart_pointer.cpp:134:8:134:9 | ref arg p1 | TAINT |
|
||||
@@ -6435,6 +6441,7 @@
|
||||
| taint.cpp:669:18:669:18 | s [post update] | taint.cpp:671:7:671:7 | s | |
|
||||
| taint.cpp:669:18:669:18 | s [post update] | taint.cpp:672:7:672:7 | s | |
|
||||
| taint.cpp:669:18:669:18 | s [post update] | taint.cpp:673:7:673:7 | s | |
|
||||
| taint.cpp:669:20:669:20 | ref arg x | taint.cpp:672:9:672:9 | x | |
|
||||
| taint.cpp:672:7:672:7 | s [post update] | taint.cpp:673:7:673:7 | s | |
|
||||
| vector.cpp:16:43:16:49 | source1 | vector.cpp:17:26:17:32 | source1 | |
|
||||
| vector.cpp:16:43:16:49 | source1 | vector.cpp:31:38:31:44 | source1 | |
|
||||
@@ -7076,14 +7083,20 @@
|
||||
| vector.cpp:198:3:198:4 | ee [post update] | vector.cpp:200:3:200:4 | ee | |
|
||||
| vector.cpp:198:3:198:4 | ee [post update] | vector.cpp:201:8:201:9 | ee | |
|
||||
| vector.cpp:198:3:198:4 | ee [post update] | vector.cpp:202:2:202:2 | ee | |
|
||||
| vector.cpp:198:6:198:7 | ref arg vs | vector.cpp:199:11:199:12 | vs | |
|
||||
| vector.cpp:198:6:198:7 | ref arg vs | vector.cpp:200:6:200:7 | vs | |
|
||||
| vector.cpp:198:6:198:7 | ref arg vs | vector.cpp:201:11:201:12 | vs | |
|
||||
| vector.cpp:198:19:198:19 | 0 | vector.cpp:198:6:198:7 | ref arg vs | TAINT |
|
||||
| vector.cpp:199:8:199:9 | ee [post update] | vector.cpp:200:3:200:4 | ee | |
|
||||
| vector.cpp:199:8:199:9 | ee [post update] | vector.cpp:201:8:201:9 | ee | |
|
||||
| vector.cpp:199:8:199:9 | ee [post update] | vector.cpp:202:2:202:2 | ee | |
|
||||
| vector.cpp:199:11:199:12 | ref arg vs | vector.cpp:200:6:200:7 | vs | |
|
||||
| vector.cpp:199:11:199:12 | ref arg vs | vector.cpp:201:11:201:12 | vs | |
|
||||
| vector.cpp:199:11:199:12 | vs | vector.cpp:199:13:199:13 | call to operator[] | TAINT |
|
||||
| vector.cpp:200:3:200:4 | ee [post update] | vector.cpp:201:8:201:9 | ee | |
|
||||
| vector.cpp:200:3:200:4 | ee [post update] | vector.cpp:202:2:202:2 | ee | |
|
||||
| vector.cpp:200:3:200:21 | ... = ... | vector.cpp:200:8:200:8 | call to operator[] [post update] | |
|
||||
| vector.cpp:200:6:200:7 | ref arg vs | vector.cpp:201:11:201:12 | vs | |
|
||||
| vector.cpp:200:6:200:7 | vs | vector.cpp:200:8:200:8 | call to operator[] | TAINT |
|
||||
| vector.cpp:200:8:200:8 | call to operator[] [post update] | vector.cpp:200:6:200:7 | ref arg vs | TAINT |
|
||||
| vector.cpp:200:14:200:19 | call to source | vector.cpp:200:3:200:21 | ... = ... | |
|
||||
|
||||
@@ -1,21 +1,13 @@
|
||||
| fields.cpp:3:8:3:12 | Entry | fields.cpp:4:9:4:12 | name | public | CharPointerType | char |
|
||||
| fields.cpp:3:8:3:12 | Entry | fields.cpp:4:9:4:12 | name | public | PointerDumpType | char |
|
||||
| fields.cpp:3:8:3:12 | Entry | fields.cpp:5:8:5:8 | t | public | Enum | |
|
||||
| fields.cpp:3:8:3:12 | Entry | fields.cpp:5:8:5:8 | t | public | UserDumpType | |
|
||||
| fields.cpp:3:8:3:12 | Entry | fields.cpp:6:9:6:9 | s | public | CharPointerType | char |
|
||||
| fields.cpp:3:8:3:12 | Entry | fields.cpp:6:9:6:9 | s | public | PointerDumpType | char |
|
||||
| fields.cpp:3:8:3:12 | Entry | fields.cpp:7:7:7:7 | i | public | IntType | |
|
||||
| fields.cpp:3:8:3:12 | Entry | fields.cpp:7:7:7:7 | i | public | IntegralDumpType | |
|
||||
| fields.cpp:3:8:3:12 | Entry | fields.cpp:7:7:7:7 | i | public | MicrosoftInt32Type | |
|
||||
| fields.cpp:3:8:3:12 | Entry | fields.cpp:9:7:9:14 | internal | private | IntType | |
|
||||
| fields.cpp:3:8:3:12 | Entry | fields.cpp:9:7:9:14 | internal | private | IntegralDumpType | |
|
||||
| fields.cpp:3:8:3:12 | Entry | fields.cpp:9:7:9:14 | internal | private | MicrosoftInt32Type | |
|
||||
| fields.cpp:12:7:12:10 | Name | fields.cpp:13:15:13:15 | s | private | PointerDumpType | const char |
|
||||
| fields.cpp:16:7:16:11 | Table | fields.cpp:17:9:17:9 | p | private | PointerDumpType | Name |
|
||||
| fields.cpp:12:7:12:10 | Name | fields.cpp:13:15:13:15 | s | private | PointerType | const char |
|
||||
| fields.cpp:16:7:16:11 | Table | fields.cpp:17:9:17:9 | p | private | PointerType | Name |
|
||||
| fields.cpp:16:7:16:11 | Table | fields.cpp:18:7:18:8 | sz | private | IntType | |
|
||||
| fields.cpp:16:7:16:11 | Table | fields.cpp:18:7:18:8 | sz | private | IntegralDumpType | |
|
||||
| fields.cpp:16:7:16:11 | Table | fields.cpp:18:7:18:8 | sz | private | MicrosoftInt32Type | |
|
||||
| fields.cpp:26:7:26:10 | Date | fields.cpp:28:16:28:26 | cache_valid | private | BoolType | |
|
||||
| fields.cpp:26:7:26:10 | Date | fields.cpp:28:16:28:26 | cache_valid | private | IntegralDumpType | |
|
||||
| fields.cpp:26:7:26:10 | Date | fields.cpp:30:17:30:21 | cache | public | CharPointerType | char |
|
||||
| fields.cpp:26:7:26:10 | Date | fields.cpp:30:17:30:21 | cache | public | PointerDumpType | char |
|
||||
|
||||
@@ -1 +1 @@
|
||||
| routinetype.cpp:2:7:2:19 | myRoutineType | file://:0:0:0:0 | ..()(..) | PrintableElement, RoutineDumpType |
|
||||
| routinetype.cpp:2:7:2:19 | myRoutineType | file://:0:0:0:0 | ..()(..) | RoutineType |
|
||||
|
||||
@@ -43,10 +43,12 @@
|
||||
| Function | specifiers2pp.cpp:10:18:10:24 | MyClass | MyClass | public |
|
||||
| Function | specifiers2pp.cpp:12:14:12:22 | publicFun | publicFun | inline |
|
||||
| Function | specifiers2pp.cpp:12:14:12:22 | publicFun | publicFun | public |
|
||||
| Function | specifiers2pp.cpp:13:21:13:26 | getInt | getInt | declared_virtual |
|
||||
| Function | specifiers2pp.cpp:13:21:13:26 | getInt | getInt | extern |
|
||||
| Function | specifiers2pp.cpp:13:21:13:26 | getInt | getInt | public |
|
||||
| Function | specifiers2pp.cpp:13:21:13:26 | getInt | getInt | pure |
|
||||
| Function | specifiers2pp.cpp:13:21:13:26 | getInt | getInt | virtual |
|
||||
| Function | specifiers2pp.cpp:14:21:14:21 | f | f | declared_virtual |
|
||||
| Function | specifiers2pp.cpp:14:21:14:21 | f | f | extern |
|
||||
| Function | specifiers2pp.cpp:14:21:14:21 | f | f | public |
|
||||
| Function | specifiers2pp.cpp:14:21:14:21 | f | f | virtual |
|
||||
@@ -71,6 +73,7 @@
|
||||
| Function | specifiers2pp.cpp:24:7:24:7 | operator= | operator= | inline |
|
||||
| Function | specifiers2pp.cpp:24:7:24:7 | operator= | operator= | public |
|
||||
| Function | specifiers2pp.cpp:24:7:24:7 | operator= | operator= | public |
|
||||
| Function | specifiers2pp.cpp:26:21:26:21 | f | f | declared_virtual |
|
||||
| Function | specifiers2pp.cpp:26:21:26:21 | f | f | extern |
|
||||
| Function | specifiers2pp.cpp:26:21:26:21 | f | f | override |
|
||||
| Function | specifiers2pp.cpp:26:21:26:21 | f | f | public |
|
||||
|
||||
@@ -109,6 +109,7 @@
|
||||
| file://:0:0:0:0 | declaration of 1st parameter |
|
||||
| file://:0:0:0:0 | declared_constexpr |
|
||||
| file://:0:0:0:0 | declared_constinit |
|
||||
| file://:0:0:0:0 | declared_virtual |
|
||||
| file://:0:0:0:0 | decltype(nullptr) |
|
||||
| file://:0:0:0:0 | definition of fp_offset |
|
||||
| file://:0:0:0:0 | definition of gp_offset |
|
||||
@@ -121,6 +122,7 @@
|
||||
| file://:0:0:0:0 | explicit |
|
||||
| file://:0:0:0:0 | extern |
|
||||
| file://:0:0:0:0 | far |
|
||||
| file://:0:0:0:0 | final |
|
||||
| file://:0:0:0:0 | float |
|
||||
| file://:0:0:0:0 | forceinline |
|
||||
| file://:0:0:0:0 | fp_offset |
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
| typedefs.cpp:6:6:6:7 | f1 | typedefs.cpp:8:15:8:18 | TYPE | CTypedefType, LocalTypedefType, PrintableElement, UserDumpType |
|
||||
| typedefs.cpp:6:6:6:7 | f1 | typedefs.cpp:9:9:9:9 | D | DirectAccessHolder, LocalClass, MetricClass, PrintableElement, StructLikeClass, UserDumpType |
|
||||
| typedefs.cpp:6:6:6:7 | f1 | typedefs.cpp:8:15:8:18 | TYPE | CTypedefType, LocalTypedefType |
|
||||
| typedefs.cpp:6:6:6:7 | f1 | typedefs.cpp:9:9:9:9 | D | DirectAccessHolder, LocalClass, MetricClass, StructLikeClass |
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
| file://:0:0:0:0 | __wchar_t * | PointerDumpType, PrintableElement | IntegralDumpType, PrintableElement, Wchar_t, WideCharType |
|
||||
| file://:0:0:0:0 | const __wchar_t | PrintableElement, SpecifiedDumpType | IntegralDumpType, PrintableElement, Wchar_t, WideCharType |
|
||||
| file://:0:0:0:0 | wchar_t | IntegralDumpType, PrintableElement, Wchar_t, WideCharType | |
|
||||
| file://:0:0:0:0 | __wchar_t * | PointerType | Wchar_t, WideCharType |
|
||||
| file://:0:0:0:0 | const __wchar_t | SpecifiedType | Wchar_t, WideCharType |
|
||||
| file://:0:0:0:0 | wchar_t | Wchar_t, WideCharType | |
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
| cstd_types.cpp:47:13:47:15 | if8 | CTypedefType, FastestMinimumWidthIntegralType, Int_fast8_t, PrintableElement, UserDumpType |
|
||||
| cstd_types.cpp:48:14:48:17 | if16 | CTypedefType, FastestMinimumWidthIntegralType, Int_fast16_t, PrintableElement, UserDumpType |
|
||||
| cstd_types.cpp:49:14:49:17 | if32 | CTypedefType, FastestMinimumWidthIntegralType, Int_fast32_t, PrintableElement, UserDumpType |
|
||||
| cstd_types.cpp:50:14:50:17 | if64 | CTypedefType, FastestMinimumWidthIntegralType, Int_fast64_t, PrintableElement, UserDumpType |
|
||||
| cstd_types.cpp:51:14:51:16 | uf8 | CTypedefType, FastestMinimumWidthIntegralType, PrintableElement, UInt_fast8_t, UserDumpType |
|
||||
| cstd_types.cpp:52:15:52:18 | uf16 | CTypedefType, FastestMinimumWidthIntegralType, PrintableElement, UInt_fast16_t, UserDumpType |
|
||||
| cstd_types.cpp:53:15:53:18 | uf32 | CTypedefType, FastestMinimumWidthIntegralType, PrintableElement, UInt_fast32_t, UserDumpType |
|
||||
| cstd_types.cpp:54:15:54:18 | uf64 | CTypedefType, FastestMinimumWidthIntegralType, PrintableElement, UInt_fast64_t, UserDumpType |
|
||||
| cstd_types.cpp:47:13:47:15 | if8 | CTypedefType, FastestMinimumWidthIntegralType, Int_fast8_t |
|
||||
| cstd_types.cpp:48:14:48:17 | if16 | CTypedefType, FastestMinimumWidthIntegralType, Int_fast16_t |
|
||||
| cstd_types.cpp:49:14:49:17 | if32 | CTypedefType, FastestMinimumWidthIntegralType, Int_fast32_t |
|
||||
| cstd_types.cpp:50:14:50:17 | if64 | CTypedefType, FastestMinimumWidthIntegralType, Int_fast64_t |
|
||||
| cstd_types.cpp:51:14:51:16 | uf8 | CTypedefType, FastestMinimumWidthIntegralType, UInt_fast8_t |
|
||||
| cstd_types.cpp:52:15:52:18 | uf16 | CTypedefType, FastestMinimumWidthIntegralType, UInt_fast16_t |
|
||||
| cstd_types.cpp:53:15:53:18 | uf32 | CTypedefType, FastestMinimumWidthIntegralType, UInt_fast32_t |
|
||||
| cstd_types.cpp:54:15:54:18 | uf64 | CTypedefType, FastestMinimumWidthIntegralType, UInt_fast64_t |
|
||||
@@ -1,8 +1,8 @@
|
||||
| cstd_types.cpp:31:8:31:9 | i8 | CTypedefType, FixedWidthIntegralType, Int8_t, PrintableElement, UserDumpType |
|
||||
| cstd_types.cpp:32:9:32:11 | i16 | CTypedefType, FixedWidthIntegralType, Int16_t, PrintableElement, UserDumpType |
|
||||
| cstd_types.cpp:33:9:33:11 | i32 | CTypedefType, FixedWidthIntegralType, Int32_t, PrintableElement, UserDumpType |
|
||||
| cstd_types.cpp:34:9:34:11 | i64 | CTypedefType, FixedWidthIntegralType, Int64_t, PrintableElement, UserDumpType |
|
||||
| cstd_types.cpp:35:9:35:11 | ui8 | CTypedefType, FixedWidthIntegralType, PrintableElement, UInt8_t, UserDumpType |
|
||||
| cstd_types.cpp:36:10:36:13 | ui16 | CTypedefType, FixedWidthIntegralType, PrintableElement, UInt16_t, UserDumpType |
|
||||
| cstd_types.cpp:37:10:37:13 | ui32 | CTypedefType, FixedWidthIntegralType, PrintableElement, UInt32_t, UserDumpType |
|
||||
| cstd_types.cpp:38:10:38:13 | ui64 | CTypedefType, FixedWidthIntegralType, PrintableElement, UInt64_t, UserDumpType |
|
||||
| cstd_types.cpp:31:8:31:9 | i8 | CTypedefType, FixedWidthIntegralType, Int8_t |
|
||||
| cstd_types.cpp:32:9:32:11 | i16 | CTypedefType, FixedWidthIntegralType, Int16_t |
|
||||
| cstd_types.cpp:33:9:33:11 | i32 | CTypedefType, FixedWidthIntegralType, Int32_t |
|
||||
| cstd_types.cpp:34:9:34:11 | i64 | CTypedefType, FixedWidthIntegralType, Int64_t |
|
||||
| cstd_types.cpp:35:9:35:11 | ui8 | CTypedefType, FixedWidthIntegralType, UInt8_t |
|
||||
| cstd_types.cpp:36:10:36:13 | ui16 | CTypedefType, FixedWidthIntegralType, UInt16_t |
|
||||
| cstd_types.cpp:37:10:37:13 | ui32 | CTypedefType, FixedWidthIntegralType, UInt32_t |
|
||||
| cstd_types.cpp:38:10:38:13 | ui64 | CTypedefType, FixedWidthIntegralType, UInt64_t |
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
| cstd_types.cpp:74:4:74:6 | _e0 | Enum, FixedWidthEnumType, PrintableElement, UserDumpType |
|
||||
| cstd_types.cpp:75:4:75:6 | _e1 | FixedWidthEnumType, PrintableElement, ScopedEnum, UserDumpType |
|
||||
| cstd_types.cpp:74:4:74:6 | _e0 | Enum, FixedWidthEnumType |
|
||||
| cstd_types.cpp:75:4:75:6 | _e1 | FixedWidthEnumType, ScopedEnum |
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
| cstd_types.cpp:55:10:55:11 | im | CTypedefType, Intmax_t, MaximumWidthIntegralType, PrintableElement, UserDumpType |
|
||||
| cstd_types.cpp:56:11:56:13 | uim | CTypedefType, MaximumWidthIntegralType, PrintableElement, Uintmax_t, UserDumpType |
|
||||
| cstd_types.cpp:55:10:55:11 | im | CTypedefType, Intmax_t, MaximumWidthIntegralType |
|
||||
| cstd_types.cpp:56:11:56:13 | uim | CTypedefType, MaximumWidthIntegralType, Uintmax_t |
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
| cstd_types.cpp:39:15:39:16 | l8 | CTypedefType, Int_least8_t, MinimumWidthIntegralType, PrintableElement, UserDumpType |
|
||||
| cstd_types.cpp:40:15:40:17 | l16 | CTypedefType, Int_least16_t, MinimumWidthIntegralType, PrintableElement, UserDumpType |
|
||||
| cstd_types.cpp:41:15:41:17 | l32 | CTypedefType, Int_least32_t, MinimumWidthIntegralType, PrintableElement, UserDumpType |
|
||||
| cstd_types.cpp:42:15:42:17 | l64 | CTypedefType, Int_least64_t, MinimumWidthIntegralType, PrintableElement, UserDumpType |
|
||||
| cstd_types.cpp:43:15:43:17 | ul8 | CTypedefType, MinimumWidthIntegralType, PrintableElement, UInt_least8_t, UserDumpType |
|
||||
| cstd_types.cpp:44:16:44:19 | ul16 | CTypedefType, MinimumWidthIntegralType, PrintableElement, UInt_least16_t, UserDumpType |
|
||||
| cstd_types.cpp:45:16:45:19 | ul32 | CTypedefType, MinimumWidthIntegralType, PrintableElement, UInt_least32_t, UserDumpType |
|
||||
| cstd_types.cpp:46:16:46:19 | ul64 | CTypedefType, MinimumWidthIntegralType, PrintableElement, UInt_least64_t, UserDumpType |
|
||||
| cstd_types.cpp:39:15:39:16 | l8 | CTypedefType, Int_least8_t, MinimumWidthIntegralType |
|
||||
| cstd_types.cpp:40:15:40:17 | l16 | CTypedefType, Int_least16_t, MinimumWidthIntegralType |
|
||||
| cstd_types.cpp:41:15:41:17 | l32 | CTypedefType, Int_least32_t, MinimumWidthIntegralType |
|
||||
| cstd_types.cpp:42:15:42:17 | l64 | CTypedefType, Int_least64_t, MinimumWidthIntegralType |
|
||||
| cstd_types.cpp:43:15:43:17 | ul8 | CTypedefType, MinimumWidthIntegralType, UInt_least8_t |
|
||||
| cstd_types.cpp:44:16:44:19 | ul16 | CTypedefType, MinimumWidthIntegralType, UInt_least16_t |
|
||||
| cstd_types.cpp:45:16:45:19 | ul32 | CTypedefType, MinimumWidthIntegralType, UInt_least32_t |
|
||||
| cstd_types.cpp:46:16:46:19 | ul64 | CTypedefType, MinimumWidthIntegralType, UInt_least64_t |
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
| integral_types.cpp:2:8:2:9 | i8 | file://:0:0:0:0 | char | IntegralDumpType, MicrosoftInt8Type, PlainCharType, PrintableElement |
|
||||
| integral_types.cpp:3:9:3:11 | i16 | file://:0:0:0:0 | short | IntegralDumpType, MicrosoftInt16Type, PrintableElement, ShortType |
|
||||
| integral_types.cpp:4:9:4:11 | i32 | file://:0:0:0:0 | int | IntType, IntegralDumpType, MicrosoftInt32Type, PrintableElement |
|
||||
| integral_types.cpp:5:9:5:11 | i64 | file://:0:0:0:0 | long long | IntegralDumpType, LongLongType, MicrosoftInt64Type, PrintableElement |
|
||||
| integral_types.cpp:2:8:2:9 | i8 | file://:0:0:0:0 | char | MicrosoftInt8Type, PlainCharType |
|
||||
| integral_types.cpp:3:9:3:11 | i16 | file://:0:0:0:0 | short | MicrosoftInt16Type, ShortType |
|
||||
| integral_types.cpp:4:9:4:11 | i32 | file://:0:0:0:0 | int | IntType, MicrosoftInt32Type |
|
||||
| integral_types.cpp:5:9:5:11 | i64 | file://:0:0:0:0 | long long | LongLongType, MicrosoftInt64Type |
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
| file://:0:0:0:0 | wchar_t | IntegralDumpType, PrintableElement, Wchar_t, WideCharType | |
|
||||
| file://:0:0:0:0 | wchar_t * | PointerDumpType, PrintableElement | CTypedefType, PrintableElement, UserDumpType, Wchar_t |
|
||||
| ms.c:2:24:2:30 | wchar_t | CTypedefType, PrintableElement, UserDumpType, Wchar_t | |
|
||||
| file://:0:0:0:0 | wchar_t | Wchar_t, WideCharType | |
|
||||
| file://:0:0:0:0 | wchar_t * | PointerType | CTypedefType, Wchar_t |
|
||||
| ms.c:2:24:2:30 | wchar_t | CTypedefType, Wchar_t | |
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
| file://:0:0:0:0 | const | Other |
|
||||
| file://:0:0:0:0 | declared_constexpr | Other |
|
||||
| file://:0:0:0:0 | declared_constinit | Other |
|
||||
| file://:0:0:0:0 | declared_virtual | Other |
|
||||
| file://:0:0:0:0 | decltype(nullptr) | Other |
|
||||
| file://:0:0:0:0 | definition of fp_offset | Other |
|
||||
| file://:0:0:0:0 | definition of gp_offset | Other |
|
||||
@@ -53,6 +54,7 @@
|
||||
| file://:0:0:0:0 | explicit | Other |
|
||||
| file://:0:0:0:0 | extern | Other |
|
||||
| file://:0:0:0:0 | far | Other |
|
||||
| file://:0:0:0:0 | final | Other |
|
||||
| file://:0:0:0:0 | float | Other |
|
||||
| file://:0:0:0:0 | forceinline | Other |
|
||||
| file://:0:0:0:0 | fp_offset | Other |
|
||||
|
||||
@@ -1,94 +1,94 @@
|
||||
| ..()(..) | PrintableElement, RoutineDumpType | | | | |
|
||||
| ..(*)(..) | FunctionPointerDumpType, PrintableElement | | ..()(..) | | |
|
||||
| _Complex __float128 | BinaryFloatingPointType, BuiltInDumpType, ComplexNumberType, PrintableElement | | | | |
|
||||
| _Complex double | BinaryFloatingPointType, BuiltInDumpType, ComplexNumberType, PrintableElement | | | | |
|
||||
| _Complex float | BinaryFloatingPointType, BuiltInDumpType, ComplexNumberType, PrintableElement | | | | |
|
||||
| _Complex long double | BinaryFloatingPointType, BuiltInDumpType, ComplexNumberType, PrintableElement | | | | |
|
||||
| _Decimal32 | BuiltInDumpType, Decimal32Type, PrintableElement | | | | |
|
||||
| _Decimal64 | BuiltInDumpType, Decimal64Type, PrintableElement | | | | |
|
||||
| _Decimal128 | BuiltInDumpType, Decimal128Type, PrintableElement | | | | |
|
||||
| _Float32 | BinaryFloatingPointType, BuiltInDumpType, PrintableElement, RealNumberType | | | | |
|
||||
| _Float32x | BinaryFloatingPointType, BuiltInDumpType, PrintableElement, RealNumberType | | | | |
|
||||
| _Float64 | BinaryFloatingPointType, BuiltInDumpType, PrintableElement, RealNumberType | | | | |
|
||||
| _Float64x | BinaryFloatingPointType, BuiltInDumpType, PrintableElement, RealNumberType | | | | |
|
||||
| _Float128 | BinaryFloatingPointType, BuiltInDumpType, PrintableElement, RealNumberType | | | | |
|
||||
| _Float128x | BinaryFloatingPointType, BuiltInDumpType, PrintableElement, RealNumberType | | | | |
|
||||
| _Imaginary double | BinaryFloatingPointType, BuiltInDumpType, ImaginaryNumberType, PrintableElement | | | | |
|
||||
| _Imaginary float | BinaryFloatingPointType, BuiltInDumpType, ImaginaryNumberType, PrintableElement | | | | |
|
||||
| _Imaginary long double | BinaryFloatingPointType, BuiltInDumpType, ImaginaryNumberType, PrintableElement | | | | |
|
||||
| __float128 | BuiltInDumpType, Float128Type, PrintableElement | | | | |
|
||||
| __int128 | Int128Type, IntegralDumpType, PrintableElement | | | | |
|
||||
| __va_list_tag | DirectAccessHolder, MetricClass, PrintableElement, Struct, StructLikeClass, UserDumpType | | | | |
|
||||
| __va_list_tag & | LValueReferenceDumpType, PrintableElement | | __va_list_tag | | |
|
||||
| __va_list_tag && | PrintableElement, RValueReferenceDumpType | | __va_list_tag | | |
|
||||
| address | DirectAccessHolder, MetricClass, PrintableElement, Struct, StructLikeClass, UserDumpType | | | | |
|
||||
| address & | LValueReferenceDumpType, PrintableElement | | address | | |
|
||||
| address && | PrintableElement, RValueReferenceDumpType | | address | | |
|
||||
| auto | AutoType, PrintableElement, UserDumpType | | | | |
|
||||
| bool | BoolType, IntegralDumpType, PrintableElement | | | | |
|
||||
| char | IntegralDumpType, MicrosoftInt8Type, PlainCharType, PrintableElement | | | | |
|
||||
| char8_t | Char8Type, IntegralDumpType, PrintableElement | | | | |
|
||||
| char16_t | Char16Type, IntegralDumpType, PrintableElement | | | | |
|
||||
| char32_t | Char32Type, IntegralDumpType, PrintableElement | | | | |
|
||||
| char * | CharPointerType, PointerDumpType, PrintableElement | | char | | |
|
||||
| char *[3] | ArrayDumpType, PrintableElement | char * | char * | | |
|
||||
| char *[32] | ArrayDumpType, PrintableElement | char * | char * | | |
|
||||
| char *[] | ArrayDumpType, PrintableElement | char * | char * | | |
|
||||
| char[2] | ArrayDumpType, PrintableElement | char | char | | |
|
||||
| char[3] | ArrayDumpType, PrintableElement | char | char | | |
|
||||
| char[5] | ArrayDumpType, PrintableElement | char | char | | |
|
||||
| char[6] | ArrayDumpType, PrintableElement | char | char | | |
|
||||
| char[8] | ArrayDumpType, PrintableElement | char | char | | |
|
||||
| char[9] | ArrayDumpType, PrintableElement | char | char | | |
|
||||
| char[10] | ArrayDumpType, PrintableElement | char | char | | |
|
||||
| char[53] | ArrayDumpType, PrintableElement | char | char | | |
|
||||
| char[] | ArrayDumpType, PrintableElement | char | char | | |
|
||||
| const __va_list_tag | PrintableElement, SpecifiedDumpType | | __va_list_tag | | |
|
||||
| const __va_list_tag & | LValueReferenceDumpType, PrintableElement | | const __va_list_tag | | |
|
||||
| const address | PrintableElement, SpecifiedDumpType | | address | | |
|
||||
| const address & | LValueReferenceDumpType, PrintableElement | | const address | | |
|
||||
| const char | PrintableElement, SpecifiedDumpType | | char | | |
|
||||
| const char * | PointerDumpType, PrintableElement | | const char | | |
|
||||
| const char *[3] | ArrayDumpType, PrintableElement | const char * | const char * | | |
|
||||
| const char *[] | ArrayDumpType, PrintableElement | const char * | const char * | | |
|
||||
| const char[5] | ArrayDumpType, PrintableElement | const char | const char | | |
|
||||
| const char[6] | ArrayDumpType, PrintableElement | const char | const char | | |
|
||||
| const char[8] | ArrayDumpType, PrintableElement | const char | const char | | |
|
||||
| const char[9] | ArrayDumpType, PrintableElement | const char | const char | | |
|
||||
| const char[10] | ArrayDumpType, PrintableElement | const char | const char | | |
|
||||
| const char[53] | ArrayDumpType, PrintableElement | const char | const char | | |
|
||||
| const double | PrintableElement, SpecifiedDumpType | | double | | |
|
||||
| const int | PrintableElement, SpecifiedDumpType | | int | | |
|
||||
| decltype(nullptr) | BuiltInDumpType, NullPointerType, PrintableElement | | | | |
|
||||
| double | BuiltInDumpType, DoubleType, PrintableElement | | | | |
|
||||
| error | BuiltInDumpType, ErroneousType, PrintableElement | | | | |
|
||||
| float | BuiltInDumpType, FloatType, PrintableElement | | | | |
|
||||
| float[3] | ArrayDumpType, PrintableElement | float | float | | |
|
||||
| int | IntType, IntegralDumpType, MicrosoftInt32Type, PrintableElement | | | | |
|
||||
| int * | IntPointerType, PointerDumpType, PrintableElement | | int | | |
|
||||
| int[4] | ArrayDumpType, PrintableElement | int | int | | |
|
||||
| int[8] | ArrayDumpType, PrintableElement | int | int | | |
|
||||
| int[10] | ArrayDumpType, PrintableElement | int | int | | |
|
||||
| int[10][20] | ArrayDumpType, PrintableElement | int[20] | int[20] | | |
|
||||
| int[20] | ArrayDumpType, PrintableElement | int | int | | |
|
||||
| int[] | ArrayDumpType, PrintableElement | int | int | | |
|
||||
| long | IntegralDumpType, LongType, PrintableElement | | | | |
|
||||
| long double | BuiltInDumpType, LongDoubleType, PrintableElement | | | | |
|
||||
| long long | IntegralDumpType, LongLongType, MicrosoftInt64Type, PrintableElement | | | | |
|
||||
| short | IntegralDumpType, MicrosoftInt16Type, PrintableElement, ShortType | | | | |
|
||||
| signed __int128 | Int128Type, IntegralDumpType, PrintableElement | | | | |
|
||||
| signed char | IntegralDumpType, PrintableElement, SignedCharType | | | | |
|
||||
| signed int | IntType, IntegralDumpType, PrintableElement | | | | |
|
||||
| signed long | IntegralDumpType, LongType, PrintableElement | | | | |
|
||||
| signed long long | IntegralDumpType, LongLongType, PrintableElement | | | | |
|
||||
| signed short | IntegralDumpType, PrintableElement, ShortType | | | | |
|
||||
| unknown | BuiltInDumpType, PrintableElement, UnknownType | | | | |
|
||||
| unsigned __int128 | Int128Type, IntegralDumpType, PrintableElement | | | | unsigned integral |
|
||||
| unsigned char | IntegralDumpType, PrintableElement, UnsignedCharType | | | | unsigned integral |
|
||||
| unsigned int | IntType, IntegralDumpType, PrintableElement | | | unsigned int | unsigned integral |
|
||||
| unsigned long | IntegralDumpType, LongType, PrintableElement | | | | unsigned integral |
|
||||
| unsigned long long | IntegralDumpType, LongLongType, PrintableElement | | | | unsigned integral |
|
||||
| unsigned short | IntegralDumpType, PrintableElement, ShortType | | | | unsigned integral |
|
||||
| void | BuiltInDumpType, PrintableElement, VoidType | | | | |
|
||||
| void * | PointerDumpType, PrintableElement, VoidPointerType | | void | | |
|
||||
| wchar_t | IntegralDumpType, PrintableElement, Wchar_t, WideCharType | | | | |
|
||||
| ..()(..) | RoutineType | | | | |
|
||||
| ..(*)(..) | FunctionPointerType | | ..()(..) | | |
|
||||
| _Complex __float128 | BinaryFloatingPointType, ComplexNumberType | | | | |
|
||||
| _Complex double | BinaryFloatingPointType, ComplexNumberType | | | | |
|
||||
| _Complex float | BinaryFloatingPointType, ComplexNumberType | | | | |
|
||||
| _Complex long double | BinaryFloatingPointType, ComplexNumberType | | | | |
|
||||
| _Decimal32 | Decimal32Type | | | | |
|
||||
| _Decimal64 | Decimal64Type | | | | |
|
||||
| _Decimal128 | Decimal128Type | | | | |
|
||||
| _Float32 | BinaryFloatingPointType, RealNumberType | | | | |
|
||||
| _Float32x | BinaryFloatingPointType, RealNumberType | | | | |
|
||||
| _Float64 | BinaryFloatingPointType, RealNumberType | | | | |
|
||||
| _Float64x | BinaryFloatingPointType, RealNumberType | | | | |
|
||||
| _Float128 | BinaryFloatingPointType, RealNumberType | | | | |
|
||||
| _Float128x | BinaryFloatingPointType, RealNumberType | | | | |
|
||||
| _Imaginary double | BinaryFloatingPointType, ImaginaryNumberType | | | | |
|
||||
| _Imaginary float | BinaryFloatingPointType, ImaginaryNumberType | | | | |
|
||||
| _Imaginary long double | BinaryFloatingPointType, ImaginaryNumberType | | | | |
|
||||
| __float128 | Float128Type | | | | |
|
||||
| __int128 | Int128Type | | | | |
|
||||
| __va_list_tag | DirectAccessHolder, MetricClass, Struct, StructLikeClass | | | | |
|
||||
| __va_list_tag & | LValueReferenceType | | __va_list_tag | | |
|
||||
| __va_list_tag && | RValueReferenceType | | __va_list_tag | | |
|
||||
| address | DirectAccessHolder, MetricClass, Struct, StructLikeClass | | | | |
|
||||
| address & | LValueReferenceType | | address | | |
|
||||
| address && | RValueReferenceType | | address | | |
|
||||
| auto | AutoType | | | | |
|
||||
| bool | BoolType | | | | |
|
||||
| char | MicrosoftInt8Type, PlainCharType | | | | |
|
||||
| char8_t | Char8Type | | | | |
|
||||
| char16_t | Char16Type | | | | |
|
||||
| char32_t | Char32Type | | | | |
|
||||
| char * | CharPointerType | | char | | |
|
||||
| char *[3] | ArrayType | char * | char * | | |
|
||||
| char *[32] | ArrayType | char * | char * | | |
|
||||
| char *[] | ArrayType | char * | char * | | |
|
||||
| char[2] | ArrayType | char | char | | |
|
||||
| char[3] | ArrayType | char | char | | |
|
||||
| char[5] | ArrayType | char | char | | |
|
||||
| char[6] | ArrayType | char | char | | |
|
||||
| char[8] | ArrayType | char | char | | |
|
||||
| char[9] | ArrayType | char | char | | |
|
||||
| char[10] | ArrayType | char | char | | |
|
||||
| char[53] | ArrayType | char | char | | |
|
||||
| char[] | ArrayType | char | char | | |
|
||||
| const __va_list_tag | SpecifiedType | | __va_list_tag | | |
|
||||
| const __va_list_tag & | LValueReferenceType | | const __va_list_tag | | |
|
||||
| const address | SpecifiedType | | address | | |
|
||||
| const address & | LValueReferenceType | | const address | | |
|
||||
| const char | SpecifiedType | | char | | |
|
||||
| const char * | PointerType | | const char | | |
|
||||
| const char *[3] | ArrayType | const char * | const char * | | |
|
||||
| const char *[] | ArrayType | const char * | const char * | | |
|
||||
| const char[5] | ArrayType | const char | const char | | |
|
||||
| const char[6] | ArrayType | const char | const char | | |
|
||||
| const char[8] | ArrayType | const char | const char | | |
|
||||
| const char[9] | ArrayType | const char | const char | | |
|
||||
| const char[10] | ArrayType | const char | const char | | |
|
||||
| const char[53] | ArrayType | const char | const char | | |
|
||||
| const double | SpecifiedType | | double | | |
|
||||
| const int | SpecifiedType | | int | | |
|
||||
| decltype(nullptr) | NullPointerType | | | | |
|
||||
| double | DoubleType | | | | |
|
||||
| error | ErroneousType | | | | |
|
||||
| float | FloatType | | | | |
|
||||
| float[3] | ArrayType | float | float | | |
|
||||
| int | IntType, MicrosoftInt32Type | | | | |
|
||||
| int * | IntPointerType | | int | | |
|
||||
| int[4] | ArrayType | int | int | | |
|
||||
| int[8] | ArrayType | int | int | | |
|
||||
| int[10] | ArrayType | int | int | | |
|
||||
| int[10][20] | ArrayType | int[20] | int[20] | | |
|
||||
| int[20] | ArrayType | int | int | | |
|
||||
| int[] | ArrayType | int | int | | |
|
||||
| long | LongType | | | | |
|
||||
| long double | LongDoubleType | | | | |
|
||||
| long long | LongLongType, MicrosoftInt64Type | | | | |
|
||||
| short | MicrosoftInt16Type, ShortType | | | | |
|
||||
| signed __int128 | Int128Type | | | | |
|
||||
| signed char | SignedCharType | | | | |
|
||||
| signed int | IntType | | | | |
|
||||
| signed long | LongType | | | | |
|
||||
| signed long long | LongLongType | | | | |
|
||||
| signed short | ShortType | | | | |
|
||||
| unknown | UnknownType | | | | |
|
||||
| unsigned __int128 | Int128Type | | | | unsigned integral |
|
||||
| unsigned char | UnsignedCharType | | | | unsigned integral |
|
||||
| unsigned int | IntType | | | unsigned int | unsigned integral |
|
||||
| unsigned long | LongType | | | | unsigned integral |
|
||||
| unsigned long long | LongLongType | | | | unsigned integral |
|
||||
| unsigned short | ShortType | | | | unsigned integral |
|
||||
| void | VoidType | | | | |
|
||||
| void * | VoidPointerType | | void | | |
|
||||
| wchar_t | Wchar_t, WideCharType | | | | |
|
||||
|
||||
@@ -1,111 +1,70 @@
|
||||
| file://:0:0:0:0 | (unnamed parameter 0) | file://:0:0:0:0 | __va_list_tag && | DumpVariable | | |
|
||||
| file://:0:0:0:0 | (unnamed parameter 0) | file://:0:0:0:0 | __va_list_tag && | SemanticStackVariable | | |
|
||||
| file://:0:0:0:0 | (unnamed parameter 0) | file://:0:0:0:0 | address && | DumpVariable | | |
|
||||
| file://:0:0:0:0 | (unnamed parameter 0) | file://:0:0:0:0 | address && | SemanticStackVariable | | |
|
||||
| file://:0:0:0:0 | (unnamed parameter 0) | file://:0:0:0:0 | const __va_list_tag & | DumpVariable | | |
|
||||
| file://:0:0:0:0 | (unnamed parameter 0) | file://:0:0:0:0 | const __va_list_tag & | SemanticStackVariable | | |
|
||||
| file://:0:0:0:0 | (unnamed parameter 0) | file://:0:0:0:0 | const address & | DumpVariable | | |
|
||||
| file://:0:0:0:0 | (unnamed parameter 0) | file://:0:0:0:0 | const address & | SemanticStackVariable | | |
|
||||
| file://:0:0:0:0 | fp_offset | file://:0:0:0:0 | unsigned int | DumpVariable | | |
|
||||
| file://:0:0:0:0 | fp_offset | file://:0:0:0:0 | unsigned int | Field | | |
|
||||
| file://:0:0:0:0 | gp_offset | file://:0:0:0:0 | unsigned int | DumpVariable | | |
|
||||
| file://:0:0:0:0 | gp_offset | file://:0:0:0:0 | unsigned int | Field | | |
|
||||
| file://:0:0:0:0 | overflow_arg_area | file://:0:0:0:0 | void * | DumpVariable | | |
|
||||
| file://:0:0:0:0 | overflow_arg_area | file://:0:0:0:0 | void * | Field | | |
|
||||
| file://:0:0:0:0 | reg_save_area | file://:0:0:0:0 | void * | DumpVariable | | |
|
||||
| file://:0:0:0:0 | reg_save_area | file://:0:0:0:0 | void * | Field | | |
|
||||
| variables.cpp:1:12:1:12 | i | file://:0:0:0:0 | int | DumpVariable | | |
|
||||
| variables.cpp:1:12:1:12 | i | file://:0:0:0:0 | int | GlobalVariable | | |
|
||||
| variables.cpp:1:12:1:12 | i | file://:0:0:0:0 | int | StaticStorageDurationVariable | | |
|
||||
| variables.cpp:2:12:2:12 | i | file://:0:0:0:0 | int | DumpVariable | | |
|
||||
| variables.cpp:2:12:2:12 | i | file://:0:0:0:0 | int | GlobalVariable | | |
|
||||
| variables.cpp:2:12:2:12 | i | file://:0:0:0:0 | int | StaticStorageDurationVariable | | |
|
||||
| variables.cpp:3:12:3:12 | i | file://:0:0:0:0 | int | DumpVariable | | |
|
||||
| variables.cpp:3:12:3:12 | i | file://:0:0:0:0 | int | GlobalVariable | | |
|
||||
| variables.cpp:3:12:3:12 | i | file://:0:0:0:0 | int | StaticStorageDurationVariable | | |
|
||||
| variables.cpp:5:11:5:11 | c | file://:0:0:0:0 | const int | DumpVariable | const | static |
|
||||
| variables.cpp:5:11:5:11 | c | file://:0:0:0:0 | const int | GlobalVariable | const | static |
|
||||
| variables.cpp:5:11:5:11 | c | file://:0:0:0:0 | const int | StaticStorageDurationVariable | const | static |
|
||||
| variables.cpp:6:14:6:15 | pi | file://:0:0:0:0 | const double | DumpVariable | const | static |
|
||||
| variables.cpp:6:14:6:15 | pi | file://:0:0:0:0 | const double | GlobalVariable | const | static |
|
||||
| variables.cpp:6:14:6:15 | pi | file://:0:0:0:0 | const double | StaticStorageDurationVariable | const | static |
|
||||
| variables.cpp:8:10:8:10 | a | file://:0:0:0:0 | unsigned int | DumpVariable | | |
|
||||
| variables.cpp:8:10:8:10 | a | file://:0:0:0:0 | unsigned int | GlobalVariable | | |
|
||||
| variables.cpp:8:10:8:10 | a | file://:0:0:0:0 | unsigned int | StaticStorageDurationVariable | | |
|
||||
| variables.cpp:10:14:10:14 | b | file://:0:0:0:0 | unsigned int | DumpVariable | | |
|
||||
| variables.cpp:10:14:10:14 | b | file://:0:0:0:0 | unsigned int | GlobalVariable | | |
|
||||
| variables.cpp:10:14:10:14 | b | file://:0:0:0:0 | unsigned int | StaticStorageDurationVariable | | |
|
||||
| variables.cpp:12:13:12:17 | kings | file://:0:0:0:0 | const char *[] | DumpVariable | | |
|
||||
| variables.cpp:12:13:12:17 | kings | file://:0:0:0:0 | const char *[] | GlobalVariable | | |
|
||||
| variables.cpp:12:13:12:17 | kings | file://:0:0:0:0 | const char *[] | StaticStorageDurationVariable | | |
|
||||
| variables.cpp:14:6:14:6 | p | file://:0:0:0:0 | int * | DumpVariable | | |
|
||||
| variables.cpp:14:6:14:6 | p | file://:0:0:0:0 | int * | GlobalVariable | | |
|
||||
| variables.cpp:14:6:14:6 | p | file://:0:0:0:0 | int * | StaticStorageDurationVariable | | |
|
||||
| variables.cpp:14:9:14:9 | q | file://:0:0:0:0 | int | DumpVariable | | |
|
||||
| variables.cpp:14:9:14:9 | q | file://:0:0:0:0 | int | GlobalVariable | | |
|
||||
| variables.cpp:14:9:14:9 | q | file://:0:0:0:0 | int | StaticStorageDurationVariable | | |
|
||||
| variables.cpp:15:12:15:13 | v1 | file://:0:0:0:0 | int[10] | DumpVariable | | static |
|
||||
| variables.cpp:15:12:15:13 | v1 | file://:0:0:0:0 | int[10] | GlobalVariable | | static |
|
||||
| variables.cpp:15:12:15:13 | v1 | file://:0:0:0:0 | int[10] | StaticStorageDurationVariable | | static |
|
||||
| variables.cpp:15:21:15:22 | pv | file://:0:0:0:0 | int * | DumpVariable | | static |
|
||||
| variables.cpp:15:21:15:22 | pv | file://:0:0:0:0 | int * | GlobalVariable | | static |
|
||||
| variables.cpp:15:21:15:22 | pv | file://:0:0:0:0 | int * | StaticStorageDurationVariable | | static |
|
||||
| variables.cpp:17:7:17:8 | fp | file://:0:0:0:0 | ..(*)(..) | DumpVariable | | |
|
||||
| variables.cpp:17:7:17:8 | fp | file://:0:0:0:0 | ..(*)(..) | FunctionPointerVariable | | |
|
||||
| variables.cpp:17:7:17:8 | fp | file://:0:0:0:0 | ..(*)(..) | GlobalVariable | | |
|
||||
| variables.cpp:17:7:17:8 | fp | file://:0:0:0:0 | ..(*)(..) | StaticStorageDurationVariable | | |
|
||||
| variables.cpp:19:7:19:8 | v2 | file://:0:0:0:0 | float[3] | DumpVariable | | |
|
||||
| variables.cpp:19:7:19:8 | v2 | file://:0:0:0:0 | float[3] | GlobalVariable | | |
|
||||
| variables.cpp:19:7:19:8 | v2 | file://:0:0:0:0 | float[3] | StaticStorageDurationVariable | | |
|
||||
| variables.cpp:20:7:20:8 | v3 | file://:0:0:0:0 | char *[32] | DumpVariable | | |
|
||||
| variables.cpp:20:7:20:8 | v3 | file://:0:0:0:0 | char *[32] | GlobalVariable | | |
|
||||
| variables.cpp:20:7:20:8 | v3 | file://:0:0:0:0 | char *[32] | StaticStorageDurationVariable | | |
|
||||
| variables.cpp:22:5:22:6 | d2 | file://:0:0:0:0 | int[10][20] | DumpVariable | | |
|
||||
| variables.cpp:22:5:22:6 | d2 | file://:0:0:0:0 | int[10][20] | GlobalVariable | | |
|
||||
| variables.cpp:22:5:22:6 | d2 | file://:0:0:0:0 | int[10][20] | StaticStorageDurationVariable | | |
|
||||
| variables.cpp:24:6:24:7 | v4 | file://:0:0:0:0 | char[3] | DumpVariable | | |
|
||||
| variables.cpp:24:6:24:7 | v4 | file://:0:0:0:0 | char[3] | GlobalVariable | | |
|
||||
| variables.cpp:24:6:24:7 | v4 | file://:0:0:0:0 | char[3] | StaticStorageDurationVariable | | |
|
||||
| variables.cpp:26:5:26:6 | v5 | file://:0:0:0:0 | int[8] | DumpVariable | | |
|
||||
| variables.cpp:26:5:26:6 | v5 | file://:0:0:0:0 | int[8] | GlobalVariable | | |
|
||||
| variables.cpp:26:5:26:6 | v5 | file://:0:0:0:0 | int[8] | StaticStorageDurationVariable | | |
|
||||
| variables.cpp:28:7:28:8 | p2 | file://:0:0:0:0 | char * | DumpVariable | | |
|
||||
| variables.cpp:28:7:28:8 | p2 | file://:0:0:0:0 | char * | GlobalVariable | | |
|
||||
| variables.cpp:28:7:28:8 | p2 | file://:0:0:0:0 | char * | StaticStorageDurationVariable | | |
|
||||
| variables.cpp:29:6:29:7 | p3 | file://:0:0:0:0 | char[] | DumpVariable | | |
|
||||
| variables.cpp:29:6:29:7 | p3 | file://:0:0:0:0 | char[] | GlobalVariable | | |
|
||||
| variables.cpp:29:6:29:7 | p3 | file://:0:0:0:0 | char[] | StaticStorageDurationVariable | | |
|
||||
| variables.cpp:31:6:31:10 | alpha | file://:0:0:0:0 | char[] | DumpVariable | | |
|
||||
| variables.cpp:31:6:31:10 | alpha | file://:0:0:0:0 | char[] | GlobalVariable | | |
|
||||
| variables.cpp:31:6:31:10 | alpha | file://:0:0:0:0 | char[] | StaticStorageDurationVariable | | |
|
||||
| variables.cpp:34:5:34:6 | av | file://:0:0:0:0 | int[] | DumpVariable | | |
|
||||
| variables.cpp:34:5:34:6 | av | file://:0:0:0:0 | int[] | GlobalVariable | | |
|
||||
| variables.cpp:34:5:34:6 | av | file://:0:0:0:0 | int[] | StaticStorageDurationVariable | | |
|
||||
| variables.cpp:35:6:35:8 | ap1 | file://:0:0:0:0 | int * | DumpVariable | | |
|
||||
| variables.cpp:35:6:35:8 | ap1 | file://:0:0:0:0 | int * | GlobalVariable | | |
|
||||
| variables.cpp:35:6:35:8 | ap1 | file://:0:0:0:0 | int * | StaticStorageDurationVariable | | |
|
||||
| variables.cpp:36:6:36:8 | ap2 | file://:0:0:0:0 | int * | DumpVariable | | |
|
||||
| variables.cpp:36:6:36:8 | ap2 | file://:0:0:0:0 | int * | GlobalVariable | | |
|
||||
| variables.cpp:36:6:36:8 | ap2 | file://:0:0:0:0 | int * | StaticStorageDurationVariable | | |
|
||||
| variables.cpp:37:6:37:8 | ap3 | file://:0:0:0:0 | int * | DumpVariable | | |
|
||||
| variables.cpp:37:6:37:8 | ap3 | file://:0:0:0:0 | int * | GlobalVariable | | |
|
||||
| variables.cpp:37:6:37:8 | ap3 | file://:0:0:0:0 | int * | StaticStorageDurationVariable | | |
|
||||
| variables.cpp:41:7:41:11 | local | file://:0:0:0:0 | char[] | DumpVariable | | |
|
||||
| variables.cpp:41:7:41:11 | local | file://:0:0:0:0 | char[] | LocalVariable | | |
|
||||
| variables.cpp:41:7:41:11 | local | file://:0:0:0:0 | char[] | SemanticStackVariable | | |
|
||||
| variables.cpp:43:14:43:18 | local | file://:0:0:0:0 | int | DumpVariable | | static |
|
||||
| variables.cpp:43:14:43:18 | local | file://:0:0:0:0 | int | StaticLocalVariable | | static |
|
||||
| variables.cpp:48:9:48:12 | name | file://:0:0:0:0 | char * | DumpVariable | | |
|
||||
| variables.cpp:48:9:48:12 | name | file://:0:0:0:0 | char * | Field | | |
|
||||
| variables.cpp:49:12:49:17 | number | file://:0:0:0:0 | long | DumpVariable | | |
|
||||
| variables.cpp:49:12:49:17 | number | file://:0:0:0:0 | long | Field | | |
|
||||
| variables.cpp:50:9:50:14 | street | file://:0:0:0:0 | char * | DumpVariable | | |
|
||||
| variables.cpp:50:9:50:14 | street | file://:0:0:0:0 | char * | Field | | |
|
||||
| variables.cpp:51:9:51:12 | town | file://:0:0:0:0 | char * | DumpVariable | | |
|
||||
| variables.cpp:51:9:51:12 | town | file://:0:0:0:0 | char * | Field | | |
|
||||
| variables.cpp:52:16:52:22 | country | file://:0:0:0:0 | char * | DumpVariable | | static |
|
||||
| variables.cpp:52:16:52:22 | country | file://:0:0:0:0 | char * | MemberVariable | | static |
|
||||
| variables.cpp:52:16:52:22 | country | file://:0:0:0:0 | char * | StaticStorageDurationVariable | | static |
|
||||
| variables.cpp:56:14:56:29 | externInFunction | file://:0:0:0:0 | int | DumpVariable | | |
|
||||
| variables.cpp:56:14:56:29 | externInFunction | file://:0:0:0:0 | int | GlobalVariable | | |
|
||||
| variables.cpp:56:14:56:29 | externInFunction | file://:0:0:0:0 | int | StaticStorageDurationVariable | | |
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
| test2.cpp:43:2:43:8 | call to fprintf | This write into file 'log' may contain unencrypted data from $@ | test2.cpp:43:36:43:43 | password | this source. |
|
||||
| test2.cpp:44:2:44:8 | call to fprintf | This write into file 'log' may contain unencrypted data from $@ | test2.cpp:44:37:44:45 | thepasswd | this source. |
|
||||
| test2.cpp:50:2:50:8 | call to fprintf | This write into file 'log' may contain unencrypted data from $@ | test2.cpp:50:41:50:53 | passwd_config | this source. |
|
||||
| test2.cpp:54:2:54:8 | call to fprintf | This write into file 'log' may contain unencrypted data from $@ | test2.cpp:54:41:54:52 | widepassword | this source. |
|
||||
| test2.cpp:55:2:55:8 | call to fprintf | This write into file 'log' may contain unencrypted data from $@ | test2.cpp:55:40:55:51 | widepassword | this source. |
|
||||
| test2.cpp:57:2:57:8 | call to fprintf | This write into file 'log' may contain unencrypted data from $@ | test2.cpp:57:39:57:49 | call to getPassword | this source. |
|
||||
| test2.cpp:65:3:65:9 | call to fprintf | This write into file 'log' may contain unencrypted data from $@ | test2.cpp:62:18:62:25 | password | this source. |
|
||||
| test.cpp:45:3:45:7 | call to fputs | This write into file 'file' may contain unencrypted data from $@ | test.cpp:45:9:45:19 | thePassword | this source. |
|
||||
| test.cpp:70:35:70:35 | call to operator<< | This write into file 'mystream' may contain unencrypted data from $@ | test.cpp:70:38:70:48 | thePassword | this source. |
|
||||
| test.cpp:73:37:73:41 | call to write | This write into file 'mystream' may contain unencrypted data from $@ | test.cpp:73:43:73:53 | thePassword | this source. |
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
|
||||
#define FILE int
|
||||
|
||||
typedef unsigned long size_t;
|
||||
|
||||
FILE *fopen(const char *filename, const char *mode);
|
||||
int snprintf(char *s, size_t n, const char *format, ...);
|
||||
int fprintf(FILE *stream, const char *format, ...);
|
||||
char *strcpy(char *s1, const char *s2);
|
||||
|
||||
char *crypt(char *input);
|
||||
|
||||
struct myStruct
|
||||
{
|
||||
// sensitive
|
||||
char *password;
|
||||
char *thepasswd;
|
||||
char *accountkey;
|
||||
wchar_t *widepassword;
|
||||
|
||||
// encrypted
|
||||
char password_hash[64];
|
||||
char *encrypted_passwd;
|
||||
|
||||
// not sensitive
|
||||
char *password_file;
|
||||
char *password_path;
|
||||
int num_passwords;
|
||||
int *password_tries;
|
||||
bool have_passwd;
|
||||
|
||||
// dubious
|
||||
char *passwd_config;
|
||||
char *passwd_config2;
|
||||
};
|
||||
|
||||
char *getPassword();
|
||||
char *getPasswordHash();
|
||||
int getPasswordMaxChars();
|
||||
|
||||
void tests(FILE *log, myStruct &s)
|
||||
{
|
||||
fprintf(log, "password = %s\n", s.password); // BAD
|
||||
fprintf(log, "thepasswd = %s\n", s.thepasswd); // BAD
|
||||
fprintf(log, "accountkey = %s\n", s.accountkey); // DUBIOUS [NOT REPORTED]
|
||||
fprintf(log, "password_hash = %s\n", s.password_hash); // GOOD
|
||||
fprintf(log, "encrypted_passwd = %s\n", s.encrypted_passwd); // GOOD
|
||||
fprintf(log, "password_file = %s\n", s.password_file); // GOOD
|
||||
fprintf(log, "password_path = %s\n", s.password_path); // GOOD
|
||||
fprintf(log, "passwd_config = %s\n", s.passwd_config); // DUBIOUS [REPORTED]
|
||||
fprintf(log, "num_passwords = %i\n", s.num_passwords); // GOOD
|
||||
fprintf(log, "password_tries = %i\n", *(s.password_tries)); // GOOD
|
||||
fprintf(log, "have_passwd = %i\n", s.have_passwd); // GOOD
|
||||
fprintf(log, "widepassword = %ls\n", s.widepassword); // BAD
|
||||
fprintf(log, "widepassword = %S\n", s.widepassword); // BAD
|
||||
|
||||
fprintf(log, "getPassword() = %s\n", getPassword()); // BAD
|
||||
fprintf(log, "getPasswordHash() = %s\n", getPasswordHash()); // GOOD
|
||||
fprintf(log, "getPasswordMaxChars() = %i\n", getPasswordMaxChars()); // GOOD
|
||||
|
||||
{
|
||||
char *cpy1 = s.password;
|
||||
char *cpy2 = crypt(s.password);
|
||||
|
||||
fprintf(log, "cpy1 = %s\n", cpy1); // BAD
|
||||
fprintf(log, "cpy2 = %s\n", cpy2); // GOOD
|
||||
}
|
||||
|
||||
{
|
||||
char buf[1024];
|
||||
|
||||
strcpy(buf, s.password);
|
||||
fprintf(log, "buf = %s\n", buf); // BAD [NOT DETECTED]
|
||||
|
||||
strcpy(buf, s.password_hash);
|
||||
fprintf(log, "buf = %s\n", buf); // GOOD
|
||||
}
|
||||
|
||||
fprintf(log, "password = %p\n", s.password); // GOOD
|
||||
|
||||
{
|
||||
if (fopen(s.passwd_config2, "rt") == 0)
|
||||
{
|
||||
fprintf(log, "could not open file '%s'.\n", s.passwd_config2); // GOOD
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
char buffer[1024];
|
||||
|
||||
snprintf(buffer, 1024, "password = %s", s.password);
|
||||
fprintf(log, "log: %s", buffer); // BAD [NOT DETECTED]
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,13 @@
|
||||
| test.cpp:21:3:21:8 | call to remove | The $@ being operated upon was previously $@, but the underlying file may have been changed since then. | test.cpp:21:10:21:14 | file1 | filename | test.cpp:19:7:19:12 | call to rename | checked |
|
||||
| test.cpp:35:3:35:8 | call to remove | The $@ being operated upon was previously $@, but the underlying file may have been changed since then. | test.cpp:35:10:35:14 | file1 | filename | test.cpp:32:7:32:12 | call to rename | checked |
|
||||
| test.cpp:49:3:49:8 | call to remove | The $@ being operated upon was previously $@, but the underlying file may have been changed since then. | test.cpp:49:10:49:14 | file1 | filename | test.cpp:47:7:47:12 | call to rename | checked |
|
||||
| test2.cpp:69:7:69:11 | call to fopen | The $@ being operated upon was previously $@, but the underlying file may have been changed since then. | test2.cpp:69:13:69:16 | path | filename | test2.cpp:67:6:67:9 | call to stat | checked |
|
||||
| test2.cpp:83:7:83:11 | call to fopen | The $@ being operated upon was previously $@, but the underlying file may have been changed since then. | test2.cpp:83:13:83:16 | path | filename | test2.cpp:81:6:81:8 | buf | checked |
|
||||
| test2.cpp:98:7:98:11 | call to fopen | The $@ being operated upon was previously $@, but the underlying file may have been changed since then. | test2.cpp:98:13:98:16 | path | filename | test2.cpp:96:6:96:12 | buf_ptr | checked |
|
||||
| test2.cpp:115:7:115:11 | call to fopen | The $@ being operated upon was previously $@, but the underlying file may have been changed since then. | test2.cpp:115:13:115:16 | path | filename | test2.cpp:113:22:113:24 | buf | checked |
|
||||
| test2.cpp:130:7:130:11 | call to fopen | The $@ being operated upon was previously $@, but the underlying file may have been changed since then. | test2.cpp:130:13:130:16 | path | filename | test2.cpp:128:21:128:27 | buf_ptr | checked |
|
||||
| test2.cpp:157:7:157:10 | call to open | The $@ being operated upon was previously $@, but the underlying file may have been changed since then. | test2.cpp:157:12:157:15 | path | filename | test2.cpp:155:6:155:9 | call to stat | checked |
|
||||
| test2.cpp:170:7:170:10 | call to open | The $@ being operated upon was previously $@, but the underlying file may have been changed since then. | test2.cpp:170:12:170:15 | path | filename | test2.cpp:168:6:168:10 | call to lstat | checked |
|
||||
| test2.cpp:245:3:245:7 | call to chmod | The $@ being operated upon was previously $@, but the underlying file may have been changed since then. | test2.cpp:245:9:245:12 | path | filename | test2.cpp:238:6:238:10 | call to fopen | checked |
|
||||
| test2.cpp:277:7:277:11 | call to fopen | The $@ being operated upon was previously $@, but the underlying file may have been changed since then. | test2.cpp:277:13:277:16 | path | filename | test2.cpp:275:6:275:11 | call to access | checked |
|
||||
| test2.cpp:303:7:303:11 | call to fopen | The $@ being operated upon was previously $@, but the underlying file may have been changed since then. | test2.cpp:303:13:303:16 | path | filename | test2.cpp:301:7:301:12 | call to access | checked |
|
||||
| test2.cpp:317:7:317:11 | call to fopen | The $@ being operated upon was previously $@, but the underlying file may have been changed since then. | test2.cpp:317:13:317:16 | path | filename | test2.cpp:313:6:313:11 | call to access | checked |
|
||||
| test2.cpp:348:3:348:7 | call to chmod | The $@ being operated upon was previously $@, but the underlying file may have been changed since then. | test2.cpp:348:9:348:12 | path | filename | test2.cpp:341:6:341:10 | call to fopen | checked |
|
||||
| test2.cpp:356:3:356:7 | call to chmod | The $@ being operated upon was previously $@, but the underlying file may have been changed since then. | test2.cpp:356:9:356:13 | path2 | filename | test2.cpp:354:7:354:12 | call to rename | checked |
|
||||
|
||||
@@ -18,7 +18,7 @@ void test1()
|
||||
create(file1);
|
||||
if (!rename(file1, file2))
|
||||
{
|
||||
remove(file1); // BAD
|
||||
remove(file1); // DUBIOUS (bad but perhaps not exploitable)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ void test2()
|
||||
if (!rename(file1, file2))
|
||||
{
|
||||
file1.set("d.txt");
|
||||
remove(file1); // GOOD [FALSE POSITIVE]
|
||||
remove(file1); // GOOD
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,6 +46,6 @@ void test3()
|
||||
create(file1);
|
||||
if (!rename(file1, file2))
|
||||
{
|
||||
remove(file1); // BAD
|
||||
remove(file1); // DUBIOUS (bad but perhaps not exploitable)
|
||||
}
|
||||
}
|
||||
|
||||
358
cpp/ql/test/query-tests/Security/CWE/CWE-367/semmle/test2.cpp
Normal file
358
cpp/ql/test/query-tests/Security/CWE/CWE-367/semmle/test2.cpp
Normal file
@@ -0,0 +1,358 @@
|
||||
// More test cases. Some of these are inspired by real-world cases, others are synthetic or variations.
|
||||
|
||||
#define NULL 0
|
||||
|
||||
typedef struct {} FILE;
|
||||
typedef struct {
|
||||
int foo;
|
||||
} stat_data;
|
||||
|
||||
FILE *fopen(const char *filename, const char *mode);
|
||||
int fclose(FILE *stream);
|
||||
|
||||
int open(const char *filename, int arg);
|
||||
int creat(const char *filename, int arg);
|
||||
int openat(int dir, const char *filename, int arg);
|
||||
int close(int file);
|
||||
|
||||
bool stat(const char *path, stat_data *buf);
|
||||
bool fstat(int file, stat_data *buf);
|
||||
bool lstat(const char *path, stat_data *buf);
|
||||
bool fstatat(int dir, const char *path, stat_data *buf);
|
||||
void chmod(const char *path, int setting);
|
||||
int rename(const char *from, const char *to);
|
||||
bool remove(const char *path);
|
||||
|
||||
bool access(const char *path);
|
||||
|
||||
// --- open -> open ---
|
||||
|
||||
void test1_1(const char *path)
|
||||
{
|
||||
FILE *f = NULL;
|
||||
|
||||
f = fopen(path, "r");
|
||||
|
||||
if (f == NULL)
|
||||
{
|
||||
// retry
|
||||
f = fopen(path, "r"); // GOOD (this is just trying again)
|
||||
}
|
||||
|
||||
// ...
|
||||
}
|
||||
|
||||
void test1_2(const char *path)
|
||||
{
|
||||
FILE *f = NULL;
|
||||
|
||||
// try until we succeed
|
||||
while (f == NULL)
|
||||
{
|
||||
f = fopen(path, "r"); // GOOD (this is just trying again)
|
||||
|
||||
// ...
|
||||
}
|
||||
|
||||
// ...
|
||||
}
|
||||
|
||||
// --- stat -> open ---
|
||||
|
||||
void test2_1(const char *path)
|
||||
{
|
||||
FILE *f = NULL;
|
||||
stat_data buf;
|
||||
|
||||
if (stat(path, &buf))
|
||||
{
|
||||
f = fopen(path, "r"); // BAD
|
||||
}
|
||||
|
||||
// ...
|
||||
}
|
||||
|
||||
void test2_2(const char *path)
|
||||
{
|
||||
FILE *f = NULL;
|
||||
stat_data buf;
|
||||
|
||||
stat(path, &buf);
|
||||
if (buf.foo > 0)
|
||||
{
|
||||
f = fopen(path, "r"); // BAD
|
||||
}
|
||||
|
||||
// ...
|
||||
}
|
||||
|
||||
void test2_3(const char *path)
|
||||
{
|
||||
FILE *f = NULL;
|
||||
stat_data buf;
|
||||
stat_data *buf_ptr = &buf;
|
||||
|
||||
stat(path, buf_ptr);
|
||||
if (buf_ptr->foo > 0)
|
||||
{
|
||||
f = fopen(path, "r"); // BAD
|
||||
}
|
||||
|
||||
// ...
|
||||
}
|
||||
|
||||
bool stat_condition(const stat_data *buf);
|
||||
bool other_condition();
|
||||
|
||||
void test2_4(const char *path)
|
||||
{
|
||||
FILE *f = NULL;
|
||||
stat_data buf;
|
||||
|
||||
stat(path, &buf);
|
||||
if (stat_condition(&buf))
|
||||
{
|
||||
f = fopen(path, "r"); // BAD
|
||||
}
|
||||
|
||||
// ...
|
||||
}
|
||||
|
||||
void test2_5(const char *path)
|
||||
{
|
||||
FILE *f = NULL;
|
||||
stat_data buf;
|
||||
stat_data *buf_ptr = &buf;
|
||||
|
||||
stat(path, buf_ptr);
|
||||
if (stat_condition(buf_ptr))
|
||||
{
|
||||
f = fopen(path, "r"); // BAD
|
||||
}
|
||||
|
||||
// ...
|
||||
}
|
||||
|
||||
void test2_6(const char *path)
|
||||
{
|
||||
FILE *f = NULL;
|
||||
stat_data buf;
|
||||
|
||||
stat(path, &buf);
|
||||
if (other_condition())
|
||||
{
|
||||
f = fopen(path, "r"); // GOOD (does not depend on the result of stat)
|
||||
}
|
||||
|
||||
// ...
|
||||
}
|
||||
|
||||
void test2_7(const char *path, int arg)
|
||||
{
|
||||
stat_data buf;
|
||||
int f;
|
||||
|
||||
if (stat(path, &buf))
|
||||
{
|
||||
f = open(path, arg); // BAD
|
||||
}
|
||||
|
||||
// ...
|
||||
}
|
||||
|
||||
void test2_8(const char *path, int arg)
|
||||
{
|
||||
stat_data buf;
|
||||
int f;
|
||||
|
||||
if (lstat(path, &buf))
|
||||
{
|
||||
f = open(path, arg); // BAD
|
||||
}
|
||||
|
||||
// ...
|
||||
}
|
||||
|
||||
void test2_9(const char *path, int arg)
|
||||
{
|
||||
stat_data buf;
|
||||
int f;
|
||||
|
||||
if (stat(path, &buf))
|
||||
{
|
||||
f = creat(path, arg); // BAD [NOT DETECTED]
|
||||
}
|
||||
|
||||
// ...
|
||||
}
|
||||
|
||||
void test2_10(int dir, const char *path, int arg)
|
||||
{
|
||||
stat_data buf;
|
||||
int f;
|
||||
|
||||
if (fstatat(dir, path, &buf))
|
||||
{
|
||||
f = openat(dir, path, arg); // BAD [NOT DETECTED]
|
||||
}
|
||||
|
||||
// ...
|
||||
}
|
||||
|
||||
// --- open -> stat ---
|
||||
|
||||
void test3_1(const char *path, int arg)
|
||||
{
|
||||
stat_data buf;
|
||||
int f;
|
||||
|
||||
f = open(path, arg);
|
||||
if (stat(path, &buf)) // BAD [NOT DETECTED]
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
// ...
|
||||
}
|
||||
|
||||
void test3_2(const char *path, int arg)
|
||||
{
|
||||
stat_data buf;
|
||||
int f;
|
||||
|
||||
f = open(path, arg);
|
||||
if (fstat(f, &buf)) // GOOD (uses file descriptor, not path)
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
// ...
|
||||
}
|
||||
|
||||
// --- open -> chmod ---
|
||||
|
||||
void test4_1(const char *path)
|
||||
{
|
||||
FILE *f = NULL;
|
||||
|
||||
f = fopen(path, "w");
|
||||
if (f)
|
||||
{
|
||||
// ...
|
||||
|
||||
fclose(f);
|
||||
|
||||
chmod(path, 0); // BAD
|
||||
}
|
||||
}
|
||||
|
||||
// --- rename -> remove / open ---
|
||||
|
||||
void test5_1(const char *path1, const char *path2)
|
||||
{
|
||||
if (rename(path1, path2))
|
||||
{
|
||||
remove(path1); // DUBIOUS (bad but perhaps not exploitable)
|
||||
}
|
||||
}
|
||||
|
||||
void test5_2(const char *path1, const char *path2)
|
||||
{
|
||||
FILE *f = NULL;
|
||||
|
||||
if (!rename(path1, path2))
|
||||
{
|
||||
f = fopen(path2, "r"); // BAD [NOT DETECTED]
|
||||
}
|
||||
}
|
||||
|
||||
// --- access -> open ---
|
||||
|
||||
void test6_1(const char *path)
|
||||
{
|
||||
FILE *f = NULL;
|
||||
|
||||
if (access(path))
|
||||
{
|
||||
f = fopen(path, "r"); // BAD
|
||||
|
||||
// ...
|
||||
}
|
||||
}
|
||||
|
||||
void test6_2(const char *path)
|
||||
{
|
||||
FILE *f = NULL;
|
||||
|
||||
if (access(path))
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
f = fopen(path, "r"); // GOOD (appears not to be intended to depend on the access check)
|
||||
|
||||
// ...
|
||||
}
|
||||
|
||||
void test6_3(const char *path)
|
||||
{
|
||||
FILE *f = NULL;
|
||||
|
||||
if (!access(path))
|
||||
{
|
||||
f = fopen(path, "r"); // BAD
|
||||
|
||||
// ...
|
||||
}
|
||||
}
|
||||
|
||||
void test6_4(const char *path)
|
||||
{
|
||||
FILE *f = NULL;
|
||||
|
||||
if (access(path))
|
||||
{
|
||||
// ...
|
||||
} else {
|
||||
f = fopen(path, "r"); // BAD
|
||||
|
||||
// ...
|
||||
}
|
||||
}
|
||||
|
||||
void test6_5(const char *path1, const char *path2)
|
||||
{
|
||||
FILE *f = NULL;
|
||||
|
||||
if (access(path1))
|
||||
{
|
||||
f = fopen(path2, "r"); // GOOD (different file)
|
||||
|
||||
// ...
|
||||
}
|
||||
}
|
||||
|
||||
// --- open / rename -> chmod ---
|
||||
|
||||
void test7_1(const char *path)
|
||||
{
|
||||
FILE *f;
|
||||
|
||||
f = fopen(path, "wt");
|
||||
if (f != 0)
|
||||
{
|
||||
// ...
|
||||
|
||||
fclose(f);
|
||||
|
||||
chmod(path, 1234); // BAD
|
||||
}
|
||||
}
|
||||
|
||||
void test7_1(const char *path1, const char *path2)
|
||||
{
|
||||
if (!rename(path1, path2))
|
||||
{
|
||||
chmod(path2, 1234); // BAD
|
||||
}
|
||||
}
|
||||
@@ -7,4 +7,3 @@
|
||||
| test.cpp:170:6:170:9 | data | Memory pointed to by 'data' may have been previously freed $@ | test.cpp:165:2:165:5 | call to free | here |
|
||||
| test.cpp:193:6:193:9 | data | Memory pointed to by 'data' may have been previously freed $@ | test.cpp:191:3:191:6 | call to free | here |
|
||||
| test.cpp:201:6:201:6 | x | Memory pointed to by 'x' may have been previously freed $@ | test.cpp:200:2:200:9 | delete | here |
|
||||
| test.cpp:242:14:242:17 | data | Memory pointed to by 'data' may have been previously freed $@ | test.cpp:243:11:243:14 | call to free | here |
|
||||
|
||||
@@ -213,42 +213,3 @@ void regression_test_for_static_var_handling()
|
||||
data = (char *)malloc(100*sizeof(char));
|
||||
use(data); // GOOD
|
||||
}
|
||||
|
||||
void test16(int n, bool b) {
|
||||
char* data = NULL;
|
||||
for(int i = 0; i < n; ++i) {
|
||||
if(b) data = (char*)malloc(10 * sizeof(char));
|
||||
if(!b || data == NULL) return;
|
||||
use(data); // GOOD
|
||||
free(data); // GOOD
|
||||
}
|
||||
}
|
||||
|
||||
void test17(int n, bool b) {
|
||||
char* data = (char*)malloc(10);
|
||||
if(b) {
|
||||
free(data);
|
||||
}
|
||||
|
||||
if(!b) {
|
||||
use(data); // GOOD
|
||||
}
|
||||
}
|
||||
|
||||
void test18(int* array) {
|
||||
char* data = (char*)malloc(10 * sizeof(char));
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
int b = array[i];
|
||||
if(b) use(data); // BAD
|
||||
if(!b) free(data);
|
||||
}
|
||||
}
|
||||
|
||||
void test19(int* array) {
|
||||
char* data = (char*)malloc(10 * sizeof(char));
|
||||
int b = array[0];
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
if(b) use(data); // GOOD
|
||||
if(!b) free(data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
| test.cpp:132:9:132:9 | j | The variable $@ may not be initialized here. | test.cpp:126:6:126:6 | j | j |
|
||||
| test.cpp:219:3:219:3 | x | The variable $@ may not be initialized here. | test.cpp:218:7:218:7 | x | x |
|
||||
| test.cpp:243:13:243:13 | i | The variable $@ may not be initialized here. | test.cpp:241:6:241:6 | i | i |
|
||||
| test.cpp:329:9:329:11 | val | The variable $@ may not be initialized here. | test.cpp:321:6:321:8 | val | val |
|
||||
| test.cpp:336:10:336:10 | a | The variable $@ may not be initialized here. | test.cpp:333:7:333:7 | a | a |
|
||||
| test.cpp:369:10:369:10 | a | The variable $@ may not be initialized here. | test.cpp:358:7:358:7 | a | a |
|
||||
| test.cpp:378:9:378:11 | val | The variable $@ may not be initialized here. | test.cpp:359:6:359:8 | val | val |
|
||||
|
||||
@@ -326,7 +326,7 @@ int test28() {
|
||||
a = false;
|
||||
c = false;
|
||||
}
|
||||
return val; // GOOD
|
||||
return val; // GOOD [FALSE POSITIVE]
|
||||
}
|
||||
|
||||
int test29() {
|
||||
|
||||
104
docs/codeql/codeql-cli/about-codeql-packs.rst
Normal file
104
docs/codeql/codeql-cli/about-codeql-packs.rst
Normal file
@@ -0,0 +1,104 @@
|
||||
.. _about-codeql-packs:
|
||||
|
||||
About CodeQL packs
|
||||
==================
|
||||
|
||||
.. include:: ../reusables/beta-note-package-management.rst
|
||||
|
||||
CodeQL packs are used to create, share, depend on, and run CodeQL queries and libraries. You can publish your own CodeQL packs and download packs created by others. CodeQL packs contain queries, library files, query suites, and metadata.
|
||||
|
||||
There are two types of CodeQL packs: query packs and library packs.
|
||||
|
||||
* Query packs are designed to be run. When a query pack is published, the bundle includes all the transitive dependencies and a compilation cache. This ensures consistent and efficient execution of the queries in the pack.
|
||||
* Library packs are designed to be used by query packs (or other library packs) and do not contain queries themselves. The libraries are not compiled and there is no compilation cache included when the pack is published.
|
||||
|
||||
You can use the package management commands in the CodeQL CLI to create CodeQL packs, add dependencies to packs, and install or update dependencies. For more information, see ":ref:`Creating and working with CodeQL packs <creating-and-working-with-codeql-packs>`." You can also publish and download CodeQL packs using the CodeQL CLI. For more information, see ":doc:`Publishing and using CodeQL packs <publishing-and-using-codeql-packs>`."
|
||||
|
||||
CodeQL pack structure
|
||||
---------------------
|
||||
|
||||
A CodeQL pack must contain a file called ``qlpack.yml`` in its root directory. In the ``qlpack.yml`` file, the ``name:`` field must have a value that follows the format of ``<scope>/<pack>``, where ``<scope>`` is the GitHub organization or user account that the pack will be published to and ``<pack>`` is the name of the pack. The other
|
||||
files and directories within the pack should be logically organized. For example, typically:
|
||||
|
||||
- Queries are organized into directories for specific categories.
|
||||
- Queries for specific products, libraries, and frameworks are organized into
|
||||
their own top-level directories.
|
||||
|
||||
About ``qlpack.yml`` files
|
||||
--------------------------
|
||||
|
||||
When executing query-related commands, CodeQL first looks in siblings of the installation directory (and their subdirectories) for ``qlpack.yml`` files.
|
||||
Then it checks the package cache for CodeQL packs which have been downloaded. This means that when you are developing queries locally, the local packages
|
||||
in the installation directory override packages of the same name in the package cache, so that you can test your local changes.
|
||||
|
||||
The metadata in each `qlpack.yml`` file tells
|
||||
CodeQL how to compile any queries in the pack, what libraries the pack depends on, and where to
|
||||
find query suite definitions.
|
||||
|
||||
The contents of the CodeQL pack (queries or libraries used in CodeQL analysis) is
|
||||
included in the same directory as ``qlpack.yml``, or its subdirectories.
|
||||
|
||||
The location of ``qlpack.yml`` defines the library path for the content
|
||||
of the CodeQL pack. That is, for all ``.ql`` and ``.qll`` files in the pack,
|
||||
CodeQL will resolve all import statements relative to the ``qlpack.yml`` at the
|
||||
pack's root.
|
||||
|
||||
.. _codeqlpack-yml-properties:
|
||||
|
||||
``qlpack.yml`` properties
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The following properties are supported in ``qlpack.yml`` files.
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
:widths: auto
|
||||
|
||||
* - Property
|
||||
- Example
|
||||
- Required
|
||||
- Purpose
|
||||
* - ``name``
|
||||
- ``octo-org/security-queries``
|
||||
- All packs
|
||||
- The scope, where the CodeQL pack is published, and the name of the pack defined using alphanumeric characters and hyphens. It must be unique as CodeQL cannot differentiate between CodeQL packs with identical names. Name components cannot start or end with a hyphen. Additionally, a period is not allowed in pack names at all. Use the pack name to specify queries to run using ``database analyze`` and to define dependencies between QL packs (see examples below).
|
||||
* - ``version``
|
||||
- ``0.0.0``
|
||||
- All packs
|
||||
- A version range for this CodeQL pack. This must be a valid semantic version that meets the `SemVer v2.0.0 specification <https://semver.org/spec/v2.0.0.html>`__.
|
||||
* - ``dependencies``
|
||||
- ``codeql/javascript-all: ^1.2.3``
|
||||
- Optional
|
||||
- The names and version ranges of any CodeQL packs that this pack depends on, as a mapping. This gives the pack access to any libraries, database schema, and query suites defined in the dependency. For more information, see `SemVer ranges <https://docs.npmjs.com/cli/v6/using-npm/semver#ranges>`__ in the NPM documentation.
|
||||
* - ``suites``
|
||||
- ``octo-org-query-suites``
|
||||
- Optional
|
||||
- The path to a directory in the pack that contains the query suites you want to make known to the CLI, defined relative to the pack directory. QL pack users can run "well-known" suites stored in this directory by specifying the pack name, without providing their full path. This is not supported for CodeQL packs downloaded from a package registry. For more information about query suites, see ":doc:`Creating CodeQL query suites <creating-codeql-query-suites>`."
|
||||
* - ``extractor``
|
||||
- ``javascript``
|
||||
- All test packs
|
||||
- The CodeQL language extractor to use when the CLI creates a database in the pack. For more information about testing queries, see ":doc:`Testing custom queries <testing-custom-queries>`."
|
||||
* - ``tests``
|
||||
- ``.``
|
||||
- Optional for test packs
|
||||
- The path to a directory within the pack that contains tests, defined relative to the pack directory. Use ``.`` to specify the whole pack. Any queries in this directory are run as tests when ``test run`` is run with the ``--strict-test-discovery`` option. These queries are ignored by query suite definitions that use ``queries`` or ``qlpack`` instructions to ask for all queries in a particular pack.
|
||||
* - ``dbscheme``
|
||||
- ``semmlecode.python.dbscheme``
|
||||
- Core language packs only
|
||||
- The path to the :ref:`database schema <codeql-database-schema>` for all libraries and queries written for this CodeQL language (see example below).
|
||||
* - ``upgrades``
|
||||
- ``.``
|
||||
- Core language packs only
|
||||
- The path to a directory within the pack that contains upgrade scripts, defined relative to the pack directory. The ``database upgrade`` action uses these scripts to update databases that were created by an older version of an extractor so they're compatible with the current extractor (see `Upgrade scripts for a language <#upgrade-scripts-for-a-language>`__ below.)
|
||||
* - ``authors``
|
||||
- ``example@github.com``
|
||||
- All packs
|
||||
- Metadata that will be displayed on the packaging search page in the packages section of the account that the CodeQL pack is published to.
|
||||
* - ``licenses``
|
||||
- ``(LGPL-2.1 AND MIT)``
|
||||
- All packs
|
||||
- Metadata that will be displayed on the packaging search page in the packages section of the account that the CodeQL pack is published to. For a list of allowed licenses, see `SPDX License List <https://spdx.org/licenses/>`__ in the SPDX Specification.
|
||||
* - ``description``
|
||||
- ``Human-readable description of the contents of the CodeQL pack.``
|
||||
- All packs
|
||||
- Metadata that will be displayed on the packaging search page in the packages section of the account that the CodeQL pack is published to.
|
||||
@@ -87,7 +87,7 @@ The following properties are supported in ``qlpack.yml`` files.
|
||||
* - ``suites``
|
||||
- ``suites``
|
||||
- Optional
|
||||
- The path to a directory that contains the "well-known" query suites in the pack, defined relative to the pack directory. You can run "well-known" suites stored in this directory by specifying the pack name, without providing their full path. To use query suites stored in other directories in the pack, you must provide their full path. For more information about query suites, see ":doc:`Creating CodeQL query suites <creating-codeql-query-suites>`."
|
||||
- The path to a directory in the pack that contains the query suites you want to make known to the CLI, defined relative to the pack directory. QL pack users can run "well-known" suites stored in this directory by specifying the pack name, without providing their full path. For more information about query suites, see ":doc:`Creating CodeQL query suites <creating-codeql-query-suites>`."
|
||||
* - ``extractor``
|
||||
- ``javascript``
|
||||
- All test packs
|
||||
|
||||
@@ -108,22 +108,38 @@ You can also run your own custom queries with the ``database analyze`` command.
|
||||
For more information about preparing your queries to use with the CodeQL CLI,
|
||||
see ":doc:`Using custom queries with the CodeQL CLI <using-custom-queries-with-the-codeql-cli>`."
|
||||
|
||||
Running GitHub code scanning suites
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Running a CodeQL pack
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
To run the GitHub code scanning suite of queries over a CodeQL database for a C/C++ codebase,
|
||||
.. include:: ../reusables/beta-note-package-management.rst
|
||||
|
||||
To run an existing CodeQL query pack from the GitHub Container registry, you need to download it first::
|
||||
|
||||
codeql pack download microsoft/coding-standards@1.0.0
|
||||
|
||||
Afterwards, you can run the pack on a specific database::
|
||||
|
||||
codeql database analyze <database> microsoft/coding-standards@1.0.0 <scope>/<other-pack> --format=sarifv2.1.0 --output=query-results.sarif
|
||||
|
||||
The ``analyze`` command above runs the default suite from ``microsoft/coding-standards v1.0.0`` and the latest version of ``scope/other-pack`` on the specified database.
|
||||
For further information about default suites, see ":ref:`Publishing and using CodeQL packs <publishing-and-using-codeql-packs>`".
|
||||
|
||||
Running query suites
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
To run a query suite over a CodeQL database for a C/C++ codebase,
|
||||
you could use the following command from the directory containing your database::
|
||||
|
||||
codeql database analyze <cpp-database> cpp-code-scanning.qls --format=sarifv2.1.0 --output=cpp-results.sarif
|
||||
|
||||
The analysis generates a file in the v2.1.0 SARIF format that is supported by all versions of GitHub.
|
||||
This file can be uploaded to GitHub using ``github upload-results`` or the code scanning API.
|
||||
This file can be uploaded to GitHub by executing ``codeql github upload-results`` or the code scanning API.
|
||||
For more information, see `Analyzing a CodeQL database <https://docs.github.com/en/code-security/secure-coding/configuring-codeql-cli-in-your-ci-system#analyzing-a-codeql-database>`__
|
||||
or `Code scanning API <https://docs.github.com/en/rest/reference/code-scanning>`__ in the GitHub documentation.
|
||||
|
||||
CodeQL query suites are ``.qls`` files that use directives to select queries to run
|
||||
based on certain metadata properties. The standard QL packs have metadata that specify
|
||||
the location of the code scanning suites, so the CodeQL CLI knows where to find these
|
||||
the location of the query suites used by code scanning, so the CodeQL CLI knows where to find these
|
||||
suite files automatically, and you don't have to specify the full path on the command line.
|
||||
For more information, see ":ref:`About QL packs <standard-ql-packs>`."
|
||||
|
||||
@@ -137,7 +153,7 @@ and at the following path in the CodeQL for Go repository::
|
||||
ql/src/codeql-suites/go-code-scanning.qls
|
||||
|
||||
The repository also includes the query suites used by `LGTM.com <https://lgtm.com>`__.
|
||||
These are stored alongside the code scanning suites with names of the form: ``<language>-lgtm.qls``.
|
||||
These are stored alongside the query suites for code scanning with names of the form: ``<language>-lgtm.qls``.
|
||||
|
||||
For information about creating custom query suites, see ":doc:`Creating
|
||||
CodeQL query suites <creating-codeql-query-suites>`."
|
||||
@@ -149,6 +165,15 @@ When you create a CodeQL database, the extractor stores diagnostic data in the d
|
||||
|
||||
If the analysis found fewer results for standard queries than you expected, review the results of the diagnostic and summary queries to check whether the CodeQL database is likely to be a good representation of the codebase that you want to analyze.
|
||||
|
||||
Integrating a CodeQL pack into a code scanning workflow in GitHub
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. include:: ../reusables/beta-note-package-management.rst
|
||||
|
||||
You can use CodeQL query packs in your Code Scanning setup. This allows you to select query packs published by various sources and use them to analyze your code.
|
||||
For more information, see "`Using CodeQL query packs in the CodeQL action <https://docs.github.com/en/code-security/secure-coding/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-codeql-query-packs/>`_" or "`Downloading and using CodeQL query packs in your CI system <https://docs.github.com/en/code-security/secure-coding/using-codeql-code-scanning-with-your-existing-ci-system/configuring-codeql-cli-in-your-ci-system#downloading-and-using-codeql-query-packs>`_."
|
||||
|
||||
|
||||
Running all queries in a directory
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
||||
@@ -9,12 +9,14 @@ Learn more about the files you can use when running CodeQL processes and the res
|
||||
:titlesonly:
|
||||
:hidden:
|
||||
|
||||
about-codeql-packs
|
||||
about-ql-packs
|
||||
query-reference-files
|
||||
sarif-output
|
||||
exit-codes
|
||||
|
||||
|
||||
- :doc:`About CodeQL packs <about-codeql-packs>`: CodeQL packs are created with the CodeQL CLI and are used to create, depend on, publish, and run CodeQL queries and libraries.
|
||||
- :doc:`About QL packs <about-ql-packs>`: QL packs are used to organize the files used in CodeQL analysis. They
|
||||
contain queries, library files, query suites, and important metadata.
|
||||
- :doc:`Query reference files <query-reference-files>`: A query reference file is text file that defines the location of one query to test.
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
.. _creating-and-working-with-codeql-packs:
|
||||
|
||||
Creating and working with CodeQL packs
|
||||
======================================
|
||||
|
||||
You can use CodeQL packs to create, share, depend on, and run CodeQL queries and libraries.
|
||||
|
||||
.. include:: ../reusables/beta-note-package-management.rst
|
||||
|
||||
About CodeQL packs and the CodeQL CLI
|
||||
-------------------------------------
|
||||
|
||||
With CodeQL packs and the package management commands in the CodeQL CLI, you can publish your custom queries and integrate them into your codebase analysis.
|
||||
|
||||
There are two types of CodeQL packs: query packs and library packs.
|
||||
|
||||
* Query packs are designed to be run. When a query pack is published, the bundle includes all the transitive dependencies and a compilation cache. This ensures consistent and efficient execution of the queries in the pack.
|
||||
* Library packs are designed to be used by query packs (or other library packs) and do not contain queries themselves. The libraries are not compiled and there is no compilation cache included when the pack is published.
|
||||
|
||||
You can use the ``pack`` command in the CodeQL CLI to create CodeQL packs, add dependencies to packs, and install or update dependencies. You can also publish and download CodeQL packs using the ``pack`` command. For more information, see ":doc:`Publishing and using CodeQL packs <publishing-and-using-codeql-packs>`."
|
||||
|
||||
Creating a CodeQL pack
|
||||
----------------------
|
||||
You can create a CodeQL pack by running the following command from the checkout root of your project:
|
||||
|
||||
::
|
||||
|
||||
codeql pack init <scope>/<pack>
|
||||
|
||||
You must specify:
|
||||
|
||||
- ``<scope>``: the name of the GitHub organization or user account that you will publish to.
|
||||
- ``<pack>``: the name for the pack that you are creating.
|
||||
|
||||
The ``codeql pack init`` command creates the directory structure and configuration files for a CodeQL pack. By default, the command creates a query pack. If you want to create a library pack, you must edit the ``qlpack.yml`` file to explicitly declare the file as a library pack by including the ``library:true`` property.
|
||||
|
||||
Modifying an existing QL pack to create a CodeQL pack
|
||||
-----------------------------------------------------
|
||||
If you already have a ``qlpack.yml`` file, you can edit it manually to convert it into a CodeQL pack.
|
||||
|
||||
#. Edit the ``name`` property so that it matches the format ``<scope>/<name>``, where ``<scope>`` is the name of the GitHub organization or user account that you will publish to.
|
||||
#. In the ``qlpack.yml`` file, include a ``version`` property with a semver identifier, as well as an optional ``dependencies`` block.
|
||||
|
||||
For more information about the properties, see ":ref:`About CodeQL packs <about-codeql-packs>`."
|
||||
|
||||
Adding and installing dependencies to a CodeQL pack
|
||||
---------------------------------------------------
|
||||
You can add dependencies on CodeQL packs using the command ``codeql pack add``. You must specify the scope, name, and version range.
|
||||
|
||||
::
|
||||
|
||||
codeql pack add <scope>/<name>@x.x.x <scope>/<other-name>
|
||||
|
||||
The version range is optional. If you leave off the version range, the latest version will be added. Otherwise, the latest version that satisfies the requested range will be added.
|
||||
|
||||
This command updates the ``qlpack.yml`` file with the requested dependencies and downloads them into the package cache. Please note that this command will reformat the file and remove all comments.
|
||||
|
||||
You can also manually edit the ``qlpack.yml`` file to include dependencies and install the dependencies with the command:
|
||||
|
||||
::
|
||||
|
||||
codeql pack install
|
||||
|
||||
This command downloads all dependencies to the shared cache on the local disk.
|
||||
|
||||
.. pull-quote::
|
||||
|
||||
Note
|
||||
|
||||
Running the ``codeql pack add`` and ``codeql pack install`` commands will generate or update the ``qlpack.lock.yml`` file. This file should be checked-in to version control. The ``qlpack.lock.yml`` file contains the precise version numbers used by the pack.
|
||||
@@ -18,9 +18,9 @@ structures. To get started quickly, we recommend adopting a relatively simple
|
||||
setup, as outlined in the steps below.
|
||||
|
||||
If you use Linux, Windows, or macOS version 10.14 ("Mojave") or earlier, simply
|
||||
follow the steps below. For macOS version 10.15 ("Catalina"), steps 1 and 4 are
|
||||
slightly different---for further details, see the sections labeled **Information
|
||||
for macOS "Catalina" users**.
|
||||
follow the steps below. For macOS version 10.15 ("Catalina") or newer, steps 1
|
||||
and 4 are slightly different---for further details, see the sections labeled
|
||||
**Information for macOS "Catalina" (or newer) users**.
|
||||
|
||||
For information about installing the CodeQL CLI in a CI system to create results
|
||||
to display in GitHub as code scanning alerts, see
|
||||
@@ -66,13 +66,14 @@ Alternatively, you can download ``codeql.zip``, which contains the CLI for all s
|
||||
|
||||
.. container:: name
|
||||
|
||||
**Information for macOS "Catalina" users**
|
||||
**Information for macOS "Catalina" (or newer) users**
|
||||
|
||||
.. pull-quote:: macOS "Catalina"
|
||||
.. pull-quote:: macOS "Catalina" (or newer)
|
||||
|
||||
If you use macOS version 10.15 ("Catalina"), you need to ensure that your web
|
||||
browser does not automatically extract zip files. If you use Safari,
|
||||
complete the following steps before downloading the CodeQL CLI zip archive:
|
||||
If you use macOS version 10.15 ("Catalina"), version 11 ("Big Sur"), or the upcoming
|
||||
version 12 ("Monterey"), you need to ensure that your web browser does not automatically
|
||||
extract zip files. If you use Safari, complete the following steps before downloading
|
||||
the CodeQL CLI zip archive:
|
||||
|
||||
i. Open Safari.
|
||||
ii. From the Safari menu, select **Preferences...**.
|
||||
@@ -164,16 +165,17 @@ For example, if the path to your copy of the CodeQL repository is
|
||||
|
||||
.. container:: name
|
||||
|
||||
**Information for macOS "Catalina" users**
|
||||
**Information for macOS "Catalina" (or newer) users**
|
||||
|
||||
.. pull-quote:: macOS "Catalina"
|
||||
|
||||
macOS "Catalina" users should run the following commands in the Terminal,
|
||||
where ``${install_loc}`` is the path to the directory you created in step 2:
|
||||
macOS "Catalina", "Big Sur", or "Monterey" users should run the following
|
||||
commands in the Terminal, where ``${install_loc}`` is the path to the
|
||||
directory you created in step 2:
|
||||
|
||||
i. ``mv ~/Downloads/codeql*.zip ${install_loc}``
|
||||
ii. ``cd ${install_loc}``
|
||||
iii. ``xattr -c codeql*.zip``
|
||||
iii. ``/usr/bin/xattr -c codeql*.zip``
|
||||
iv. ``unzip codeql*.zip``
|
||||
|
||||
5. Launch ``codeql``
|
||||
|
||||
74
docs/codeql/codeql-cli/publishing-and-using-codeql-packs.rst
Normal file
74
docs/codeql/codeql-cli/publishing-and-using-codeql-packs.rst
Normal file
@@ -0,0 +1,74 @@
|
||||
.. _publishing-and-using-codeql-packs:
|
||||
|
||||
Publishing and using CodeQL packs
|
||||
=================================
|
||||
|
||||
You can publish your own CodeQL packs and use packs published by other people.
|
||||
|
||||
.. include:: ../reusables/beta-note-package-management.rst
|
||||
|
||||
Configuring the ``qlpack.yml`` file before publishing
|
||||
-----------------------------------------------------
|
||||
|
||||
You can check and modify the configuration details of your CodeQL pack prior to publishing. Open the ``qlpack.yml`` file in your preferred text editor.
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
library: # set to true if the pack is a library. Set to false or omit for a query pack
|
||||
name: <scope>/<pack>
|
||||
version: <x.x.x>
|
||||
description: <Description to publish with the package>
|
||||
default-suite: # optional, one or more queries in the pack to run by default
|
||||
- query: <relative-path>/query-file>.ql
|
||||
default-suite-file: default-queries.qls # optional, a pointer to a query-suite in this pack
|
||||
license: # optional, the license under which the pack is published
|
||||
dependencies: # map from CodeQL pack name to version range
|
||||
|
||||
- ``name:`` must follow the <scope>/<pack> format, where <scope> is the GitHub organization that you will publish to and <pack> is the name for the pack.
|
||||
- A maximum of one of ``default-suite`` or ``default-suite-file`` is allowed. These are two different ways to define a default query suite to be run, the first by specifying queries directly in the `qlpack.yml` file and the second by specifying a query suite in the pack.
|
||||
|
||||
Running ``codeql pack publish``
|
||||
-------------------------------
|
||||
|
||||
When you are ready to publish a pack to the GitHub Container registry, you can run the following command in the root of the pack directory:
|
||||
|
||||
::
|
||||
|
||||
codeql pack publish
|
||||
|
||||
The published package will be displayed in the packages section of GitHub organization specified by the scope in the ``qlpack.yml`` file.
|
||||
|
||||
Running ``codeql pack download <scope>/<pack>``
|
||||
-----------------------------------------------
|
||||
|
||||
To run a pack that someone else has created, you must first download it by running the following command:
|
||||
|
||||
::
|
||||
|
||||
codeql pack download <scope>/<pack>@x.x.x
|
||||
|
||||
- ``<scope>``: the name of the GitHub organization that you will download from.
|
||||
- ``<pack>``: the name for the pack that you want to download.
|
||||
- ``@x.x.x``: an optional version number. If omitted, the latest version will be downloaded.
|
||||
|
||||
This command accepts arguments for multiple packs.
|
||||
|
||||
Using a CodeQL pack to analyze a CodeQL database
|
||||
------------------------------------------------
|
||||
|
||||
To analyze a CodeQL database with a CodeQL pack, run the following command:
|
||||
|
||||
::
|
||||
|
||||
codeql database analyze <database> <scope>/<pack>@x.x.x
|
||||
|
||||
- ``<database>``: the CodeQL database to be analyzed.
|
||||
- ``<scope>``: the name of the GitHub organization that the pack is published to.
|
||||
- ``<pack>``: the name for the pack that you are using.
|
||||
- ``@x.x.x``: an optional version number. If omitted, the latest version will be used.
|
||||
|
||||
The ``analyze`` command will run the default suite of any specified CodeQL packs. You can specify multiple CodeQL packs to be used for analyzing a CodeQL database. For example:
|
||||
|
||||
::
|
||||
|
||||
codeql <database> analyze <scope>/<pack> <scope>/<other-pack>
|
||||
@@ -49,20 +49,28 @@ For more information about these metadata properties, see ":ref:`Metadata for Co
|
||||
<metadata-for-codeql-queries>`
|
||||
."
|
||||
|
||||
Packaging custom QL queries
|
||||
---------------------------
|
||||
|
||||
Creating a custom QL pack
|
||||
-------------------------
|
||||
.. include:: ../reusables/beta-note-package-management.rst
|
||||
|
||||
When writing your own queries, you should save them in a custom QL pack
|
||||
directory. QL packs provide a way of organizing
|
||||
the files used in CodeQL analysis. This directory must contain a file
|
||||
named ``qlpack.yml`` at the root. Your custom queries should be saved in the QL
|
||||
pack root, or its subdirectories.
|
||||
When you write your own queries, you should save them in a custom QL pack
|
||||
directory. When you are ready to share your queries with other users, you can publish the pack as a CodeQL pack to GitHub Packages - the GitHub Container registry.
|
||||
|
||||
QL packs organize the files used in CodeQL analysis and can store queries,
|
||||
library files, query suites, and important metadata. Their root directory must
|
||||
contain a file named ``qlpack.yml``. Your custom queries should be saved in the
|
||||
QL pack root, or its subdirectories.
|
||||
|
||||
For each QL pack, the ``qlpack.yml`` file includes information that tells CodeQL
|
||||
how to compile the queries, what libraries the pack depends on, and where to find
|
||||
query suite definitions. For more information about what to include in this
|
||||
file, see ":ref:`About QL packs <custom-ql-packs>`."
|
||||
how to compile the queries, which other CodeQL packs and libraries the pack
|
||||
depends on, and where to find query suite definitions. For more information
|
||||
about what to include in this file, see ":ref:`About QL packs <about-ql-packs>`."
|
||||
|
||||
CodeQL packages are used to create, share, depend on, and run CodeQL queries and
|
||||
libraries. You can publish your own CodeQL packages and download ones created by
|
||||
others via the the Container registry. For further information see
|
||||
":ref:`About CodeQL packs <about-codeql-packs>`."
|
||||
|
||||
Contributing to the CodeQL repository
|
||||
-------------------------------------
|
||||
|
||||
@@ -41,6 +41,12 @@ See the following links to learn how to get set up and run CodeQL commands:
|
||||
Test query help files by rendering them as markdown to ensure they are valid
|
||||
before adding them to the CodeQL repository or using them in code scanning.
|
||||
|
||||
- :doc:`Creating and working with CodeQL packs <creating-and-working-with-codeql-packs>`:
|
||||
Create, share, depend on, and run CodeQL queries and libraries.
|
||||
|
||||
- :doc:`Publishing and using CodeQL packs <publishing-and-using-codeql-packs>`:
|
||||
Publish your own or use others CodeQL packs for code scanning.
|
||||
|
||||
- :doc:`Specifying command options in a CodeQL configuration file <specifying-command-options-in-a-codeql-configuration-file>`:
|
||||
You can save default or frequently used options for your commands in a per-user configuration file.
|
||||
|
||||
@@ -57,4 +63,6 @@ See the following links to learn how to get set up and run CodeQL commands:
|
||||
creating-codeql-query-suites
|
||||
testing-custom-queries
|
||||
testing-query-help-files
|
||||
creating-and-working-with-codeql-packs
|
||||
publishing-and-using-codeql-packs
|
||||
Specifying command options <specifying-command-options-in-a-codeql-configuration-file>
|
||||
|
||||
@@ -497,6 +497,7 @@ The following sequences of characters are keyword tokens:
|
||||
max
|
||||
min
|
||||
module
|
||||
newtype
|
||||
none
|
||||
not
|
||||
or
|
||||
@@ -514,6 +515,7 @@ The following sequences of characters are keyword tokens:
|
||||
then
|
||||
this
|
||||
true
|
||||
unique
|
||||
where
|
||||
|
||||
Operators
|
||||
|
||||
@@ -57,7 +57,7 @@ As an aside, note that the following query leads to a compile-time error:
|
||||
select i
|
||||
|
||||
In theory, it would have infinitely many results, as the variable ``i`` is not constrained to a
|
||||
finite number of possible values. For more informaion, see ":ref:`binding`."
|
||||
finite number of possible values. For more information, see ":ref:`binding`."
|
||||
|
||||
.. index:: variable; free, variable; bound
|
||||
.. _free-variables:
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
# CodeQL CWE coverage
|
||||
CodeQL CWE coverage
|
||||
===================
|
||||
|
||||
An overview of the coverage of MITRE's Common Weakness Enumeration (CWE) for the latest release of CodeQL.
|
||||
You can view the full coverage of MITRE's Common Weakness Enumeration (CWE) or coverage by language for the latest release of CodeQL.
|
||||
|
||||
## About CWEs
|
||||
About CWEs
|
||||
##########
|
||||
|
||||
The CWE categorization contains several types of entity, collectively known as CWEs. The CWEs that we consider in this report are only those of the types:
|
||||
|
||||
@@ -11,15 +13,22 @@ The CWE categorization contains several types of entity, collectively known as C
|
||||
- Weakness Variant
|
||||
- Compound Element
|
||||
|
||||
Other types of CWE do not correspond directly to weaknesses, so are omitted.
|
||||
Other types of CWE that do not correspond directly to weaknesses are omitted.
|
||||
|
||||
The CWE categorization includes relationships between entities, in particular a parent-child relationship.
|
||||
These relationships are associated with Views (another kind of CWE entity). For the purposes of coverage claims, we use the "[Research View](https://cwe.mitre.org/data/definitions/1000.html)."
|
||||
These relationships are associated with Views (another kind of CWE entity). For the purposes of coverage claims, we use the "`Research View <https://cwe.mitre.org/data/definitions/1000.html>`_."
|
||||
|
||||
Every security query is associated with one or more CWEs, which are the most precise CWEs that are covered by that query.
|
||||
Overall coverage is claimed for the most-precise CWEs, as well as for any of their ancestors in the View.
|
||||
|
||||
## Overview
|
||||
|
||||
<!-- autogenerated CWE coverage table will be added below -->
|
||||
.. toctree::
|
||||
:hidden:
|
||||
:titlesonly:
|
||||
|
||||
full-cwe
|
||||
cpp-cwe
|
||||
csharp-cwe
|
||||
go-cwe
|
||||
java-cwe
|
||||
javascript-cwe
|
||||
python-cwe
|
||||
8
docs/codeql/query-help/cpp-cwe.md
Normal file
8
docs/codeql/query-help/cpp-cwe.md
Normal file
@@ -0,0 +1,8 @@
|
||||
# CWE coverage for C and C++
|
||||
|
||||
An overview of CWE coverage for C and C++ in the latest release of CodeQL.
|
||||
|
||||
## Overview
|
||||
|
||||
<!-- autogenerated CWE coverage table will be added below -->
|
||||
|
||||
8
docs/codeql/query-help/csharp-cwe.md
Normal file
8
docs/codeql/query-help/csharp-cwe.md
Normal file
@@ -0,0 +1,8 @@
|
||||
# CWE coverage for C#
|
||||
|
||||
An overview of CWE coverage for C# in the latest release of CodeQL.
|
||||
|
||||
## Overview
|
||||
|
||||
<!-- autogenerated CWE coverage table will be added below -->
|
||||
|
||||
8
docs/codeql/query-help/full-cwe.md
Normal file
8
docs/codeql/query-help/full-cwe.md
Normal file
@@ -0,0 +1,8 @@
|
||||
# CodeQL full CWE coverage
|
||||
|
||||
An overview of the full coverage of MITRE's Common Weakness Enumeration (CWE) for the latest release of CodeQL.
|
||||
|
||||
## Overview
|
||||
|
||||
<!-- autogenerated CWE coverage table will be added below -->
|
||||
|
||||
8
docs/codeql/query-help/go-cwe.md
Normal file
8
docs/codeql/query-help/go-cwe.md
Normal file
@@ -0,0 +1,8 @@
|
||||
# CWE coverage for Go
|
||||
|
||||
An overview of CWE coverage for Go in the latest release of CodeQL.
|
||||
|
||||
## Overview
|
||||
|
||||
<!-- autogenerated CWE coverage table will be added below -->
|
||||
|
||||
8
docs/codeql/query-help/java-cwe.md
Normal file
8
docs/codeql/query-help/java-cwe.md
Normal file
@@ -0,0 +1,8 @@
|
||||
# CWE coverage for Java
|
||||
|
||||
An overview of CWE coverage for Java in the latest release of CodeQL.
|
||||
|
||||
## Overview
|
||||
|
||||
<!-- autogenerated CWE coverage table will be added below -->
|
||||
|
||||
8
docs/codeql/query-help/javascript-cwe.md
Normal file
8
docs/codeql/query-help/javascript-cwe.md
Normal file
@@ -0,0 +1,8 @@
|
||||
# CWE coverage for JavaScript
|
||||
|
||||
An overview of CWE coverage for JavaScript in the latest release of CodeQL.
|
||||
|
||||
## Overview
|
||||
|
||||
<!-- autogenerated CWE coverage table will be added below -->
|
||||
|
||||
8
docs/codeql/query-help/python-cwe.md
Normal file
8
docs/codeql/query-help/python-cwe.md
Normal file
@@ -0,0 +1,8 @@
|
||||
# CWE coverage for Python
|
||||
|
||||
An overview of CWE coverage for Python in the latest release of CodeQL.
|
||||
|
||||
## Overview
|
||||
|
||||
<!-- autogenerated CWE coverage table will be added below -->
|
||||
|
||||
@@ -2,7 +2,9 @@ CodeQL query help Sphinx documentation
|
||||
--------------------------------------
|
||||
|
||||
This project supplies the configuration and some boiler plate
|
||||
index files for the CodeQL query help documentation.
|
||||
index files for the CodeQL query help and CWE coverage documentation.
|
||||
|
||||
The query help itself is automatically generated by the
|
||||
"Generate CodeQL query help documentation using Sphinx" workflow.
|
||||
|
||||
The CWE coverage tables are generated and appended to pages by the "Docs generate query help" workflow in the `semmle-code` repository.
|
||||
|
||||
5
docs/codeql/reusables/beta-note-package-management.rst
Normal file
5
docs/codeql/reusables/beta-note-package-management.rst
Normal file
@@ -0,0 +1,5 @@
|
||||
.. pull-quote::
|
||||
|
||||
Note
|
||||
|
||||
The CodeQL package management functionality, including CodeQL packs, is currently available as a beta release and is subject to change. During the beta release, CodeQL packs are available only using GitHub Packages - the GitHub Container registry. To use this beta functionality, install the beta release of the CodeQL CLI bundle from: https://github.com/github/codeql-action/releases/tag/codeql-bundle-v2.6.0-beta.1.
|
||||
@@ -95,6 +95,7 @@ Java built-in support
|
||||
Guava, Utility and collections library
|
||||
Hibernate, Database
|
||||
iBatis / MyBatis, Database
|
||||
Jackson, Serialization
|
||||
Java Persistence API (JPA), Database
|
||||
JDBC, Database
|
||||
Protobuf, Serialization
|
||||
|
||||
@@ -45,10 +45,12 @@ The following properties are supported by all query files:
|
||||
| | | ``high`` | |
|
||||
| | | ``very-high`` | |
|
||||
+-----------------------+---------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| ``@problem.severity`` | | ``error`` | Defines the level of severity of any alerts generated by the query. This, along with the ``@precision`` property, determines whether the results are displayed by default on LGTM. |
|
||||
| ``@problem.severity`` | | ``error`` | Defines the level of severity of any alerts generated by a non-security query. This, along with the ``@precision`` property, determines whether the results are displayed by default on LGTM. |
|
||||
| | | ``warning`` | |
|
||||
| | | ``recommendation`` | |
|
||||
+-----------------------+---------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| ``@security-severity``| ``<score>`` | Defines the level of severity, between 0.0 and 10.0, for queries with `@tags security`. For more information about calculating `@security-severity`, see the [GitHub changelog](https://github.blog/changelog/2021-07-19-codeql-code-scanning-new-severity-levels-for-security-alerts/). |
|
||||
+-----------------------+---------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
|
||||
Additional properties for filter queries
|
||||
----------------------------------------
|
||||
|
||||
@@ -110,6 +110,21 @@ For example:
|
||||
|
||||
If your query checks code for a CWE weakness, you should use the `@tags` element in the query file to reference the associated CWEs, as explained [here](query-metadata-style-guide.md). When you use these tags, a link to the appropriate entry from the [MITRE.org](https://cwe.mitre.org/scoring/index.html) site will automatically appear as a reference in the output HTML file.
|
||||
|
||||
## Validating qhelp files
|
||||
|
||||
Before making a pull request, please ensure the `.qhelp` files are well-formed and can be generated without errors. This can be done locally with the CodeQL CLI, as shown in the following example:
|
||||
|
||||
```bash
|
||||
# codeql generate query-help <path_to_your_qhelp_file> --format=<format>
|
||||
# For example:
|
||||
codeql generate query-help ./myCustomQuery.qhelp --format=markdown
|
||||
```
|
||||
|
||||
|
||||
Please include the `.qhelp` files (and any associated code snippets) in your pull request, but do not commit the generated Markdown.
|
||||
|
||||
More information on how to test your `.qhelp` files can be found [within the documentation](https://codeql.github.com/docs/codeql-cli/testing-query-help-files/)
|
||||
|
||||
## Query help example
|
||||
|
||||
The following example is a query help file for a query from the standard query suite for Java:
|
||||
|
||||
@@ -115,10 +115,11 @@ Alert queries (`@kind problem` or `path-problem`) support two further properties
|
||||
* `medium`
|
||||
* `high`
|
||||
* `very-high`
|
||||
* `@problem.severity`–defines the level of severity of the alert:
|
||||
* `@problem.severity`–defines the level of severity of non-security alerts:
|
||||
* `error`–an issue that is likely to cause incorrect program behavior, for example a crash or vulnerability.
|
||||
* `warning`–an issue that indicates a potential problem in the code, or makes the code fragile if another (unrelated) part of code is changed.
|
||||
* `recommendation`–an issue where the code behaves correctly, but it could be improved.
|
||||
* `@security-severity`-defines the level of severity, between 0.0 and 10.0, for queries with `@tags security`. For more information about calculating `@security-severity`, see the [GitHub changelog](https://github.blog/changelog/2021-07-19-codeql-code-scanning-new-severity-levels-for-security-alerts/).
|
||||
|
||||
The values of `@precision` and `@problem.severity` assigned to a query that is part of the standard set determine how the results are displayed by LGTM. See [About alerts](https://help.semmle.com/lgtm-enterprise/user/help/about-alerts.html) and [Alert interest](https://lgtm.com/help/lgtm/alert-interest) for further information. For information about using custom queries in LGTM on a 'per-project' basis, see [Writing custom queries to include in LGTM analysis](https://lgtm.com/help/lgtm/writing-custom-queries) and [About adding custom queries](https://help.semmle.com/lgtm-enterprise/admin/help/about-adding-custom-queries.html).
|
||||
|
||||
@@ -165,6 +166,8 @@ When you tag a query like this, the associated CWE pages from [MITRE.org](https:
|
||||
Code Scanning may use tags to identify queries with specific meanings across languages. Currently, there is only one such tag: `lines-of-code`. The sum of the results for queries with this tag that return a single number column ([example for JavaScript](https://github.com/github/codeql/blob/c47d680d65f09a851e41d4edad58ffa7486b5431/java/ql/src/Metrics/Summaries/LinesOfCode.ql)) is interpreted by Code Scanning as the lines of code under the source root present in the database. Each language should have exactly one query of this form.
|
||||
|
||||
|
||||
Maintainers are expected to add a `@security-severity` tag to security relevant queries that will be run on Code Scanning. There is a documented internal process for generating these `@security-severity` values.
|
||||
|
||||
## QL area
|
||||
|
||||
### Alert messages
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
lgtm,codescanning
|
||||
* The "Deserialization of user-controlled data" (`java/unsafe-deserialization`) query
|
||||
now recognizes `Jackson` deserialization.
|
||||
2
java/change-notes/2021-06-02-mvel-injection-query.md
Normal file
2
java/change-notes/2021-06-02-mvel-injection-query.md
Normal file
@@ -0,0 +1,2 @@
|
||||
lgtm,codescanning
|
||||
* The query "Expression language injection (MVEL) (`java/mvel-expression-injection`) has been promoted from experimental to the main query pack. Its results will now appear by default. This query was originally [submitted as an experimental query by @artem-smotrakov](https://github.com/github/codeql/pull/3329)
|
||||
@@ -0,0 +1,2 @@
|
||||
lgtm,codescanning
|
||||
* Added additional taint steps modeling constructors for collections in `java.util`.
|
||||
@@ -9,6 +9,7 @@ com.fasterxml.jackson.databind,,,3,,,,,,,,,,,,,,,3,
|
||||
com.google.common.base,,,85,,,,,,,,,,,,,,,62,23
|
||||
com.google.common.io,6,,73,,,,,,,,,,6,,,,,72,1
|
||||
com.unboundid.ldap.sdk,17,,,,,,,,17,,,,,,,,,,
|
||||
jakarta.json,,,123,,,,,,,,,,,,,,,100,23
|
||||
jakarta.ws.rs.client,1,,,,,,,,,1,,,,,,,,,
|
||||
jakarta.ws.rs.core,2,,143,,,,,,,,,,,2,,,,88,55
|
||||
java.beans,,,1,,,,,,,,,,,,,,,1,
|
||||
@@ -17,7 +18,8 @@ java.lang,,,3,,,,,,,,,,,,,,,1,2
|
||||
java.net,10,3,6,,,,,,,10,,,,,,,3,6,
|
||||
java.nio,10,,2,,10,,,,,,,,,,,,,2,
|
||||
java.sql,7,,,,,,,,,,,7,,,,,,,
|
||||
java.util,,,295,,,,,,,,,,,,,,,15,280
|
||||
java.util,,,332,,,,,,,,,,,,,,,15,317
|
||||
javax.json,,,123,,,,,,,,,,,,,,,100,23
|
||||
javax.naming.directory,1,,,,,,,,1,,,,,,,,,,
|
||||
javax.net.ssl,2,,,,,,,,,,2,,,,,,,,
|
||||
javax.servlet,4,21,2,,,3,1,,,,,,,,,,21,2,
|
||||
@@ -47,7 +49,7 @@ org.hibernate,7,,,,,,,,,,,7,,,,,,,
|
||||
org.jooq,1,,,,,,,,,,,1,,,,,,,
|
||||
org.springframework.beans,,,26,,,,,,,,,,,,,,,,26
|
||||
org.springframework.cache,,,13,,,,,,,,,,,,,,,,13
|
||||
org.springframework.http,14,,,,,,,,,14,,,,,,,,,
|
||||
org.springframework.http,14,,70,,,,,,,14,,,,,,,,60,10
|
||||
org.springframework.jdbc.core,10,,,,,,,,,,,10,,,,,,,
|
||||
org.springframework.jdbc.object,9,,,,,,,,,,,9,,,,,,,
|
||||
org.springframework.ldap.core,14,,,,,,,,14,,,,,,,,,,
|
||||
|
||||
|
@@ -14,9 +14,9 @@ Java framework & library support
|
||||
`Apache Commons Text <https://commons.apache.org/proper/commons-text/>`_,``org.apache.commons.text``,,272,,,,,,,,
|
||||
`Apache HttpComponents <https://hc.apache.org/>`_,"``org.apache.hc.core5.*``, ``org.apache.http``",5,136,28,,,3,,,,25
|
||||
`Google Guava <https://guava.dev/>`_,``com.google.common.*``,,158,6,,6,,,,,
|
||||
Java Standard Library,``java.*``,3,327,30,13,,,7,,,10
|
||||
Java extensions,"``javax.*``, ``jakarta.*``",22,294,18,,,,,1,1,2
|
||||
`Spring <https://spring.io/>`_,``org.springframework.*``,29,236,62,,,,19,14,,29
|
||||
Java Standard Library,``java.*``,3,364,30,13,,,7,,,10
|
||||
Java extensions,"``javax.*``, ``jakarta.*``",22,540,18,,,,,1,1,2
|
||||
`Spring <https://spring.io/>`_,``org.springframework.*``,29,306,62,,,,19,14,,29
|
||||
Others,"``com.esotericsoftware.kryo.io``, ``com.esotericsoftware.kryo5.io``, ``com.fasterxml.jackson.databind``, ``com.unboundid.ldap.sdk``, ``org.apache.commons.codec``, ``org.apache.commons.jexl2``, ``org.apache.commons.jexl3``, ``org.apache.directory.ldap.client.api``, ``org.apache.ibatis.jdbc``, ``org.dom4j``, ``org.hibernate``, ``org.jooq``, ``org.xml.sax``, ``org.xmlpull.v1``, ``play.mvc``",7,12,82,,,,14,18,,
|
||||
Totals,,84,2112,296,13,6,6,107,33,1,66
|
||||
Totals,,84,2465,296,13,6,6,107,33,1,66
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ from InstanceOfExpr ioe, RefType t, RefType ct
|
||||
where
|
||||
ioe.getExpr() instanceof ThisAccess and
|
||||
t = ioe.getExpr().getType() and
|
||||
ct = ioe.getTypeName().getType() and
|
||||
ct = ioe.getCheckedType() and
|
||||
ct.getASupertype*() = t
|
||||
select ioe,
|
||||
"Testing whether 'this' is an instance of $@ in $@ introduces a dependency cycle between the two types.",
|
||||
|
||||
@@ -15,7 +15,7 @@ import java
|
||||
from InstanceOfExpr ioe, RefType t, RefType ct
|
||||
where
|
||||
t = ioe.getExpr().getType() and
|
||||
ct = ioe.getTypeName().getType() and
|
||||
ct = ioe.getCheckedType() and
|
||||
ct = t.getASupertype+()
|
||||
select ioe,
|
||||
"There is no need to test whether an instance of $@ is also an instance of $@ - it always is.", t,
|
||||
|
||||
@@ -17,8 +17,8 @@ predicate instanceofInEquals(EqualsMethod m, InstanceOfExpr e) {
|
||||
m.fromSource() and
|
||||
e.getEnclosingCallable() = m and
|
||||
e.getExpr().(VarAccess).getVariable() = m.getParameter() and
|
||||
exists(Class instanceofType |
|
||||
instanceofType = e.getTypeName().getType() and
|
||||
exists(RefType instanceofType |
|
||||
instanceofType = e.getCheckedType() and
|
||||
not instanceofType.isFinal()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ class LockObjectField extends Field {
|
||||
class ValidSynchStmt extends Stmt {
|
||||
ValidSynchStmt() {
|
||||
// It's OK to lock the enclosing class.
|
||||
this.(SynchronizedStmt).getExpr().(TypeLiteral).getTypeName().getType() =
|
||||
this.(SynchronizedStmt).getExpr().(TypeLiteral).getReferencedType() =
|
||||
this.getEnclosingCallable().getDeclaringType()
|
||||
or
|
||||
// It's OK to lock on a "lock object field".
|
||||
|
||||
@@ -24,7 +24,7 @@ import java
|
||||
predicate isSynchronizedByBlock(Method m) {
|
||||
exists(SynchronizedStmt sync, Expr on | sync = m.getBody().getAChild*() and on = sync.getExpr() |
|
||||
if m.isStatic()
|
||||
then on.(TypeLiteral).getTypeName().getType() = m.getDeclaringType()
|
||||
then on.(TypeLiteral).getReferencedType() = m.getDeclaringType()
|
||||
else on.(ThisAccess).getType().(RefType).getSourceDeclaration() = m.getDeclaringType()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ import semmle.code.java.dataflow.SSA
|
||||
/** `ioe` is of the form `va instanceof t`. */
|
||||
predicate instanceOfCheck(InstanceOfExpr ioe, VarAccess va, RefType t) {
|
||||
ioe.getExpr() = va and
|
||||
ioe.getTypeName().getType().(RefType).getSourceDeclaration() = t
|
||||
ioe.getCheckedType().getSourceDeclaration() = t
|
||||
}
|
||||
|
||||
/** Expression `e` assumes that `va` could be of type `t`. */
|
||||
|
||||
@@ -127,7 +127,9 @@ predicate potentiallyStatic(InnerClass c) {
|
||||
forall(InnerClass superOfNested | superOfNested = nested.getASourceSupertype+() |
|
||||
potentiallyStatic(superOfNested)
|
||||
)
|
||||
)
|
||||
) and
|
||||
// JUnit Nested test classes are required to be non-static.
|
||||
not c.hasAnnotation("org.junit.jupiter.api", "Nested")
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
public void evaluate(Socket socket) throws IOException {
|
||||
try (BufferedReader reader = new BufferedReader(
|
||||
new InputStreamReader(socket.getInputStream()))) {
|
||||
|
||||
String expression = reader.readLine();
|
||||
// BAD: the user-provided expression is directly evaluated
|
||||
MVEL.eval(expression);
|
||||
}
|
||||
}
|
||||
|
||||
public void safeEvaluate(Socket socket) throws IOException {
|
||||
try (BufferedReader reader = new BufferedReader(
|
||||
new InputStreamReader(socket.getInputStream()))) {
|
||||
|
||||
String expression = reader.readLine();
|
||||
// GOOD: the user-provided expression is validated before evaluation
|
||||
validateExpression(expression);
|
||||
MVEL.eval(expression);
|
||||
}
|
||||
}
|
||||
|
||||
private void validateExpression(String expression) {
|
||||
// Validate that the expression does not contain unexpected code.
|
||||
// For instance, this can be done with allow-lists or deny-lists of code patterns.
|
||||
}
|
||||
@@ -3,11 +3,11 @@
|
||||
|
||||
<overview>
|
||||
<p>
|
||||
MVEL is an expression language based on Java-syntax.
|
||||
The language offers many features
|
||||
MVEL is an expression language based on Java-syntax,
|
||||
which offers many features
|
||||
including invocation of methods available in the JVM.
|
||||
If a MVEL expression is built using attacker-controlled data,
|
||||
and then evaluated, then it may allow the attacker to run arbitrary code.
|
||||
and then evaluated, then it may allow attackers to run arbitrary code.
|
||||
</p>
|
||||
</overview>
|
||||
|
||||
@@ -19,10 +19,12 @@ Including user input in a MVEL expression should be avoided.
|
||||
|
||||
<example>
|
||||
<p>
|
||||
The following example uses untrusted data to build a MVEL expression
|
||||
and then runs it in the default powerfull context.
|
||||
In the following sample, the first example uses untrusted data to build a MVEL expression
|
||||
and then runs it in the default context. In the second example, the untrusted data is
|
||||
validated with a custom method that checks that the expression does not contain unexpected code
|
||||
before evaluating it.
|
||||
</p>
|
||||
<sample src="UnsafeMvelExpressionEvaluation.java" />
|
||||
<sample src="MvelExpressionEvaluation.java" />
|
||||
</example>
|
||||
|
||||
<references>
|
||||
@@ -11,9 +11,9 @@
|
||||
*/
|
||||
|
||||
import java
|
||||
import MvelInjectionLib
|
||||
import semmle.code.java.security.MvelInjectionQuery
|
||||
import DataFlow::PathGraph
|
||||
|
||||
from DataFlow::PathNode source, DataFlow::PathNode sink, MvelInjectionConfig conf
|
||||
from DataFlow::PathNode source, DataFlow::PathNode sink, MvelInjectionFlowConfig conf
|
||||
where conf.hasFlowPath(source, sink)
|
||||
select sink.getNode(), source, sink, "MVEL injection from $@.", source.getNode(), "this user input"
|
||||
@@ -14,8 +14,8 @@ may have unforeseen effects, such as the execution of arbitrary code.
|
||||
</p>
|
||||
<p>
|
||||
There are many different serialization frameworks. This query currently
|
||||
supports Kryo, XmlDecoder, XStream, SnakeYaml, JYaml, JsonIO, YAMLBeans, HessianBurlap, Castor, Burlap
|
||||
and Java IO serialization through <code>ObjectInputStream</code>/<code>ObjectOutputStream</code>.
|
||||
supports Kryo, XmlDecoder, XStream, SnakeYaml, JYaml, JsonIO, YAMLBeans, HessianBurlap, Castor, Burlap,
|
||||
Jackson and Java IO serialization through <code>ObjectInputStream</code>/<code>ObjectOutputStream</code>.
|
||||
</p>
|
||||
</overview>
|
||||
|
||||
@@ -91,6 +91,15 @@ Remote code execution in JYaml library:
|
||||
JsonIO deserialization vulnerabilities:
|
||||
<a href="https://klezvirus.github.io/Advanced-Web-Hacking/Serialisation/">JsonIO deserialization</a>.
|
||||
</li>
|
||||
<li>
|
||||
Research by Moritz Bechler:
|
||||
<a href="https://www.github.com/mbechler/marshalsec/blob/master/marshalsec.pdf?raw=true">Java Unmarshaller Security - Turning your data into code execution</a>
|
||||
</li>
|
||||
<li>
|
||||
Blog posts by the developer of Jackson libraries:
|
||||
<a href="https://cowtowncoder.medium.com/on-jackson-cves-dont-panic-here-is-what-you-need-to-know-54cd0d6e8062">On Jackson CVEs: Don’t Panic — Here is what you need to know</a>
|
||||
<a href="https://cowtowncoder.medium.com/jackson-2-10-safe-default-typing-2d018f0ce2ba">Jackson 2.10: Safe Default Typing</a>
|
||||
</li>
|
||||
</references>
|
||||
|
||||
</qhelp>
|
||||
|
||||
@@ -12,51 +12,9 @@
|
||||
*/
|
||||
|
||||
import java
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
import semmle.code.java.security.UnsafeDeserialization
|
||||
import semmle.code.java.security.UnsafeDeserializationQuery
|
||||
import DataFlow::PathGraph
|
||||
|
||||
class UnsafeDeserializationConfig extends TaintTracking::Configuration {
|
||||
UnsafeDeserializationConfig() { this = "UnsafeDeserializationConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof UnsafeDeserializationSink }
|
||||
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
exists(ClassInstanceExpr cie |
|
||||
cie.getArgument(0) = pred.asExpr() and
|
||||
cie = succ.asExpr() and
|
||||
(
|
||||
cie.getConstructor().getDeclaringType() instanceof JsonIoJsonReader or
|
||||
cie.getConstructor().getDeclaringType() instanceof YamlBeansReader or
|
||||
cie.getConstructor().getDeclaringType().getASupertype*() instanceof UnsafeHessianInput or
|
||||
cie.getConstructor().getDeclaringType() instanceof BurlapInput
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(MethodAccess ma |
|
||||
ma.getMethod() instanceof BurlapInputInitMethod and
|
||||
ma.getArgument(0) = pred.asExpr() and
|
||||
ma.getQualifier() = succ.asExpr()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) {
|
||||
exists(ClassInstanceExpr cie |
|
||||
cie.getConstructor().getDeclaringType() instanceof JsonIoJsonReader and
|
||||
cie = node.asExpr() and
|
||||
exists(SafeJsonIoConfig sji | sji.hasFlowToExpr(cie.getArgument(1)))
|
||||
)
|
||||
or
|
||||
exists(MethodAccess ma |
|
||||
ma.getMethod() instanceof JsonIoJsonToJavaMethod and
|
||||
ma.getArgument(0) = node.asExpr() and
|
||||
exists(SafeJsonIoConfig sji | sji.hasFlowToExpr(ma.getArgument(1)))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
from DataFlow::PathNode source, DataFlow::PathNode sink, UnsafeDeserializationConfig conf
|
||||
where conf.hasFlowPath(source, sink)
|
||||
select sink.getNode().(UnsafeDeserializationSink).getMethodAccess(), source, sink,
|
||||
|
||||
@@ -16,9 +16,9 @@ import java
|
||||
import semmle.code.java.Collections
|
||||
|
||||
predicate guardedByInstanceOf(VarAccess e, RefType t) {
|
||||
exists(IfStmt s, InstanceOfExpr instanceCheck, Type checkType |
|
||||
exists(IfStmt s, InstanceOfExpr instanceCheck, RefType checkType |
|
||||
s.getCondition() = instanceCheck and
|
||||
instanceCheck.getTypeName().getType() = checkType and
|
||||
instanceCheck.getCheckedType() = checkType and
|
||||
// The same variable appears as the subject of the `instanceof`.
|
||||
instanceCheck.getExpr() = e.getVariable().getAnAccess() and
|
||||
// The checked type is either the type itself, or a raw version. For example, it is usually
|
||||
|
||||
@@ -1,367 +0,0 @@
|
||||
import java
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
import semmle.code.java.dataflow.TaintTracking
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for unsafe user input
|
||||
* that is used to construct and evaluate a MVEL expression.
|
||||
*/
|
||||
class MvelInjectionConfig extends TaintTracking::Configuration {
|
||||
MvelInjectionConfig() { this = "MvelInjectionConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof MvelEvaluationSink }
|
||||
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
expressionCompilationStep(node1, node2) or
|
||||
createExpressionCompilerStep(node1, node2) or
|
||||
expressionCompilerCompileStep(node1, node2) or
|
||||
createCompiledAccExpressionStep(node1, node2) or
|
||||
scriptCompileStep(node1, node2) or
|
||||
createMvelCompiledScriptStep(node1, node2) or
|
||||
templateCompileStep(node1, node2) or
|
||||
createTemplateCompilerStep(node1, node2)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A sink for EL injection vulnerabilities via MVEL,
|
||||
* i.e. methods that run evaluation of a MVEL expression.
|
||||
*/
|
||||
class MvelEvaluationSink extends DataFlow::ExprNode {
|
||||
MvelEvaluationSink() {
|
||||
exists(StaticMethodAccess ma, Method m | m = ma.getMethod() |
|
||||
(
|
||||
m instanceof MvelEvalMethod or
|
||||
m instanceof TemplateRuntimeEvaluationMethod
|
||||
) and
|
||||
ma.getArgument(0) = asExpr()
|
||||
)
|
||||
or
|
||||
exists(MethodAccess ma, Method m | m = ma.getMethod() |
|
||||
m instanceof MvelScriptEngineEvaluationMethod and
|
||||
ma.getArgument(0) = asExpr()
|
||||
)
|
||||
or
|
||||
exists(MethodAccess ma, Method m | m = ma.getMethod() |
|
||||
(
|
||||
m instanceof ExecutableStatementEvaluationMethod or
|
||||
m instanceof CompiledExpressionEvaluationMethod or
|
||||
m instanceof CompiledAccExpressionEvaluationMethod or
|
||||
m instanceof AccessorEvaluationMethod or
|
||||
m instanceof CompiledScriptEvaluationMethod or
|
||||
m instanceof MvelCompiledScriptEvaluationMethod
|
||||
) and
|
||||
ma.getQualifier() = asExpr()
|
||||
)
|
||||
or
|
||||
exists(StaticMethodAccess ma, Method m | m = ma.getMethod() |
|
||||
m instanceof MvelRuntimeEvaluationMethod and
|
||||
ma.getArgument(1) = asExpr()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node1` to `node2` is a dataflow step that compiles a MVEL expression
|
||||
* by callilng `MVEL.compileExpression(tainted)`.
|
||||
*/
|
||||
predicate expressionCompilationStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
exists(StaticMethodAccess ma, Method m | ma.getMethod() = m |
|
||||
m.getDeclaringType() instanceof MVEL and
|
||||
m.hasName("compileExpression") and
|
||||
ma.getAnArgument() = node1.asExpr() and
|
||||
node2.asExpr() = ma
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node1` to `node2` is a dataflow step creates `ExpressionCompiler`,
|
||||
* i.e. `new ExpressionCompiler(tainted)`.
|
||||
*/
|
||||
predicate createExpressionCompilerStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
exists(ConstructorCall cc |
|
||||
cc.getConstructedType() instanceof ExpressionCompiler and
|
||||
cc = node2.asExpr() and
|
||||
cc.getArgument(0) = node1.asExpr()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node1` to `node2` is a dataflow step creates `CompiledAccExpression`,
|
||||
* i.e. `new CompiledAccExpression(tainted, ...)`.
|
||||
*/
|
||||
predicate createCompiledAccExpressionStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
exists(ConstructorCall cc |
|
||||
cc.getConstructedType() instanceof CompiledAccExpression and
|
||||
cc = node2.asExpr() and
|
||||
cc.getArgument(0) = node1.asExpr()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node1` to `node2` is a dataflow step that compiles a MVEL expression
|
||||
* by calling `ExpressionCompiler.compile()`.
|
||||
*/
|
||||
predicate expressionCompilerCompileStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
exists(MethodAccess ma, Method m | ma.getMethod() = m |
|
||||
m.getDeclaringType() instanceof ExpressionCompiler and
|
||||
m.hasName("compile") and
|
||||
ma = node2.asExpr() and
|
||||
ma.getQualifier() = node1.asExpr()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node1` to `node2` is a dataflow step that compiles a script via `MvelScriptEngine`,
|
||||
* i.e. `engine.compile(tainted)` or `engine.compiledScript(tainted)`.
|
||||
*/
|
||||
predicate scriptCompileStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
exists(MethodAccess ma, Method m | ma.getMethod() = m |
|
||||
m instanceof MvelScriptEngineCompilationMethod and
|
||||
ma = node2.asExpr() and
|
||||
ma.getArgument(0) = node1.asExpr()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node1` to `node2` is a dataflow step creates `MvelCompiledScript`,
|
||||
* i.e. `new MvelCompiledScript(engine, tainted)`.
|
||||
*/
|
||||
predicate createMvelCompiledScriptStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
exists(ConstructorCall cc |
|
||||
cc.getConstructedType() instanceof MvelCompiledScript and
|
||||
cc = node2.asExpr() and
|
||||
cc.getArgument(1) = node1.asExpr()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node1` to `node2` is a dataflow step creates `TemplateCompiler`,
|
||||
* i.e. `new TemplateCompiler(tainted)`.
|
||||
*/
|
||||
predicate createTemplateCompilerStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
exists(ConstructorCall cc |
|
||||
cc.getConstructedType() instanceof TemplateCompiler and
|
||||
cc = node2.asExpr() and
|
||||
cc.getArgument(0) = node1.asExpr()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node1` to `node2` is a dataflow step that compiles a script via `TemplateCompiler`,
|
||||
* i.e. `compiler.compile()` or `TemplateCompiler.compileTemplate(tainted)`.
|
||||
*/
|
||||
predicate templateCompileStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
exists(MethodAccess ma, Method m | ma.getMethod() = m |
|
||||
m instanceof TemplateCompilerCompileMethod and
|
||||
ma.getQualifier() = node1.asExpr() and
|
||||
ma = node2.asExpr()
|
||||
)
|
||||
or
|
||||
exists(StaticMethodAccess ma, Method m | ma.getMethod() = m |
|
||||
m instanceof TemplateCompilerCompileTemplateMethod and
|
||||
ma = node2.asExpr() and
|
||||
ma.getArgument(0) = node1.asExpr()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Methods in the MVEL class that evaluate a MVEL expression.
|
||||
*/
|
||||
class MvelEvalMethod extends Method {
|
||||
MvelEvalMethod() {
|
||||
getDeclaringType() instanceof MVEL and
|
||||
(
|
||||
hasName("eval") or
|
||||
hasName("executeExpression") or
|
||||
hasName("evalToBoolean") or
|
||||
hasName("evalToString") or
|
||||
hasName("executeAllExpression") or
|
||||
hasName("executeSetExpression")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Methods in `MVEL` class that compile a MVEL expression.
|
||||
*/
|
||||
class MvelCompileExpressionMethod extends Method {
|
||||
MvelCompileExpressionMethod() {
|
||||
getDeclaringType() instanceof MVEL and
|
||||
(
|
||||
hasName("compileExpression") or
|
||||
hasName("compileGetExpression") or
|
||||
hasName("compileSetExpression")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Methods in `ExecutableStatement` that evaluate a MVEL expression.
|
||||
*/
|
||||
class ExecutableStatementEvaluationMethod extends Method {
|
||||
ExecutableStatementEvaluationMethod() {
|
||||
getDeclaringType() instanceof ExecutableStatement and
|
||||
hasName("getValue")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Methods in `CompiledExpression` that evaluate a MVEL expression.
|
||||
*/
|
||||
class CompiledExpressionEvaluationMethod extends Method {
|
||||
CompiledExpressionEvaluationMethod() {
|
||||
getDeclaringType() instanceof CompiledExpression and
|
||||
hasName("getDirectValue")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Methods in `CompiledAccExpression` that evaluate a MVEL expression.
|
||||
*/
|
||||
class CompiledAccExpressionEvaluationMethod extends Method {
|
||||
CompiledAccExpressionEvaluationMethod() {
|
||||
getDeclaringType() instanceof CompiledAccExpression and
|
||||
hasName("getValue")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Methods in `Accessor` that evaluate a MVEL expression.
|
||||
*/
|
||||
class AccessorEvaluationMethod extends Method {
|
||||
AccessorEvaluationMethod() {
|
||||
getDeclaringType() instanceof Accessor and
|
||||
hasName("getValue")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Methods in `MvelScriptEngine` that evaluate a MVEL expression.
|
||||
*/
|
||||
class MvelScriptEngineEvaluationMethod extends Method {
|
||||
MvelScriptEngineEvaluationMethod() {
|
||||
getDeclaringType() instanceof MvelScriptEngine and
|
||||
(hasName("eval") or hasName("evaluate"))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Methods in `MvelScriptEngine` that compile a MVEL expression.
|
||||
*/
|
||||
class MvelScriptEngineCompilationMethod extends Method {
|
||||
MvelScriptEngineCompilationMethod() {
|
||||
getDeclaringType() instanceof MvelScriptEngine and
|
||||
(hasName("compile") or hasName("compiledScript"))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Methods in `CompiledScript` that evaluate a MVEL expression.
|
||||
*/
|
||||
class CompiledScriptEvaluationMethod extends Method {
|
||||
CompiledScriptEvaluationMethod() {
|
||||
getDeclaringType() instanceof CompiledScript and
|
||||
hasName("eval")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Methods in `TemplateRuntime` that evaluate a MVEL template.
|
||||
*/
|
||||
class TemplateRuntimeEvaluationMethod extends Method {
|
||||
TemplateRuntimeEvaluationMethod() {
|
||||
getDeclaringType() instanceof TemplateRuntime and
|
||||
(hasName("eval") or hasName("execute"))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* `TemplateCompiler.compile()` method compiles a MVEL template.
|
||||
*/
|
||||
class TemplateCompilerCompileMethod extends Method {
|
||||
TemplateCompilerCompileMethod() {
|
||||
getDeclaringType() instanceof TemplateCompiler and
|
||||
hasName("compile")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* `TemplateCompiler.compileTemplate(tainted)` static method compiles a MVEL template.
|
||||
*/
|
||||
class TemplateCompilerCompileTemplateMethod extends Method {
|
||||
TemplateCompilerCompileTemplateMethod() {
|
||||
getDeclaringType() instanceof TemplateCompiler and
|
||||
hasName("compileTemplate")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Methods in `MvelCompiledScript` that evaluate a MVEL expression.
|
||||
*/
|
||||
class MvelCompiledScriptEvaluationMethod extends Method {
|
||||
MvelCompiledScriptEvaluationMethod() {
|
||||
getDeclaringType() instanceof MvelCompiledScript and
|
||||
hasName("eval")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Methods in `MVELRuntime` that evaluate a MVEL expression.
|
||||
*/
|
||||
class MvelRuntimeEvaluationMethod extends Method {
|
||||
MvelRuntimeEvaluationMethod() {
|
||||
getDeclaringType() instanceof MVELRuntime and
|
||||
hasName("execute")
|
||||
}
|
||||
}
|
||||
|
||||
class MVEL extends RefType {
|
||||
MVEL() { hasQualifiedName("org.mvel2", "MVEL") }
|
||||
}
|
||||
|
||||
class ExpressionCompiler extends RefType {
|
||||
ExpressionCompiler() { hasQualifiedName("org.mvel2.compiler", "ExpressionCompiler") }
|
||||
}
|
||||
|
||||
class ExecutableStatement extends RefType {
|
||||
ExecutableStatement() { hasQualifiedName("org.mvel2.compiler", "ExecutableStatement") }
|
||||
}
|
||||
|
||||
class CompiledExpression extends RefType {
|
||||
CompiledExpression() { hasQualifiedName("org.mvel2.compiler", "CompiledExpression") }
|
||||
}
|
||||
|
||||
class CompiledAccExpression extends RefType {
|
||||
CompiledAccExpression() { hasQualifiedName("org.mvel2.compiler", "CompiledAccExpression") }
|
||||
}
|
||||
|
||||
class Accessor extends RefType {
|
||||
Accessor() { hasQualifiedName("org.mvel2.compiler", "Accessor") }
|
||||
}
|
||||
|
||||
class CompiledScript extends RefType {
|
||||
CompiledScript() { hasQualifiedName("javax.script", "CompiledScript") }
|
||||
}
|
||||
|
||||
class MvelScriptEngine extends RefType {
|
||||
MvelScriptEngine() { hasQualifiedName("org.mvel2.jsr223", "MvelScriptEngine") }
|
||||
}
|
||||
|
||||
class MvelCompiledScript extends RefType {
|
||||
MvelCompiledScript() { hasQualifiedName("org.mvel2.jsr223", "MvelCompiledScript") }
|
||||
}
|
||||
|
||||
class TemplateRuntime extends RefType {
|
||||
TemplateRuntime() { hasQualifiedName("org.mvel2.templates", "TemplateRuntime") }
|
||||
}
|
||||
|
||||
class TemplateCompiler extends RefType {
|
||||
TemplateCompiler() { hasQualifiedName("org.mvel2.templates", "TemplateCompiler") }
|
||||
}
|
||||
|
||||
class MVELRuntime extends RefType {
|
||||
MVELRuntime() { hasQualifiedName("org.mvel2", "MVELRuntime") }
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
public void evaluate(Socket socket) throws IOException {
|
||||
try (BufferedReader reader = new BufferedReader(
|
||||
new InputStreamReader(socket.getInputStream()))) {
|
||||
|
||||
String expression = reader.readLine();
|
||||
MVEL.eval(expression);
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,7 @@ all certificate errors are ignored. In the 'GOOD' case, certificate errors are r
|
||||
|
||||
<references>
|
||||
<li>Teamdev:
|
||||
<a href="https://jxbrowser.support.teamdev.com/support/discussions/topics/9000051708">
|
||||
<a href="https://jxbrowser-support.teamdev.com/release-notes/2019/v6-24.html">
|
||||
Changelog of JxBrowser 6.24</a>.</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
@Controller
|
||||
public class UnsafeReflection {
|
||||
|
||||
@RequestMapping(value = {"/service/{beanIdOrClassName}/{methodName}"}, method = {RequestMethod.POST}, consumes = {"application/json"}, produces = {"application/json"})
|
||||
public Object bad1(@PathVariable("beanIdOrClassName") String beanIdOrClassName, @PathVariable("methodName") String methodName, @RequestBody Map<String, Object> body) throws Exception {
|
||||
List<Object> rawData = null;
|
||||
try {
|
||||
rawData = (List<Object>)body.get("methodInput");
|
||||
} catch (Exception e) {
|
||||
return e;
|
||||
}
|
||||
return invokeService(beanIdOrClassName, methodName, null, rawData);
|
||||
}
|
||||
|
||||
@GetMapping(value = "uf1")
|
||||
public void good1(HttpServletRequest request) throws Exception {
|
||||
HashSet<String> hashSet = new HashSet<>();
|
||||
hashSet.add("com.example.test1");
|
||||
hashSet.add("com.example.test2");
|
||||
String className = request.getParameter("className");
|
||||
String parameterValue = request.getParameter("parameterValue");
|
||||
if (!hashSet.contains(className)){
|
||||
throw new Exception("Class not valid: " + className);
|
||||
}
|
||||
try {
|
||||
Class clazz = Class.forName(className);
|
||||
Object object = clazz.getDeclaredConstructors()[0].newInstance(parameterValue); //good
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping(value = "uf2")
|
||||
public void good2(HttpServletRequest request) throws Exception {
|
||||
String className = request.getParameter("className");
|
||||
String parameterValue = request.getParameter("parameterValue");
|
||||
if (!"com.example.test1".equals(className)){
|
||||
throw new Exception("Class not valid: " + className);
|
||||
}
|
||||
try {
|
||||
Class clazz = Class.forName(className);
|
||||
Object object = clazz.getDeclaredConstructors()[0].newInstance(parameterValue); //good
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private Object invokeService(String beanIdOrClassName, String methodName, MultipartFile[] files, List<Object> data) throws Exception {
|
||||
BeanFactory beanFactory = new BeanFactory();
|
||||
try {
|
||||
Object bean = null;
|
||||
Class<?> beanClass = Class.forName(beanIdOrClassName);
|
||||
bean = beanFactory.getBean(beanClass);
|
||||
byte b;
|
||||
int i;
|
||||
Method[] arrayOfMethod;
|
||||
for (i = (arrayOfMethod = bean.getClass().getMethods()).length, b = 0; b < i; ) {
|
||||
Method method = arrayOfMethod[b];
|
||||
if (!method.getName().equals(methodName)) {
|
||||
b++;
|
||||
continue;
|
||||
}
|
||||
Object result = method.invoke(bean, data);
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
return map;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return e;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
class BeanFactory {
|
||||
|
||||
private static HashMap<String, Object> classNameMap = new HashMap<>();
|
||||
|
||||
private static HashMap<Class<?>, Object> classMap = new HashMap<>();
|
||||
|
||||
static {
|
||||
classNameMap.put("xxxx", Runtime.getRuntime());
|
||||
classMap.put(Runtime.class, Runtime.getRuntime());
|
||||
}
|
||||
|
||||
public Object getBean(Class<?> clzz) {
|
||||
return classMap.get(clzz);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
<overview>
|
||||
<p>
|
||||
Allowing users to freely choose the name of a class to instantiate could provide means to attack a vulnerable appplication.
|
||||
</p>
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>
|
||||
Create a list of classes that are allowed to load reflectively and strictly verify the input to ensure that
|
||||
users can only instantiate classes or execute methods that ought to be allowed.
|
||||
</p>
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
<p>
|
||||
The <code>bad</code> method shown below illustrate class loading with <code>Class.forName</code> without any check on the particular class being instantiated.
|
||||
The <code>good</code> methods illustrate some different ways to restrict which classes can be instantiated.
|
||||
</p>
|
||||
<sample src="UnsafeReflection.java" />
|
||||
|
||||
</example>
|
||||
|
||||
<references>
|
||||
|
||||
<li>
|
||||
Unsafe use of Reflection | OWASP:
|
||||
<a href="https://owasp.org/www-community/vulnerabilities/Unsafe_use_of_Reflection">Unsafe use of Reflection</a>.
|
||||
</li>
|
||||
<li>
|
||||
Java owasp: Classes should not be loaded dynamically:
|
||||
<a href="https://rules.sonarsource.com/java/tag/owasp/RSPEC-2658">Classes should not be loaded dynamically</a>.
|
||||
</li>
|
||||
</references>
|
||||
|
||||
</qhelp>
|
||||
@@ -0,0 +1,99 @@
|
||||
/**
|
||||
* @name Use of externally-controlled input to select classes or code ('unsafe reflection')
|
||||
* @description Use external input with reflection function to select the class or code to
|
||||
* be used, which brings serious security risks.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @precision high
|
||||
* @id java/unsafe-reflection
|
||||
* @tags security
|
||||
* external/cwe/cwe-470
|
||||
*/
|
||||
|
||||
import java
|
||||
import DataFlow
|
||||
import UnsafeReflectionLib
|
||||
import semmle.code.java.dataflow.DataFlow
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
import DataFlow::PathGraph
|
||||
|
||||
private class ContainsSanitizer extends DataFlow::BarrierGuard {
|
||||
ContainsSanitizer() { this.(MethodAccess).getMethod().hasName("contains") }
|
||||
|
||||
override predicate checks(Expr e, boolean branch) {
|
||||
e = this.(MethodAccess).getArgument(0) and branch = true
|
||||
}
|
||||
}
|
||||
|
||||
private class EqualsSanitizer extends DataFlow::BarrierGuard {
|
||||
EqualsSanitizer() { this.(MethodAccess).getMethod().hasName("equals") }
|
||||
|
||||
override predicate checks(Expr e, boolean branch) {
|
||||
e = [this.(MethodAccess).getArgument(0), this.(MethodAccess).getQualifier()] and
|
||||
branch = true
|
||||
}
|
||||
}
|
||||
|
||||
class UnsafeReflectionConfig extends TaintTracking::Configuration {
|
||||
UnsafeReflectionConfig() { this = "UnsafeReflectionConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof UnsafeReflectionSink }
|
||||
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
// Argument -> return of Class.forName, ClassLoader.loadClass
|
||||
exists(ReflectiveClassIdentifierMethodAccess rcimac |
|
||||
rcimac.getArgument(0) = pred.asExpr() and rcimac = succ.asExpr()
|
||||
)
|
||||
or
|
||||
// Qualifier -> return of Class.getDeclaredConstructors/Methods and similar
|
||||
exists(MethodAccess ma |
|
||||
(
|
||||
ma instanceof ReflectiveConstructorsAccess or
|
||||
ma instanceof ReflectiveMethodsAccess
|
||||
) and
|
||||
ma.getQualifier() = pred.asExpr() and
|
||||
ma = succ.asExpr()
|
||||
)
|
||||
or
|
||||
// Qualifier -> return of Object.getClass
|
||||
exists(MethodAccess ma |
|
||||
ma.getMethod().hasName("getClass") and
|
||||
ma.getMethod().getDeclaringType().hasQualifiedName("java.lang", "Object") and
|
||||
ma.getQualifier() = pred.asExpr() and
|
||||
ma = succ.asExpr()
|
||||
)
|
||||
or
|
||||
// Argument -> return of methods that look like Class.forName
|
||||
looksLikeResolveClassStep(pred, succ)
|
||||
or
|
||||
// Argument -> return of methods that look like `Object getInstance(Class c)`
|
||||
looksLikeInstantiateClassStep(pred, succ)
|
||||
or
|
||||
// Qualifier -> return of Constructor.newInstance, Class.newInstance
|
||||
exists(NewInstance ni |
|
||||
ni.getQualifier() = pred.asExpr() and
|
||||
ni = succ.asExpr()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
|
||||
guard instanceof ContainsSanitizer or guard instanceof EqualsSanitizer
|
||||
}
|
||||
}
|
||||
|
||||
private Expr getAMethodArgument(MethodAccess reflectiveCall) {
|
||||
result = reflectiveCall.(NewInstance).getAnArgument()
|
||||
or
|
||||
result = reflectiveCall.(MethodInvokeCall).getAnArgument()
|
||||
}
|
||||
|
||||
from
|
||||
DataFlow::PathNode source, DataFlow::PathNode sink, UnsafeReflectionConfig conf,
|
||||
MethodAccess reflectiveCall
|
||||
where
|
||||
conf.hasFlowPath(source, sink) and
|
||||
sink.getNode().asExpr() = reflectiveCall.getQualifier() and
|
||||
conf.hasFlowToExpr(getAMethodArgument(reflectiveCall))
|
||||
select sink.getNode(), source, sink, "Unsafe reflection of $@.", source.getNode(), "user input"
|
||||
@@ -0,0 +1,60 @@
|
||||
import java
|
||||
import DataFlow
|
||||
import semmle.code.java.Reflection
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
|
||||
/**
|
||||
* A call to `java.lang.reflect.Method.invoke`.
|
||||
*/
|
||||
class MethodInvokeCall extends MethodAccess {
|
||||
MethodInvokeCall() { this.getMethod().hasQualifiedName("java.lang.reflect", "Method", "invoke") }
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsafe reflection sink (the qualifier or method arguments to `Constructor.newInstance(...)` or `Method.invoke(...)`)
|
||||
*/
|
||||
class UnsafeReflectionSink extends DataFlow::ExprNode {
|
||||
UnsafeReflectionSink() {
|
||||
exists(MethodAccess ma |
|
||||
(
|
||||
ma.getMethod().hasQualifiedName("java.lang.reflect", "Constructor<>", "newInstance") or
|
||||
ma instanceof MethodInvokeCall
|
||||
) and
|
||||
this.asExpr() = [ma.getQualifier(), ma.getAnArgument()]
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `fromNode` to `toNode` is a dataflow step that looks like resolving a class.
|
||||
* A method probably resolves a class if it takes a string, returns a Class
|
||||
* and its name contains "resolve", "load", etc.
|
||||
*/
|
||||
predicate looksLikeResolveClassStep(DataFlow::Node fromNode, DataFlow::Node toNode) {
|
||||
exists(MethodAccess ma, Method m, int i, Expr arg |
|
||||
m = ma.getMethod() and arg = ma.getArgument(i)
|
||||
|
|
||||
m.getReturnType() instanceof TypeClass and
|
||||
m.getName().toLowerCase().regexpMatch("resolve|load|class|type") and
|
||||
arg.getType() instanceof TypeString and
|
||||
arg = fromNode.asExpr() and
|
||||
ma = toNode.asExpr()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `fromNode` to `toNode` is a dataflow step that looks like instantiating a class.
|
||||
* A method probably instantiates a class if it is external, takes a Class, returns an Object
|
||||
* and its name contains "instantiate" or similar terms.
|
||||
*/
|
||||
predicate looksLikeInstantiateClassStep(DataFlow::Node fromNode, DataFlow::Node toNode) {
|
||||
exists(MethodAccess ma, Method m, int i, Expr arg |
|
||||
m = ma.getMethod() and arg = ma.getArgument(i)
|
||||
|
|
||||
m.getReturnType() instanceof TypeObject and
|
||||
m.getName().toLowerCase().regexpMatch("instantiate|instance|create|make|getbean") and
|
||||
arg.getType() instanceof TypeClass and
|
||||
arg = fromNode.asExpr() and
|
||||
ma = toNode.asExpr()
|
||||
)
|
||||
}
|
||||
@@ -34,6 +34,29 @@ public class SpringUrlRedirect {
|
||||
}
|
||||
|
||||
@GetMapping("url5")
|
||||
public ResponseEntity<Void> bad5(String redirectUrl) {
|
||||
return ResponseEntity.status(HttpStatus.FOUND)
|
||||
.location(URI.create(redirectUrl))
|
||||
.build();
|
||||
}
|
||||
|
||||
@GetMapping("url6")
|
||||
public ResponseEntity<Void> bad6(String redirectUrl) {
|
||||
HttpHeaders httpHeaders = new HttpHeaders();
|
||||
httpHeaders.setLocation(URI.create(redirectUrl));
|
||||
|
||||
return new ResponseEntity<>(httpHeaders, HttpStatus.SEE_OTHER);
|
||||
}
|
||||
|
||||
@GetMapping("url7")
|
||||
public ResponseEntity<Void> bad7(String redirectUrl) {
|
||||
HttpHeaders httpHeaders = new HttpHeaders();
|
||||
httpHeaders.add("Location", redirectUrl);
|
||||
|
||||
return ResponseEntity.status(HttpStatus.SEE_OTHER).headers(httpHeaders).build();
|
||||
}
|
||||
|
||||
@GetMapping("url8")
|
||||
public RedirectView good1(String redirectUrl) {
|
||||
RedirectView rv = new RedirectView();
|
||||
if (redirectUrl.startsWith(VALID_REDIRECT)){
|
||||
|
||||
@@ -21,10 +21,10 @@ redirects on the server; then choose from that list based on the user input prov
|
||||
<example>
|
||||
|
||||
<p>The following examples show the bad case and the good case respectively.
|
||||
In <code>bad1</code> method and <code>bad2</code> method and <code>bad3</code> method and
|
||||
<code>bad4</code> method, shows an HTTP request parameter being used directly in a URL redirect
|
||||
without validating the input, which facilitates phishing attacks. In <code>good1</code> method,
|
||||
shows how to solve this problem by verifying whether the user input is a known fixed string beginning.
|
||||
The <code>bad</code> methods show an HTTP request parameter being used directly
|
||||
in a URL redirect without validating the input, which facilitates phishing attacks.
|
||||
In the <code>good1</code> method, it is shown how to solve this problem by verifying whether
|
||||
the user input is a known fixed string beginning.
|
||||
</p>
|
||||
|
||||
<sample src="SpringUrlRedirect.java" />
|
||||
@@ -33,5 +33,6 @@ shows how to solve this problem by verifying whether the user input is a known f
|
||||
<references>
|
||||
<li>A Guide To Spring Redirects: <a href="https://www.baeldung.com/spring-redirect-and-forward">Spring Redirects</a>.</li>
|
||||
<li>Url redirection - attack and defense: <a href="https://www.virtuesecurity.com/kb/url-redirection-attack-and-defense/">Url Redirection</a>.</li>
|
||||
<li>How to redirect to an external URL from Spring Boot REST Controller (Post/Redirect/Get pattern)?: <a href="https://fullstackdeveloper.guru/2021/03/12/how-to-redirect-to-an-external-url-from-spring-boot-rest-controller/">ResponseEntity Redirection</a>.</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
|
||||
@@ -34,6 +34,10 @@ class SpringUrlRedirectFlowConfig extends TaintTracking::Configuration {
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof SpringUrlRedirectSink }
|
||||
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node fromNode, DataFlow::Node toNode) {
|
||||
springUrlRedirectTaintStep(fromNode, toNode)
|
||||
}
|
||||
|
||||
override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
|
||||
guard instanceof StartsWithSanitizer
|
||||
}
|
||||
@@ -57,6 +61,8 @@ class SpringUrlRedirectFlowConfig extends TaintTracking::Configuration {
|
||||
not ma.getArgument(0).(CompileTimeConstantExpr).getStringValue().regexpMatch("^%s.*")
|
||||
)
|
||||
)
|
||||
or
|
||||
nonLocationHeaderSanitizer(node)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -35,8 +35,13 @@ class RedirectAppendCall extends MethodAccess {
|
||||
}
|
||||
|
||||
/** A URL redirection sink from spring controller method. */
|
||||
class SpringUrlRedirectSink extends DataFlow::Node {
|
||||
SpringUrlRedirectSink() {
|
||||
abstract class SpringUrlRedirectSink extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A sink for URL Redirection via the Spring View classes.
|
||||
*/
|
||||
private class SpringViewUrlRedirectSink extends SpringUrlRedirectSink {
|
||||
SpringViewUrlRedirectSink() {
|
||||
exists(RedirectBuilderExpr rbe |
|
||||
rbe.getRightOperand() = this.asExpr() and
|
||||
any(SpringRequestMappingMethod sqmm).polyCalls*(this.getEnclosingCallable())
|
||||
@@ -71,3 +76,64 @@ class SpringUrlRedirectSink extends DataFlow::Node {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A sink for URL Redirection via the `ResponseEntity` class.
|
||||
*/
|
||||
private class SpringResponseEntityUrlRedirectSink extends SpringUrlRedirectSink {
|
||||
SpringResponseEntityUrlRedirectSink() {
|
||||
// Find `new ResponseEntity(httpHeaders, ...)` or
|
||||
// `new ResponseEntity(..., httpHeaders, ...)` sinks
|
||||
exists(ClassInstanceExpr cie, Argument argument |
|
||||
cie.getConstructedType() instanceof SpringResponseEntity and
|
||||
argument.getType() instanceof SpringHttpHeaders and
|
||||
argument = cie.getArgument([0, 1]) and
|
||||
this.asExpr() = argument
|
||||
)
|
||||
or
|
||||
// Find `ResponseEntity.status(...).headers(taintHeaders).build()` or
|
||||
// `ResponseEntity.status(...).location(URI.create(taintURL)).build()` sinks
|
||||
exists(MethodAccess ma |
|
||||
ma.getMethod()
|
||||
.getDeclaringType()
|
||||
.hasQualifiedName("org.springframework.http", "ResponseEntity$HeadersBuilder<BodyBuilder>") and
|
||||
ma.getMethod().getName() in ["headers", "location"] and
|
||||
this.asExpr() = ma.getArgument(0)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class HttpHeadersMethodAccess extends MethodAccess {
|
||||
HttpHeadersMethodAccess() { this.getMethod().getDeclaringType() instanceof SpringHttpHeaders }
|
||||
}
|
||||
|
||||
private class HttpHeadersAddSetMethodAccess extends HttpHeadersMethodAccess {
|
||||
HttpHeadersAddSetMethodAccess() { this.getMethod().getName() in ["add", "set"] }
|
||||
}
|
||||
|
||||
private class HttpHeadersSetLocationMethodAccess extends HttpHeadersMethodAccess {
|
||||
HttpHeadersSetLocationMethodAccess() { this.getMethod().hasName("setLocation") }
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `fromNode` to `toNode` is a dataflow step from a tainted argument to
|
||||
* a `HttpHeaders` instance qualifier, i.e. `httpHeaders.setLocation(tainted)`.
|
||||
*/
|
||||
predicate springUrlRedirectTaintStep(DataFlow::Node fromNode, DataFlow::Node toNode) {
|
||||
exists(HttpHeadersSetLocationMethodAccess ma |
|
||||
fromNode.asExpr() = ma.getArgument(0) and
|
||||
toNode.asExpr() = ma.getQualifier()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A sanitizer to exclude the cases where the `HttpHeaders.add` or `HttpHeaders.set`
|
||||
* methods are called with a HTTP header other than "Location".
|
||||
* E.g: `httpHeaders.add("X-Some-Header", taintedUrlString)`
|
||||
*/
|
||||
predicate nonLocationHeaderSanitizer(DataFlow::Node node) {
|
||||
exists(HttpHeadersAddSetMethodAccess ma, Argument firstArg | node.asExpr() = ma.getArgument(1) |
|
||||
firstArg = ma.getArgument(0) and
|
||||
not firstArg.(CompileTimeConstantExpr).getStringValue().matches("Location")
|
||||
)
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ predicate instantiates(RefType t, GenericType g, int i, RefType arg) {
|
||||
* - a class `MyIntMap<V> extends HashMap<Integer, V>` instantiates `Map` (among others)
|
||||
* with the `0`-th type parameter being `Integer` and the `1`-th type parameter being `V`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate indirectlyInstantiates(RefType t, GenericType g, int i, RefType arg) {
|
||||
instantiates(t, g, i, arg)
|
||||
or
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user