mirror of
https://github.com/github/codeql.git
synced 2026-02-11 20:51:06 +01:00
C#: Add support for MaD barriers and barrier guards.
This commit is contained in:
@@ -197,6 +197,42 @@ module BarrierGuard<guardChecksSig/3 guardChecks> {
|
||||
}
|
||||
}
|
||||
|
||||
bindingset[this]
|
||||
private signature class ParamSig;
|
||||
|
||||
private module WithParam<ParamSig P> {
|
||||
/**
|
||||
* Holds if the guard `g` validates the expression `e` upon evaluating to `gv`.
|
||||
*
|
||||
* The expression `e` is expected to be a syntactic part of the guard `g`.
|
||||
* For example, the guard `g` might be a call `isSafe(x)` and the expression `e`
|
||||
* the argument `x`.
|
||||
*/
|
||||
signature predicate guardChecksSig(Guard g, Expr e, GuardValue gv, P param);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a set of barrier nodes for a guard that validates an expression.
|
||||
*
|
||||
* This is expected to be used in `isBarrier`/`isSanitizer` definitions
|
||||
* in data flow and taint tracking.
|
||||
*/
|
||||
module ParameterizedBarrierGuard<ParamSig P, WithParam<P>::guardChecksSig/4 guardChecks> {
|
||||
private import SsaImpl as SsaImpl
|
||||
|
||||
/** Gets a node that is safely guarded by the given guard check. */
|
||||
pragma[nomagic]
|
||||
Node getABarrierNode(P param) {
|
||||
SsaFlow::asNode(result) =
|
||||
SsaImpl::DataFlowIntegration::ParameterizedBarrierGuard<P, guardChecks/4>::getABarrierNode(param)
|
||||
or
|
||||
exists(Guard g, Expr e, GuardValue v |
|
||||
guardChecks(g, e, v, param) and
|
||||
g.controlsNode(result.getControlFlowNode(), e, v)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A reference contained in an object. This is either a field, a property,
|
||||
* or an element in a collection.
|
||||
|
||||
@@ -97,6 +97,7 @@ private import FlowSummaryImpl::Public
|
||||
private import FlowSummaryImpl::Private
|
||||
private import FlowSummaryImpl::Private::External
|
||||
private import semmle.code.csharp.commons.QualifiedName
|
||||
private import semmle.code.csharp.controlflow.Guards
|
||||
private import semmle.code.csharp.dispatch.OverridableCallable
|
||||
private import semmle.code.csharp.frameworks.System
|
||||
private import codeql.dataflow.internal.AccessPathSyntax as AccessPathSyntax
|
||||
@@ -115,7 +116,9 @@ module ModelValidation {
|
||||
summaryModel(_, _, _, _, _, _, path, _, _, _, _) or
|
||||
summaryModel(_, _, _, _, _, _, _, path, _, _, _) or
|
||||
sinkModel(_, _, _, _, _, _, path, _, _, _) or
|
||||
sourceModel(_, _, _, _, _, _, path, _, _, _)
|
||||
sourceModel(_, _, _, _, _, _, path, _, _, _) or
|
||||
barrierModel(_, _, _, _, _, _, path, _, _, _) or
|
||||
barrierGuardModel(_, _, _, _, _, _, path, _, _, _, _)
|
||||
}
|
||||
|
||||
private module MkAccessPath = AccessPathSyntax::AccessPath<getRelevantAccessPath/1>;
|
||||
@@ -128,6 +131,8 @@ module ModelValidation {
|
||||
exists(string pred, AccessPath input, AccessPathToken part |
|
||||
sinkModel(_, _, _, _, _, _, input, _, _, _) and pred = "sink"
|
||||
or
|
||||
barrierGuardModel(_, _, _, _, _, _, input, _, _, _, _) and pred = "barrier guard"
|
||||
or
|
||||
summaryModel(_, _, _, _, _, _, input, _, _, _, _) and pred = "summary"
|
||||
|
|
||||
(
|
||||
@@ -150,6 +155,8 @@ module ModelValidation {
|
||||
exists(string pred, AccessPath output, AccessPathToken part |
|
||||
sourceModel(_, _, _, _, _, _, output, _, _, _) and pred = "source"
|
||||
or
|
||||
barrierModel(_, _, _, _, _, _, output, _, _, _) and pred = "barrier"
|
||||
or
|
||||
summaryModel(_, _, _, _, _, _, _, output, _, _, _) and pred = "summary"
|
||||
|
|
||||
(
|
||||
@@ -167,7 +174,13 @@ module ModelValidation {
|
||||
private module KindValConfig implements SharedModelVal::KindValidationConfigSig {
|
||||
predicate summaryKind(string kind) { summaryModel(_, _, _, _, _, _, _, _, kind, _, _) }
|
||||
|
||||
predicate sinkKind(string kind) { sinkModel(_, _, _, _, _, _, _, kind, _, _) }
|
||||
predicate sinkKind(string kind) {
|
||||
sinkModel(_, _, _, _, _, _, _, kind, _, _)
|
||||
or
|
||||
barrierModel(_, _, _, _, _, _, _, kind, _, _)
|
||||
or
|
||||
barrierGuardModel(_, _, _, _, _, _, _, _, kind, _, _)
|
||||
}
|
||||
|
||||
predicate sourceKind(string kind) { sourceModel(_, _, _, _, _, _, _, kind, _, _) }
|
||||
|
||||
@@ -185,6 +198,12 @@ module ModelValidation {
|
||||
or
|
||||
sinkModel(namespace, type, _, name, signature, ext, _, _, provenance, _) and pred = "sink"
|
||||
or
|
||||
barrierModel(namespace, type, _, name, signature, ext, _, _, provenance, _) and
|
||||
pred = "barrier"
|
||||
or
|
||||
barrierGuardModel(namespace, type, _, name, signature, ext, _, _, _, provenance, _) and
|
||||
pred = "barrier guard"
|
||||
or
|
||||
summaryModel(namespace, type, _, name, signature, ext, _, _, _, provenance, _) and
|
||||
pred = "summary"
|
||||
or
|
||||
@@ -210,6 +229,14 @@ module ModelValidation {
|
||||
invalidProvenance(provenance) and
|
||||
result = "Unrecognized provenance description \"" + provenance + "\" in " + pred + " model."
|
||||
)
|
||||
or
|
||||
exists(string acceptingvalue |
|
||||
barrierGuardModel(_, _, _, _, _, _, _, acceptingvalue, _, _, _) and
|
||||
invalidAcceptingValue(acceptingvalue) and
|
||||
result =
|
||||
"Unrecognized accepting value description \"" + acceptingvalue +
|
||||
"\" in barrier guard model."
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if some row in a MaD flow model appears to contain typos. */
|
||||
@@ -229,6 +256,10 @@ private predicate elementSpec(
|
||||
or
|
||||
sinkModel(namespace, type, subtypes, name, signature, ext, _, _, _, _)
|
||||
or
|
||||
barrierModel(namespace, type, subtypes, name, signature, ext, _, _, _, _)
|
||||
or
|
||||
barrierGuardModel(namespace, type, subtypes, name, signature, ext, _, _, _, _, _)
|
||||
or
|
||||
summaryModel(namespace, type, subtypes, name, signature, ext, _, _, _, _, _)
|
||||
or
|
||||
Extensions::neutralModel(namespace, type, name, signature, _, _) and ext = "" and subtypes = true
|
||||
@@ -372,7 +403,9 @@ Declaration interpretElement(
|
||||
private predicate relevantExt(string ext) {
|
||||
summaryModel(_, _, _, _, _, ext, _, _, _, _, _) or
|
||||
sourceModel(_, _, _, _, _, ext, _, _, _, _) or
|
||||
sinkModel(_, _, _, _, _, ext, _, _, _, _)
|
||||
sinkModel(_, _, _, _, _, ext, _, _, _, _) or
|
||||
barrierModel(_, _, _, _, _, ext, _, _, _, _) or
|
||||
barrierGuardModel(_, _, _, _, _, ext, _, _, _, _, _)
|
||||
}
|
||||
|
||||
private class ExtPath = AccessPathSyntax::AccessPath<relevantExt/1>::AccessPath;
|
||||
@@ -411,6 +444,53 @@ private module Cached {
|
||||
isSinkNode(n, kind, model) and n.asNode() = node
|
||||
)
|
||||
}
|
||||
|
||||
private newtype TKindModelPair =
|
||||
TMkPair(string kind, string model) { isBarrierGuardNode(_, _, kind, model) }
|
||||
|
||||
private GuardValue convertAcceptingValue(AcceptingValue av) {
|
||||
av.isTrue() and result.asBooleanValue() = true
|
||||
or
|
||||
av.isFalse() and result.asBooleanValue() = false
|
||||
or
|
||||
av.isNoException() and result.getDualValue().isThrowsException()
|
||||
or
|
||||
av.isZero() and result.asIntValue() = 0
|
||||
or
|
||||
av.isNotZero() and result.getDualValue().asIntValue() = 0
|
||||
or
|
||||
av.isNull() and result.isNullValue()
|
||||
or
|
||||
av.isNotNull() and result.isNonNullValue()
|
||||
}
|
||||
|
||||
private predicate barrierGuardChecks(Guard g, Expr e, GuardValue gv, TKindModelPair kmp) {
|
||||
exists(
|
||||
SourceSinkInterpretationInput::InterpretNode n, AcceptingValue acceptingvalue, string kind,
|
||||
string model
|
||||
|
|
||||
isBarrierGuardNode(n, acceptingvalue, kind, model) and
|
||||
n.asNode().asExpr() = e and
|
||||
kmp = TMkPair(kind, model) and
|
||||
gv = convertAcceptingValue(acceptingvalue)
|
||||
|
|
||||
g.(Call).getAnArgument() = e or g.(MethodCall).getQualifier() = e
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` is specified as a barrier with the given kind in a MaD flow
|
||||
* model.
|
||||
*/
|
||||
cached
|
||||
predicate barrierNode(Node node, string kind, string model) {
|
||||
exists(SourceSinkInterpretationInput::InterpretNode n |
|
||||
isBarrierNode(n, kind, model) and n.asNode() = node
|
||||
)
|
||||
or
|
||||
ParameterizedBarrierGuard<TKindModelPair, barrierGuardChecks/4>::getABarrierNode(TMkPair(kind,
|
||||
model)) = node
|
||||
}
|
||||
}
|
||||
|
||||
import Cached
|
||||
@@ -427,6 +507,12 @@ predicate sourceNode(Node node, string kind) { sourceNode(node, kind, _) }
|
||||
*/
|
||||
predicate sinkNode(Node node, string kind) { sinkNode(node, kind, _) }
|
||||
|
||||
/**
|
||||
* Holds if `node` is specified as a barrier with the given kind in a MaD flow
|
||||
* model.
|
||||
*/
|
||||
predicate barrierNode(Node node, string kind) { barrierNode(node, kind, _) }
|
||||
|
||||
private predicate isOverridableCallable(OverridableCallable c) {
|
||||
not exists(Type t, Callable base | c.getOverridee+() = base and t = base.getDeclaringType() |
|
||||
t instanceof SystemObjectClass or
|
||||
|
||||
@@ -232,16 +232,27 @@ module SourceSinkInterpretationInput implements
|
||||
}
|
||||
|
||||
predicate barrierElement(
|
||||
Element n, string output, string kind, Public::Provenance provenance, string model
|
||||
Element e, string output, string kind, Public::Provenance provenance, string model
|
||||
) {
|
||||
none()
|
||||
exists(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext
|
||||
|
|
||||
barrierModel(namespace, type, subtypes, name, signature, ext, output, kind, provenance, model) and
|
||||
e = interpretElement(namespace, type, subtypes, name, signature, ext)
|
||||
)
|
||||
}
|
||||
|
||||
predicate barrierGuardElement(
|
||||
Element n, string input, Public::AcceptingValue acceptingvalue, string kind,
|
||||
Element e, string input, Public::AcceptingValue acceptingvalue, string kind,
|
||||
Public::Provenance provenance, string model
|
||||
) {
|
||||
none()
|
||||
exists(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext
|
||||
|
|
||||
barrierGuardModel(namespace, type, subtypes, name, signature, ext, input, acceptingvalue,
|
||||
kind, provenance, model) and
|
||||
e = interpretElement(namespace, type, subtypes, name, signature, ext)
|
||||
)
|
||||
}
|
||||
|
||||
class SourceOrSinkElement = Element;
|
||||
|
||||
@@ -987,6 +987,36 @@ private module Cached {
|
||||
|
||||
predicate getABarrierNode = getABarrierNodeImpl/0;
|
||||
}
|
||||
|
||||
bindingset[this]
|
||||
private signature class ParamSig;
|
||||
|
||||
private module WithParam<ParamSig P> {
|
||||
signature predicate guardChecksSig(Guards::Guard g, Expr e, Guards::GuardValue gv, P param);
|
||||
}
|
||||
|
||||
cached // nothing is actually cached
|
||||
module ParameterizedBarrierGuard<ParamSig P, WithParam<P>::guardChecksSig/4 guardChecks> {
|
||||
private predicate guardChecksAdjTypes(
|
||||
Guards::Guards::Guard g, Expr e, Guards::GuardValue gv, P param
|
||||
) {
|
||||
guardChecks(g, e, gv, param)
|
||||
}
|
||||
|
||||
private predicate guardChecksWithWrappers(
|
||||
DataFlowIntegrationInput::Guard g, Definition def, Guards::GuardValue val, P param
|
||||
) {
|
||||
Guards::Guards::ParameterizedValidationWrapper<P, guardChecksAdjTypes/4>::guardChecksDef(g,
|
||||
def, val, param)
|
||||
}
|
||||
|
||||
private Node getABarrierNodeImpl(P param) {
|
||||
result =
|
||||
DataFlowIntegrationImpl::BarrierGuardDefWithState<P, guardChecksWithWrappers/4>::getABarrierNode(param)
|
||||
}
|
||||
|
||||
predicate getABarrierNode = getABarrierNodeImpl/1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user