Merge pull request #19382 from michaelnebel/shared/modelgenrefactor

Shared: Re-factor summary, source and sink model generators into separate modules.
This commit is contained in:
Michael Nebel
2025-05-02 09:38:24 +02:00
committed by GitHub
53 changed files with 1566 additions and 1459 deletions

View File

@@ -7,6 +7,7 @@
*/
import internal.CaptureModels
import SummaryModels
from DataFlowSummaryTargetApi api, string flow
where flow = ContentSensitive::captureFlow(api, _)

View File

@@ -7,6 +7,7 @@
*/
import internal.CaptureModels
import SummaryModels
from DataFlowSummaryTargetApi api, string noflow
where noflow = captureNeutral(api)

View File

@@ -7,8 +7,8 @@
*/
import internal.CaptureModels
import Heuristic
import SinkModels
from DataFlowSinkTargetApi api, string sink
where sink = captureSink(api)
where sink = Heuristic::captureSink(api)
select sink order by sink

View File

@@ -7,8 +7,8 @@
*/
import internal.CaptureModels
import Heuristic
import SourceModels
from DataFlowSourceTargetApi api, string source
where source = captureSource(api)
where source = Heuristic::captureSource(api)
select source order by source

View File

@@ -7,6 +7,7 @@
*/
import internal.CaptureModels
import SummaryModels
from DataFlowSummaryTargetApi api, string flow
where flow = captureFlow(api, _)

View File

@@ -2,7 +2,7 @@
* Provides predicates related to capturing summary models of the Standard or a 3rd party library.
*/
private import cpp
private import cpp as Cpp
private import semmle.code.cpp.ir.IR
private import semmle.code.cpp.dataflow.ExternalFlow as ExternalFlow
private import semmle.code.cpp.ir.dataflow.internal.DataFlowImplCommon as DataFlowImplCommon
@@ -10,113 +10,67 @@ private import semmle.code.cpp.ir.dataflow.internal.DataFlowImplSpecific
private import semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate as DataFlowPrivate
private import semmle.code.cpp.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
private import semmle.code.cpp.ir.dataflow.internal.TaintTrackingImplSpecific
private import semmle.code.cpp.dataflow.new.TaintTracking
private import semmle.code.cpp.dataflow.new.TaintTracking as Tt
private import semmle.code.cpp.dataflow.new.DataFlow as Df
private import codeql.mad.modelgenerator.internal.ModelGeneratorImpl
module ModelGeneratorInput implements ModelGeneratorInputSig<Location, CppDataFlow> {
/**
* Holds if `f` is a "private" function.
*
* A "private" function does not contribute any models as it is assumed
* to be an implementation detail of some other "public" function for which
* we will generate a summary.
*/
private predicate isPrivateOrProtected(Cpp::Function f) {
f.getNamespace().getParentNamespace*().isAnonymous()
or
exists(Cpp::MemberFunction mf | mf = f |
mf.isPrivate()
or
mf.isProtected()
)
or
f.isStatic()
}
private predicate isUninterestingForModels(Callable api) {
// Note: This also makes all global/static-local variables
// not relevant (which is good!)
not api.(Cpp::Function).hasDefinition()
or
isPrivateOrProtected(api)
or
api instanceof Cpp::Destructor
or
api = any(Cpp::LambdaExpression lambda).getLambdaFunction()
or
api.isFromUninstantiatedTemplate(_)
}
private predicate relevant(Callable api) {
api.fromSource() and
not isUninterestingForModels(api)
}
module ModelGeneratorCommonInput implements ModelGeneratorCommonInputSig<Cpp::Location, CppDataFlow>
{
private module DataFlow = Df::DataFlow;
class Type = DataFlowPrivate::DataFlowType;
// Note: This also includes `this`
class Parameter = DataFlow::ParameterNode;
class Callable = Declaration;
class Callable = Cpp::Declaration;
class NodeExtended extends DataFlow::Node {
Callable getAsExprEnclosingCallable() { result = this.asExpr().getEnclosingDeclaration() }
}
Parameter asParameter(NodeExtended n) { result = n }
Callable getEnclosingCallable(NodeExtended n) {
result = n.getEnclosingCallable().asSourceCallable()
}
Callable getAsExprEnclosingCallable(NodeExtended n) {
result = n.asExpr().getEnclosingDeclaration()
}
/** Gets `api` if it is relevant. */
private Callable liftedImpl(Callable api) { result = api and relevant(api) }
private predicate hasManualSummaryModel(Callable api) {
api = any(FlowSummaryImpl::Public::SummarizedCallable sc | sc.applyManualModel()) or
api = any(FlowSummaryImpl::Public::NeutralSummaryCallable sc | sc.hasManualModel())
}
private predicate hasManualSourceModel(Callable api) {
api = any(FlowSummaryImpl::Public::NeutralSourceCallable sc | sc.hasManualModel())
}
private predicate hasManualSinkModel(Callable api) {
api = any(FlowSummaryImpl::Public::NeutralSinkCallable sc | sc.hasManualModel())
}
/**
* Holds if `f` is a "private" function.
*
* A "private" function does not contribute any models as it is assumed
* to be an implementation detail of some other "public" function for which
* we will generate a summary.
*/
private predicate isPrivateOrProtected(Function f) {
f.getNamespace().getParentNamespace*().isAnonymous()
or
exists(MemberFunction mf | mf = f |
mf.isPrivate()
or
mf.isProtected()
)
or
f.isStatic()
}
private predicate isUninterestingForModels(Callable api) {
// Note: This also makes all global/static-local variables
// not relevant (which is good!)
not api.(Function).hasDefinition()
or
isPrivateOrProtected(api)
or
api instanceof Destructor
or
api = any(LambdaExpression lambda).getLambdaFunction()
or
api.isFromUninstantiatedTemplate(_)
}
private predicate relevant(Callable api) {
api.fromSource() and
not isUninterestingForModels(api)
}
class SummaryTargetApi extends Callable {
private Callable lift;
SummaryTargetApi() {
lift = liftedImpl(this) and
not hasManualSummaryModel(lift)
}
Callable lift() { result = lift }
predicate isRelevant() {
relevant(this) and
not hasManualSummaryModel(this)
}
}
class SourceOrSinkTargetApi extends Callable {
SourceOrSinkTargetApi() { relevant(this) }
}
class SinkTargetApi extends SourceOrSinkTargetApi {
SinkTargetApi() { not hasManualSinkModel(this) }
}
class SourceTargetApi extends SourceOrSinkTargetApi {
SourceTargetApi() { not hasManualSourceModel(this) }
}
class InstanceParameterNode extends DataFlow::ParameterNode {
InstanceParameterNode() {
DataFlowPrivate::nodeHasInstruction(this,
@@ -124,7 +78,7 @@ module ModelGeneratorInput implements ModelGeneratorInputSig<Location, CppDataFl
}
}
private predicate isFinalMemberFunction(MemberFunction mf) {
private predicate isFinalMemberFunction(Cpp::MemberFunction mf) {
mf.isFinal()
or
mf.getDeclaringType().isFinal()
@@ -146,12 +100,12 @@ module ModelGeneratorInput implements ModelGeneratorInputSig<Location, CppDataFl
* - An uninstantiated template, or
* - A declaration that is not from a template instantiation.
*/
private string templateParams(Declaration template) {
private string templateParams(Cpp::Declaration template) {
exists(string params |
params =
concat(int i |
|
template.getTemplateArgument(i).(TypeTemplateParameter).getName(), "," order by i
template.getTemplateArgument(i).(Cpp::TypeTemplateParameter).getName(), "," order by i
)
|
if params = "" then result = "" else result = "<" + params + ">"
@@ -166,7 +120,7 @@ module ModelGeneratorInput implements ModelGeneratorInputSig<Location, CppDataFl
* - An uninstantiated template, or
* - A declaration that is not from a template instantiation.
*/
private string params(Function functionTemplate) {
private string params(Cpp::Function functionTemplate) {
exists(string params |
params =
concat(int i |
@@ -193,7 +147,7 @@ module ModelGeneratorInput implements ModelGeneratorInputSig<Location, CppDataFl
Callable callable, string namespace, string type, string name, string params
) {
exists(
Function functionTemplate, string typeWithoutTemplateArgs, string nameWithoutTemplateArgs
Cpp::Function functionTemplate, string typeWithoutTemplateArgs, string nameWithoutTemplateArgs
|
functionTemplate = ExternalFlow::getFullyTemplatedFunction(callable) and
functionTemplate.hasQualifiedName(namespace, typeWithoutTemplateArgs, nameWithoutTemplateArgs) and
@@ -201,7 +155,7 @@ module ModelGeneratorInput implements ModelGeneratorInputSig<Location, CppDataFl
name = nameWithoutTemplateArgs + templateParams(functionTemplate) and
params = params(functionTemplate)
|
exists(Class classTemplate |
exists(Cpp::Class classTemplate |
classTemplate = functionTemplate.getDeclaringType() and
type = typeWithoutTemplateArgs + templateParams(classTemplate)
)
@@ -263,10 +217,10 @@ module ModelGeneratorInput implements ModelGeneratorInputSig<Location, CppDataFl
/** Holds if this instance access is to an enclosing instance of type `t`. */
pragma[nomagic]
private predicate isEnclosingInstanceAccess(DataFlowPrivate::ReturnNode n, Class t) {
private predicate isEnclosingInstanceAccess(DataFlowPrivate::ReturnNode n, Cpp::Class t) {
n.getKind().isIndirectReturn(-1) and
t = n.getType().stripType() and
t != n.getEnclosingCallable().asSourceCallable().(Function).getDeclaringType()
t != n.getEnclosingCallable().asSourceCallable().(Cpp::Function).getDeclaringType()
}
pragma[nomagic]
@@ -275,26 +229,6 @@ module ModelGeneratorInput implements ModelGeneratorInputSig<Location, CppDataFl
not isEnclosingInstanceAccess(node, _)
}
predicate sinkModelSanitizer(DataFlow::Node node) { none() }
predicate apiSource(DataFlow::Node source) {
DataFlowPrivate::nodeHasOperand(source, any(DataFlow::FieldAddress fa), 1)
or
source instanceof DataFlow::ParameterNode
}
string getInputArgument(DataFlow::Node source) {
exists(DataFlowPrivate::Position pos, int argumentIndex, int indirectionIndex |
source.(DataFlow::ParameterNode).isParameterOf(_, pos) and
argumentIndex = pos.getArgumentIndex() and
indirectionIndex = pos.getIndirectionIndex() and
result = "Argument[" + DataFlow::repeatStars(indirectionIndex) + argumentIndex + "]"
)
or
DataFlowPrivate::nodeHasOperand(source, any(DataFlow::FieldAddress fa), 1) and
result = qualifierString()
}
DataFlowPrivate::ParameterPosition getReturnKindParamPosition(DataFlowPrivate::ReturnKind k) {
exists(int argumentIndex, int indirectionIndex |
k.isIndirectReturn(argumentIndex) and
@@ -314,18 +248,71 @@ module ModelGeneratorInput implements ModelGeneratorInputSig<Location, CppDataFl
)
}
predicate irrelevantSourceSinkApi(Callable source, SourceTargetApi api) { none() }
bindingset[kind]
predicate isRelevantSourceKind(string kind) { any() }
bindingset[kind]
predicate isRelevantSinkKind(string kind) { any() }
predicate containerContent(DataFlow::ContentSet cs) { cs instanceof DataFlow::ElementContent }
string partialModelRow(Callable api, int i) {
i = 0 and qualifiedName(api, result, _, _, _) // namespace
or
i = 1 and qualifiedName(api, _, result, _, _) // type
or
i = 2 and result = isExtensible(api) // extensible
or
i = 3 and qualifiedName(api, _, _, result, _) // name
or
i = 4 and qualifiedName(api, _, _, _, result) // parameters
or
i = 5 and result = "" and exists(api) // ext
}
string partialNeutralModelRow(Callable api, int i) {
i = 0 and qualifiedName(api, result, _, _, _) // namespace
or
i = 1 and qualifiedName(api, _, result, _, _) // type
or
i = 2 and qualifiedName(api, _, _, result, _) // name
or
i = 3 and qualifiedName(api, _, _, _, result) // parameters
}
}
private import ModelGeneratorCommonInput
private import MakeModelGeneratorFactory<Cpp::Location, CppDataFlow, CppTaintTracking, ModelGeneratorCommonInput>
private module SummaryModelGeneratorInput implements SummaryModelGeneratorInputSig {
private module DataFlow = Df::DataFlow;
Parameter asParameter(NodeExtended n) { result = n }
Callable getAsExprEnclosingCallable(NodeExtended n) {
result = n.asExpr().getEnclosingDeclaration()
}
private predicate hasManualSummaryModel(Callable api) {
api = any(FlowSummaryImpl::Public::SummarizedCallable sc | sc.applyManualModel()) or
api = any(FlowSummaryImpl::Public::NeutralSummaryCallable sc | sc.hasManualModel())
}
/** Gets `api` if it is relevant. */
private Callable liftedImpl(Callable api) { result = api and relevant(api) }
class SummaryTargetApi extends Callable {
private Callable lift;
SummaryTargetApi() {
lift = liftedImpl(this) and
not hasManualSummaryModel(lift)
}
Callable lift() { result = lift }
predicate isRelevant() {
relevant(this) and
not hasManualSummaryModel(this)
}
}
predicate isAdditionalContentFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
TaintTracking::defaultAdditionalTaintStep(node1, node2, _) and
Tt::TaintTracking::defaultAdditionalTaintStep(node1, node2, _) and
not exists(DataFlow::Content f |
DataFlowPrivate::readStep(node1, f, node2) and containerContent(f)
)
@@ -341,7 +328,7 @@ module ModelGeneratorInput implements ModelGeneratorInputSig<Location, CppDataFl
predicate isCallback(DataFlow::ContentSet c) { none() }
string getSyntheticName(DataFlow::ContentSet c) {
exists(Field f |
exists(Cpp::Field f |
not f.isPublic() and
f = c.(DataFlow::FieldContent).getField() and
result = f.getName()
@@ -373,40 +360,52 @@ module ModelGeneratorInput implements ModelGeneratorInputSig<Location, CppDataFl
result = "Element[" + ec.getIndirectionIndex() + "]"
)
}
}
predicate isUninterestingForDataFlowModels(Callable api) { none() }
predicate isUninterestingForHeuristicDataFlowModels(Callable api) {
isUninterestingForDataFlowModels(api)
private module SourceModelGeneratorInput implements SourceModelGeneratorInputSig {
private predicate hasManualSourceModel(Callable api) {
api = any(FlowSummaryImpl::Public::NeutralSourceCallable sc | sc.hasManualModel())
}
string partialModelRow(Callable api, int i) {
i = 0 and qualifiedName(api, result, _, _, _) // namespace
or
i = 1 and qualifiedName(api, _, result, _, _) // type
or
i = 2 and result = isExtensible(api) // extensible
or
i = 3 and qualifiedName(api, _, _, result, _) // name
or
i = 4 and qualifiedName(api, _, _, _, result) // parameters
or
i = 5 and result = "" and exists(api) // ext
}
string partialNeutralModelRow(Callable api, int i) {
i = 0 and qualifiedName(api, result, _, _, _) // namespace
or
i = 1 and qualifiedName(api, _, result, _, _) // type
or
i = 2 and qualifiedName(api, _, _, result, _) // name
or
i = 3 and qualifiedName(api, _, _, _, result) // parameters
class SourceTargetApi extends Callable {
SourceTargetApi() { relevant(this) and not hasManualSourceModel(this) }
}
predicate sourceNode = ExternalFlow::sourceNode/2;
}
private module SinkModelGeneratorInput implements SinkModelGeneratorInputSig {
private module DataFlow = Df::DataFlow;
private predicate hasManualSinkModel(Callable api) {
api = any(FlowSummaryImpl::Public::NeutralSinkCallable sc | sc.hasManualModel())
}
class SinkTargetApi extends Callable {
SinkTargetApi() { relevant(this) and not hasManualSinkModel(this) }
}
predicate apiSource(DataFlow::Node source) {
DataFlowPrivate::nodeHasOperand(source, any(DataFlow::FieldAddress fa), 1)
or
source instanceof DataFlow::ParameterNode
}
string getInputArgument(DataFlow::Node source) {
exists(DataFlowPrivate::Position pos, int argumentIndex, int indirectionIndex |
source.(DataFlow::ParameterNode).isParameterOf(_, pos) and
argumentIndex = pos.getArgumentIndex() and
indirectionIndex = pos.getIndirectionIndex() and
result = "Argument[" + DataFlow::repeatStars(indirectionIndex) + argumentIndex + "]"
)
or
DataFlowPrivate::nodeHasOperand(source, any(DataFlow::FieldAddress fa), 1) and
result = qualifierString()
}
predicate sinkNode = ExternalFlow::sinkNode/2;
}
import MakeModelGenerator<Location, CppDataFlow, CppTaintTracking, ModelGeneratorInput>
import MakeSummaryModelGenerator<SummaryModelGeneratorInput> as SummaryModels
import MakeSourceModelGenerator<SourceModelGeneratorInput> as SourceModels
import MakeSinkModelGenerator<SinkModelGeneratorInput> as SinkModels

View File

@@ -1,6 +1,6 @@
private import cpp as Cpp
private import codeql.mad.modelgenerator.internal.ModelPrinting
private import CaptureModels::ModelGeneratorInput as ModelGeneratorInput
private import CaptureModels::ModelGeneratorCommonInput as ModelGeneratorInput
private module ModelPrintingLang implements ModelPrintingLangSig {
class Callable = Cpp::Declaration;

View File

@@ -1,5 +1,6 @@
import cpp
import utils.modelgenerator.internal.CaptureModels
import SummaryModels
import InlineModelsAsDataTest
module InlineMadTestConfig implements InlineMadTestConfigSig {

View File

@@ -1,5 +1,6 @@
import cpp
import utils.modelgenerator.internal.CaptureModels
import SummaryModels
import InlineModelsAsDataTest
module InlineMadTestConfig implements InlineMadTestConfigSig {

View File

@@ -7,6 +7,7 @@
*/
import internal.CaptureModels
import SummaryModels
from DataFlowSummaryTargetApi api, string flow
where flow = ContentSensitive::captureFlow(api, _)

View File

@@ -7,6 +7,7 @@
*/
import internal.CaptureModels
import SummaryModels
from DataFlowSummaryTargetApi api, string noflow
where noflow = captureNeutral(api)

View File

@@ -7,6 +7,7 @@
*/
import internal.CaptureModels
import SinkModels
from DataFlowSinkTargetApi api, string sink
where sink = Heuristic::captureSink(api)

View File

@@ -7,6 +7,7 @@
*/
import internal.CaptureModels
import SourceModels
from DataFlowSourceTargetApi api, string source
where source = Heuristic::captureSource(api)

View File

@@ -7,6 +7,7 @@
*/
import internal.CaptureModels
import SummaryModels
from DataFlowSummaryTargetApi api, string flow
where flow = captureFlow(api, _)

View File

@@ -10,6 +10,7 @@
import csharp
import utils.modelgenerator.internal.CaptureModels
import SummaryModels
import PartialFlow::PartialPathGraph
int explorationLimit() { result = 3 }

View File

@@ -10,6 +10,7 @@
import csharp
import utils.modelgenerator.internal.CaptureModels
import SummaryModels
import Heuristic
import PropagateFlow::PathGraph

View File

@@ -15,7 +15,41 @@ private import semmle.code.csharp.frameworks.System
private import semmle.code.csharp.Location
private import codeql.mad.modelgenerator.internal.ModelGeneratorImpl
module ModelGeneratorInput implements ModelGeneratorInputSig<Location, CsharpDataFlow> {
private predicate irrelevantAccessor(CS::Accessor a) {
a.getDeclaration().(CS::Property).isReadWrite()
}
private predicate isUninterestingForModels(Callable api) {
api.getDeclaringType().getNamespace().getFullName() = ""
or
api instanceof CS::ConversionOperator
or
api instanceof Util::MainMethod
or
api instanceof CS::Destructor
or
api instanceof CS::AnonymousFunctionExpr
or
api.(CS::Constructor).isParameterless()
or
exists(Type decl | decl = api.getDeclaringType() |
decl instanceof SystemObjectClass or
decl instanceof SystemValueTypeClass
)
or
// Disregard properties that have both a get and a set accessor,
// which implicitly means auto implemented properties.
irrelevantAccessor(api)
}
private predicate relevant(Callable api) {
[api.(CS::Modifiable), api.(CS::Accessor).getDeclaration()].isEffectivelyPublic() and
api.fromSource() and
api.isUnboundDeclaration() and
not isUninterestingForModels(api)
}
module ModelGeneratorCommonInput implements ModelGeneratorCommonInputSig<Location, CsharpDataFlow> {
class Type = CS::Type;
class Parameter = CS::Parameter;
@@ -24,127 +58,8 @@ module ModelGeneratorInput implements ModelGeneratorInputSig<Location, CsharpDat
class NodeExtended = CS::DataFlow::Node;
Callable getAsExprEnclosingCallable(NodeExtended node) {
result = node.asExpr().getEnclosingCallable()
}
Callable getEnclosingCallable(NodeExtended node) { result = node.getEnclosingCallable() }
Parameter asParameter(NodeExtended node) { result = node.asParameter() }
/**
* Holds if any of the parameters of `api` are `System.Func<>`.
*/
private predicate isHigherOrder(Callable api) {
exists(Type t | t = api.getAParameter().getType().getUnboundDeclaration() |
t instanceof SystemLinqExpressions::DelegateExtType
)
}
private predicate irrelevantAccessor(CS::Accessor a) {
a.getDeclaration().(CS::Property).isReadWrite()
}
private predicate isUninterestingForModels(Callable api) {
api.getDeclaringType().getNamespace().getFullName() = ""
or
api instanceof CS::ConversionOperator
or
api instanceof Util::MainMethod
or
api instanceof CS::Destructor
or
api instanceof CS::AnonymousFunctionExpr
or
api.(CS::Constructor).isParameterless()
or
exists(Type decl | decl = api.getDeclaringType() |
decl instanceof SystemObjectClass or
decl instanceof SystemValueTypeClass
)
or
// Disregard properties that have both a get and a set accessor,
// which implicitly means auto implemented properties.
irrelevantAccessor(api)
}
private predicate relevant(Callable api) {
[api.(CS::Modifiable), api.(CS::Accessor).getDeclaration()].isEffectivelyPublic() and
api.fromSource() and
api.isUnboundDeclaration() and
not isUninterestingForModels(api)
}
private Callable getARelevantOverrideeOrImplementee(Overridable m) {
m.overridesOrImplements(result) and relevant(result)
}
/**
* Gets the super implementation of `api` if it is relevant.
* If such a super implementation does not exist, returns `api` if it is relevant.
*/
private Callable liftedImpl(Callable api) {
(
result = getARelevantOverrideeOrImplementee(api)
or
result = api and relevant(api)
) and
not exists(getARelevantOverrideeOrImplementee(result))
}
private predicate hasManualSummaryModel(Callable api) {
api = any(FlowSummaryImpl::Public::SummarizedCallable sc | sc.applyManualModel()) or
api = any(FlowSummaryImpl::Public::NeutralSummaryCallable sc | sc.hasManualModel())
}
private predicate hasManualSourceModel(Callable api) {
api = any(ExternalFlow::SourceCallable sc | sc.hasManualModel()) or
api = any(FlowSummaryImpl::Public::NeutralSourceCallable sc | sc.hasManualModel())
}
private predicate hasManualSinkModel(Callable api) {
api = any(ExternalFlow::SinkCallable sc | sc.hasManualModel()) or
api = any(FlowSummaryImpl::Public::NeutralSinkCallable sc | sc.hasManualModel())
}
predicate isUninterestingForDataFlowModels(Callable api) { none() }
predicate isUninterestingForHeuristicDataFlowModels(Callable api) { isHigherOrder(api) }
class SourceOrSinkTargetApi extends Callable {
SourceOrSinkTargetApi() { relevant(this) }
}
class SinkTargetApi extends SourceOrSinkTargetApi {
SinkTargetApi() { not hasManualSinkModel(this) }
}
class SourceTargetApi extends SourceOrSinkTargetApi {
SourceTargetApi() {
not hasManualSourceModel(this) and
// Do not generate source models for overridable callables
// as virtual dispatch implies that too many methods
// will be considered sources.
not this.(Overridable).overridesOrImplements(_)
}
}
class SummaryTargetApi extends Callable {
private Callable lift;
SummaryTargetApi() {
lift = liftedImpl(this) and
not hasManualSummaryModel(lift)
}
Callable lift() { result = lift }
predicate isRelevant() {
relevant(this) and
not hasManualSummaryModel(this)
}
}
/**
* Holds if `t` is a type that is generally used for bulk data in collection types.
* Eg. char[] is roughly equivalent to string and thus a highly
@@ -205,6 +120,8 @@ module ModelGeneratorInput implements ModelGeneratorInputSig<Location, CsharpDat
)
}
class InstanceParameterNode = DataFlowPrivate::InstanceParameterNode;
string qualifierString() { result = "Argument[this]" }
string parameterAccess(CS::Parameter p) {
@@ -215,8 +132,6 @@ module ModelGeneratorInput implements ModelGeneratorInputSig<Location, CsharpDat
string parameterContentAccess(CS::Parameter p) { result = "Argument[" + p.getPosition() + "]" }
class InstanceParameterNode = DataFlowPrivate::InstanceParameterNode;
private signature string parameterAccessSig(Parameter p);
private module ParamReturnNodeAsOutput<parameterAccessSig/1 getParamAccess> {
@@ -251,63 +166,92 @@ module ModelGeneratorInput implements ModelGeneratorInputSig<Location, CsharpDat
node.asExpr() instanceof CS::ThisAccess
}
private predicate isRelevantMemberAccess(DataFlow::Node node) {
exists(CS::MemberAccess access | access = node.asExpr() |
access.hasThisQualifier() and
access.getTarget().isEffectivelyPublic() and
(
access instanceof CS::FieldAccess
or
access.getTarget().(CS::Property).getSetter().isPublic()
)
)
}
predicate sinkModelSanitizer(DataFlow::Node node) { none() }
predicate apiSource(DataFlow::Node source) {
isRelevantMemberAccess(source) or source instanceof DataFlow::ParameterNode
}
private predicate uniquelyCalls(DataFlowCallable dc1, DataFlowCallable dc2) {
exists(DataFlowCall call |
dc1 = call.getEnclosingCallable() and
dc2 = unique(DataFlowCallable dc0 | dc0 = viableCallable(call) | dc0)
)
}
bindingset[dc1, dc2]
private predicate uniquelyCallsPlus(DataFlowCallable dc1, DataFlowCallable dc2) =
fastTC(uniquelyCalls/2)(dc1, dc2)
bindingset[sourceEnclosing, api]
predicate irrelevantSourceSinkApi(Callable sourceEnclosing, SourceTargetApi api) {
not exists(DataFlowCallable dc1, DataFlowCallable dc2 |
uniquelyCallsPlus(dc1, dc2) or dc1 = dc2
|
dc1.getUnderlyingCallable() = api and
dc2.getUnderlyingCallable() = sourceEnclosing
)
}
string getInputArgument(DataFlow::Node source) {
exists(int pos |
pos = source.(DataFlow::ParameterNode).getParameter().getPosition() and
result = "Argument[" + pos + "]"
)
or
source.asExpr() instanceof DataFlowPrivate::FieldOrPropertyAccess and
result = qualifierString()
}
bindingset[kind]
predicate isRelevantSinkKind(string kind) { any() }
bindingset[kind]
predicate isRelevantSourceKind(string kind) { any() }
predicate containerContent(DataFlow::ContentSet c) { c.isElement() }
string partialModelRow(Callable api, int i) {
i = 0 and ExternalFlow::partialModel(api, result, _, _, _, _) // package
or
i = 1 and ExternalFlow::partialModel(api, _, result, _, _, _) // type
or
i = 2 and ExternalFlow::partialModel(api, _, _, result, _, _) // extensible
or
i = 3 and ExternalFlow::partialModel(api, _, _, _, result, _) // name
or
i = 4 and ExternalFlow::partialModel(api, _, _, _, _, result) // parameters
or
i = 5 and result = "" and exists(api) // ext
}
string partialNeutralModelRow(Callable api, int i) {
i = 0 and result = partialModelRow(api, 0) // package
or
i = 1 and result = partialModelRow(api, 1) // type
or
i = 2 and result = partialModelRow(api, 3) // name
or
i = 3 and result = partialModelRow(api, 4) // parameters
}
}
private import ModelGeneratorCommonInput
private import MakeModelGeneratorFactory<Location, CsharpDataFlow, CsharpTaintTracking, ModelGeneratorCommonInput>
module SummaryModelGeneratorInput implements SummaryModelGeneratorInputSig {
Callable getAsExprEnclosingCallable(NodeExtended node) {
result = node.asExpr().getEnclosingCallable()
}
Parameter asParameter(NodeExtended node) { result = node.asParameter() }
/**
* Holds if any of the parameters of `api` are `System.Func<>`.
*/
private predicate isHigherOrder(Callable api) {
exists(Type t | t = api.getAParameter().getType().getUnboundDeclaration() |
t instanceof SystemLinqExpressions::DelegateExtType
)
}
private Callable getARelevantOverrideeOrImplementee(Overridable m) {
m.overridesOrImplements(result) and relevant(result)
}
/**
* Gets the super implementation of `api` if it is relevant.
* If such a super implementation does not exist, returns `api` if it is relevant.
*/
private Callable liftedImpl(Callable api) {
(
result = getARelevantOverrideeOrImplementee(api)
or
result = api and relevant(api)
) and
not exists(getARelevantOverrideeOrImplementee(result))
}
private predicate hasManualSummaryModel(Callable api) {
api = any(FlowSummaryImpl::Public::SummarizedCallable sc | sc.applyManualModel()) or
api = any(FlowSummaryImpl::Public::NeutralSummaryCallable sc | sc.hasManualModel())
}
predicate isUninterestingForHeuristicDataFlowModels(Callable api) { isHigherOrder(api) }
class SummaryTargetApi extends Callable {
private Callable lift;
SummaryTargetApi() {
lift = liftedImpl(this) and
not hasManualSummaryModel(lift)
}
Callable lift() { result = lift }
predicate isRelevant() {
relevant(this) and
not hasManualSummaryModel(this)
}
}
predicate isAdditionalContentFlowStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
TaintTrackingPrivate::defaultAdditionalTaintStep(nodeFrom, nodeTo, _) and
not nodeTo.asExpr() instanceof CS::ElementAccess and
@@ -370,34 +314,88 @@ module ModelGeneratorInput implements ModelGeneratorInputSig<Location, CsharpDat
or
c.isDelegateCallReturn() and result = "ReturnValue"
}
}
string partialModelRow(Callable api, int i) {
i = 0 and ExternalFlow::partialModel(api, result, _, _, _, _) // package
or
i = 1 and ExternalFlow::partialModel(api, _, result, _, _, _) // type
or
i = 2 and ExternalFlow::partialModel(api, _, _, result, _, _) // extensible
or
i = 3 and ExternalFlow::partialModel(api, _, _, _, result, _) // name
or
i = 4 and ExternalFlow::partialModel(api, _, _, _, _, result) // parameters
or
i = 5 and result = "" and exists(api) // ext
private module SourceModelGeneratorInput implements SourceModelGeneratorInputSig {
private predicate hasManualSourceModel(Callable api) {
api = any(ExternalFlow::SourceCallable sc | sc.hasManualModel()) or
api = any(FlowSummaryImpl::Public::NeutralSourceCallable sc | sc.hasManualModel())
}
string partialNeutralModelRow(Callable api, int i) {
i = 0 and result = partialModelRow(api, 0) // package
or
i = 1 and result = partialModelRow(api, 1) // type
or
i = 2 and result = partialModelRow(api, 3) // name
or
i = 3 and result = partialModelRow(api, 4) // parameters
class SourceTargetApi extends Callable {
SourceTargetApi() {
relevant(this) and
not hasManualSourceModel(this) and
// Do not generate source models for overridable callables
// as virtual dispatch implies that too many methods
// will be considered sources.
not this.(Overridable).overridesOrImplements(_)
}
}
private predicate uniquelyCalls(DataFlowCallable dc1, DataFlowCallable dc2) {
exists(DataFlowCall call |
dc1 = call.getEnclosingCallable() and
dc2 = unique(DataFlowCallable dc0 | dc0 = viableCallable(call) | dc0)
)
}
bindingset[dc1, dc2]
private predicate uniquelyCallsPlus(DataFlowCallable dc1, DataFlowCallable dc2) =
fastTC(uniquelyCalls/2)(dc1, dc2)
bindingset[sourceEnclosing, api]
predicate irrelevantSourceSinkApi(Callable sourceEnclosing, SourceTargetApi api) {
not exists(DataFlowCallable dc1, DataFlowCallable dc2 |
uniquelyCallsPlus(dc1, dc2) or dc1 = dc2
|
dc1.getUnderlyingCallable() = api and
dc2.getUnderlyingCallable() = sourceEnclosing
)
}
predicate sourceNode = ExternalFlow::sourceNode/2;
}
private module SinkModelGeneratorInput implements SinkModelGeneratorInputSig {
private predicate hasManualSinkModel(Callable api) {
api = any(ExternalFlow::SinkCallable sc | sc.hasManualModel()) or
api = any(FlowSummaryImpl::Public::NeutralSinkCallable sc | sc.hasManualModel())
}
class SinkTargetApi extends Callable {
SinkTargetApi() { relevant(this) and not hasManualSinkModel(this) }
}
private predicate isRelevantMemberAccess(DataFlow::Node node) {
exists(CS::MemberAccess access | access = node.asExpr() |
access.hasThisQualifier() and
access.getTarget().isEffectivelyPublic() and
(
access instanceof CS::FieldAccess
or
access.getTarget().(CS::Property).getSetter().isPublic()
)
)
}
predicate apiSource(DataFlow::Node source) {
isRelevantMemberAccess(source) or source instanceof DataFlow::ParameterNode
}
string getInputArgument(DataFlow::Node source) {
exists(int pos |
pos = source.(DataFlow::ParameterNode).getParameter().getPosition() and
result = "Argument[" + pos + "]"
)
or
source.asExpr() instanceof DataFlowPrivate::FieldOrPropertyAccess and
result = qualifierString()
}
predicate sinkNode = ExternalFlow::sinkNode/2;
}
import MakeModelGenerator<Location, CsharpDataFlow, CsharpTaintTracking, ModelGeneratorInput>
import MakeSummaryModelGenerator<SummaryModelGeneratorInput> as SummaryModels
import MakeSourceModelGenerator<SourceModelGeneratorInput> as SourceModels
import MakeSinkModelGenerator<SinkModelGeneratorInput> as SinkModels

View File

@@ -1,6 +1,6 @@
private import csharp as CS
private import codeql.mad.modelgenerator.internal.ModelPrinting
private import CaptureModels::ModelGeneratorInput as ModelGeneratorInput
private import CaptureModels::ModelGeneratorCommonInput as ModelGeneratorInput
private module ModelPrintingLang implements ModelPrintingLangSig {
class Callable = CS::Callable;

View File

@@ -2,7 +2,8 @@ private import csharp
private import semmle.code.csharp.frameworks.system.collections.Generic as GenericCollections
private import semmle.code.csharp.dataflow.internal.DataFlowPrivate
private import semmle.code.csharp.frameworks.system.linq.Expressions
private import CaptureModels::ModelGeneratorInput as ModelGeneratorInput
private import CaptureModels::ModelGeneratorCommonInput as ModelGeneratorInput
private import CaptureModels::SummaryModelGeneratorInput as SummaryModelGeneratorInput
private import CaptureModelsPrinting
/**
@@ -177,21 +178,19 @@ private predicate output(Callable callable, TypeParameter tp, string output) {
delegateSink(callable, tp, output)
}
private module ModelPrintingInput implements ModelPrintingSig {
private module ModelPrintingInput implements ModelPrintingSummarySig {
class SummaryApi = TypeBasedFlowTargetApi;
class SourceOrSinkApi = TypeBasedFlowTargetApi;
string getProvenance() { result = "tb-generated" }
}
private module Printing = ModelPrinting<ModelPrintingInput>;
private module Printing = ModelPrintingSummary<ModelPrintingInput>;
/**
* A class of callables that are relevant generating summaries for based
* on the Theorems for Free approach.
*/
class TypeBasedFlowTargetApi extends ModelGeneratorInput::SummaryTargetApi {
class TypeBasedFlowTargetApi extends SummaryModelGeneratorInput::SummaryTargetApi {
/**
* Gets the string representation of all type based summaries for `this`
* inspired by the Theorems for Free approach.

View File

@@ -1,5 +1,6 @@
import csharp
import utils.modelgenerator.internal.CaptureModels
import SummaryModels
import utils.test.InlineMadTest
module InlineMadTestConfig implements InlineMadTestConfigSig {

View File

@@ -1,5 +1,6 @@
import csharp
import utils.modelgenerator.internal.CaptureModels
import SummaryModels
import utils.test.InlineMadTest
module InlineMadTestConfig implements InlineMadTestConfigSig {

View File

@@ -1,5 +1,6 @@
import csharp
import utils.modelgenerator.internal.CaptureModels
import SummaryModels
import utils.test.InlineMadTest
module InlineMadTestConfig implements InlineMadTestConfigSig {

View File

@@ -1,5 +1,6 @@
import csharp
import utils.modelgenerator.internal.CaptureModels
import SinkModels
import utils.test.InlineMadTest
module InlineMadTestConfig implements InlineMadTestConfigSig {

View File

@@ -1,5 +1,6 @@
import csharp
import utils.modelgenerator.internal.CaptureModels
import SourceModels
import utils.test.InlineMadTest
module InlineMadTestConfig implements InlineMadTestConfigSig {

View File

@@ -7,6 +7,7 @@
*/
import internal.CaptureModels
import SummaryModels
from DataFlowSummaryTargetApi api, string flow
where flow = ContentSensitive::captureFlow(api, _)

View File

@@ -7,6 +7,7 @@
*/
import internal.CaptureModels
import SummaryModels
from DataFlowSummaryTargetApi api, string noflow
where noflow = captureNeutral(api)

View File

@@ -7,6 +7,7 @@
*/
import internal.CaptureModels
import SinkModels
from DataFlowSinkTargetApi api, string sink
where sink = Heuristic::captureSink(api)

View File

@@ -7,6 +7,7 @@
*/
import internal.CaptureModels
import SourceModels
from DataFlowSourceTargetApi api, string source
where source = Heuristic::captureSource(api)

View File

@@ -7,6 +7,7 @@
*/
import internal.CaptureModels
import SummaryModels
from DataFlowSummaryTargetApi api, string flow
where flow = captureFlow(api, _)

View File

@@ -11,6 +11,7 @@
import java
import semmle.code.java.dataflow.DataFlow
import utils.modelgenerator.internal.CaptureModels
import SummaryModels
import PartialFlow::PartialPathGraph
int explorationLimit() { result = 3 }

View File

@@ -11,6 +11,7 @@
import java
import semmle.code.java.dataflow.DataFlow
import utils.modelgenerator.internal.CaptureModels
import SummaryModels
import Heuristic
import PropagateFlow::PathGraph

View File

@@ -25,7 +25,20 @@ predicate isPrimitiveTypeUsedForBulkData(J::Type t) {
t.hasName(["byte", "char", "Byte", "Character"])
}
module ModelGeneratorInput implements ModelGeneratorInputSig<Location, JavaDataFlow> {
private predicate isInfrequentlyUsed(J::CompilationUnit cu) {
cu.getPackage().getName().matches("javax.swing%") or
cu.getPackage().getName().matches("java.awt%")
}
private predicate relevant(Callable api) {
api.isPublic() and
api.getDeclaringType().isPublic() and
api.fromSource() and
not isUninterestingForModels(api) and
not isInfrequentlyUsed(api.getCompilationUnit())
}
module ModelGeneratorCommonInput implements ModelGeneratorCommonInputSig<Location, JavaDataFlow> {
class Type = J::Type;
class Parameter = J::Parameter;
@@ -34,96 +47,8 @@ module ModelGeneratorInput implements ModelGeneratorInputSig<Location, JavaDataF
class NodeExtended = DataFlow::Node;
Callable getAsExprEnclosingCallable(NodeExtended node) {
result = node.asExpr().getEnclosingCallable()
}
Callable getEnclosingCallable(NodeExtended node) { result = node.getEnclosingCallable() }
Parameter asParameter(NodeExtended node) { result = node.asParameter() }
private predicate isInfrequentlyUsed(J::CompilationUnit cu) {
cu.getPackage().getName().matches("javax.swing%") or
cu.getPackage().getName().matches("java.awt%")
}
private predicate relevant(Callable api) {
api.isPublic() and
api.getDeclaringType().isPublic() and
api.fromSource() and
not isUninterestingForModels(api) and
not isInfrequentlyUsed(api.getCompilationUnit())
}
private J::Method getARelevantOverride(J::Method m) {
result = m.getAnOverride() and
relevant(result) and
// Other exclusions for overrides.
not m instanceof J::ToStringMethod
}
/**
* Gets the super implementation of `m` if it is relevant.
* If such a super implementations does not exist, returns `m` if it is relevant.
*/
private J::Callable liftedImpl(J::Callable m) {
(
result = getARelevantOverride(m)
or
result = m and relevant(m)
) and
not exists(getARelevantOverride(result))
}
private predicate hasManualSummaryModel(Callable api) {
api = any(FlowSummaryImpl::Public::SummarizedCallable sc | sc.applyManualModel()).asCallable() or
api = any(FlowSummaryImpl::Public::NeutralSummaryCallable sc | sc.hasManualModel()).asCallable()
}
private predicate hasManualSourceModel(Callable api) {
api = any(ExternalFlow::SourceCallable sc | sc.hasManualModel()) or
api = any(FlowSummaryImpl::Public::NeutralSourceCallable sc | sc.hasManualModel()).asCallable()
}
private predicate hasManualSinkModel(Callable api) {
api = any(ExternalFlow::SinkCallable sc | sc.hasManualModel()) or
api = any(FlowSummaryImpl::Public::NeutralSinkCallable sc | sc.hasManualModel()).asCallable()
}
predicate isUninterestingForDataFlowModels(Callable api) {
api.getDeclaringType() instanceof J::Interface and not exists(api.getBody())
}
predicate isUninterestingForHeuristicDataFlowModels(Callable api) { none() }
class SourceOrSinkTargetApi extends Callable {
SourceOrSinkTargetApi() { relevant(this) }
}
class SinkTargetApi extends SourceOrSinkTargetApi {
SinkTargetApi() { not hasManualSinkModel(this) }
}
class SourceTargetApi extends SourceOrSinkTargetApi {
SourceTargetApi() { not hasManualSourceModel(this) }
}
class SummaryTargetApi extends Callable {
private Callable lift;
SummaryTargetApi() {
lift = liftedImpl(this) and
not hasManualSummaryModel(lift)
}
Callable lift() { result = lift }
predicate isRelevant() {
relevant(this) and
not hasManualSummaryModel(this)
}
}
private string isExtensible(Callable c) {
if c.getDeclaringType().isFinal() then result = "false" else result = "true"
}
@@ -204,50 +129,88 @@ module ModelGeneratorInput implements ModelGeneratorInputSig<Location, JavaDataF
node.asExpr().(J::ThisAccess).isOwnInstanceAccess()
}
predicate sinkModelSanitizer(DataFlow::Node node) {
// exclude variable capture jump steps
exists(Ssa::SsaImplicitInit closure |
closure.captures(_) and
node.asExpr() = closure.getAFirstUse()
)
}
predicate apiSource(DataFlow::Node source) {
(
source.asExpr().(J::FieldAccess).isOwnFieldAccess() or
source instanceof DataFlow::ParameterNode
) and
exists(J::RefType t |
t = source.getEnclosingCallable().getDeclaringType().getAnAncestor() and
not t instanceof J::TypeObject and
t.isPublic()
)
}
predicate irrelevantSourceSinkApi(Callable source, SourceTargetApi api) { none() }
string getInputArgument(DataFlow::Node source) {
exists(int pos |
source.(DataFlow::ParameterNode).isParameterOf(_, pos) and
if pos >= 0 then result = "Argument[" + pos + "]" else result = qualifierString()
)
or
source.asExpr() instanceof J::FieldAccess and
result = qualifierString()
}
bindingset[kind]
predicate isRelevantSinkKind(string kind) {
not kind = "log-injection" and
not kind.matches("regex-use%") and
not kind = "file-content-store"
}
bindingset[kind]
predicate isRelevantSourceKind(string kind) { any() }
predicate containerContent = DataFlowPrivate::containerContent/1;
string partialModelRow(Callable api, int i) {
i = 0 and qualifiedName(api, result, _) // package
or
i = 1 and qualifiedName(api, _, result) // type
or
i = 2 and result = isExtensible(api) // extensible
or
i = 3 and result = api.getName() // name
or
i = 4 and result = ExternalFlow::paramsString(api) // parameters
or
i = 5 and result = "" and exists(api) // ext
}
string partialNeutralModelRow(Callable api, int i) {
i = 0 and qualifiedName(api, result, _) // package
or
i = 1 and qualifiedName(api, _, result) // type
or
i = 2 and result = api.getName() // name
or
i = 3 and result = ExternalFlow::paramsString(api) // parameters
}
}
private import ModelGeneratorCommonInput
private import MakeModelGeneratorFactory<Location, JavaDataFlow, JavaTaintTracking, ModelGeneratorCommonInput>
module SummaryModelGeneratorInput implements SummaryModelGeneratorInputSig {
Callable getAsExprEnclosingCallable(NodeExtended node) {
result = node.asExpr().getEnclosingCallable()
}
Parameter asParameter(NodeExtended node) { result = node.asParameter() }
private J::Method getARelevantOverride(J::Method m) {
result = m.getAnOverride() and
relevant(result) and
// Other exclusions for overrides.
not m instanceof J::ToStringMethod
}
/**
* Gets the super implementation of `m` if it is relevant.
* If such a super implementations does not exist, returns `m` if it is relevant.
*/
private J::Callable liftedImpl(J::Callable m) {
(
result = getARelevantOverride(m)
or
result = m and relevant(m)
) and
not exists(getARelevantOverride(result))
}
private predicate hasManualSummaryModel(Callable api) {
api = any(FlowSummaryImpl::Public::SummarizedCallable sc | sc.applyManualModel()).asCallable() or
api = any(FlowSummaryImpl::Public::NeutralSummaryCallable sc | sc.hasManualModel()).asCallable()
}
class SummaryTargetApi extends Callable {
private Callable lift;
SummaryTargetApi() {
lift = liftedImpl(this) and
not hasManualSummaryModel(lift)
}
Callable lift() { result = lift }
predicate isRelevant() {
relevant(this) and
not hasManualSummaryModel(this)
}
}
predicate isUninterestingForDataFlowModels(Callable api) {
api.getDeclaringType() instanceof J::Interface and not exists(api.getBody())
}
predicate isAdditionalContentFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
TaintTracking::defaultAdditionalTaintStep(node1, node2, _) and
not exists(DataFlow::Content f |
@@ -287,34 +250,71 @@ module ModelGeneratorInput implements ModelGeneratorInputSig<Location, JavaDataF
or
c instanceof DataFlowUtil::MapKeyContent and result = "MapKey"
}
}
string partialModelRow(Callable api, int i) {
i = 0 and qualifiedName(api, result, _) // package
or
i = 1 and qualifiedName(api, _, result) // type
or
i = 2 and result = isExtensible(api) // extensible
or
i = 3 and result = api.getName() // name
or
i = 4 and result = ExternalFlow::paramsString(api) // parameters
or
i = 5 and result = "" and exists(api) // ext
private module SourceModelGeneratorInput implements SourceModelGeneratorInputSig {
private predicate hasManualSourceModel(Callable api) {
api = any(ExternalFlow::SourceCallable sc | sc.hasManualModel()) or
api = any(FlowSummaryImpl::Public::NeutralSourceCallable sc | sc.hasManualModel()).asCallable()
}
string partialNeutralModelRow(Callable api, int i) {
i = 0 and qualifiedName(api, result, _) // package
or
i = 1 and qualifiedName(api, _, result) // type
or
i = 2 and result = api.getName() // name
or
i = 3 and result = ExternalFlow::paramsString(api) // parameters
class SourceTargetApi extends Callable {
SourceTargetApi() { relevant(this) and not hasManualSourceModel(this) }
}
predicate sourceNode = ExternalFlow::sourceNode/2;
}
private module SinkModelGeneratorInput implements SinkModelGeneratorInputSig {
private predicate hasManualSinkModel(Callable api) {
api = any(ExternalFlow::SinkCallable sc | sc.hasManualModel()) or
api = any(FlowSummaryImpl::Public::NeutralSinkCallable sc | sc.hasManualModel()).asCallable()
}
class SinkTargetApi extends Callable {
SinkTargetApi() { relevant(this) and not hasManualSinkModel(this) }
}
predicate sinkModelSanitizer(DataFlow::Node node) {
// exclude variable capture jump steps
exists(Ssa::SsaImplicitInit closure |
closure.captures(_) and
node.asExpr() = closure.getAFirstUse()
)
}
predicate apiSource(DataFlow::Node source) {
(
source.asExpr().(J::FieldAccess).isOwnFieldAccess() or
source instanceof DataFlow::ParameterNode
) and
exists(J::RefType t |
t = source.getEnclosingCallable().getDeclaringType().getAnAncestor() and
not t instanceof J::TypeObject and
t.isPublic()
)
}
string getInputArgument(DataFlow::Node source) {
exists(int pos |
source.(DataFlow::ParameterNode).isParameterOf(_, pos) and
if pos >= 0 then result = "Argument[" + pos + "]" else result = qualifierString()
)
or
source.asExpr() instanceof J::FieldAccess and
result = qualifierString()
}
bindingset[kind]
predicate isRelevantSinkKind(string kind) {
not kind = "log-injection" and
not kind.matches("regex-use%") and
not kind = "file-content-store"
}
predicate sinkNode = ExternalFlow::sinkNode/2;
}
import MakeModelGenerator<Location, JavaDataFlow, JavaTaintTracking, ModelGeneratorInput>
import MakeSummaryModelGenerator<SummaryModelGeneratorInput> as SummaryModels
import MakeSourceModelGenerator<SourceModelGeneratorInput> as SourceModels
import MakeSinkModelGenerator<SinkModelGeneratorInput> as SinkModels

View File

@@ -1,6 +1,6 @@
private import java as J
private import codeql.mad.modelgenerator.internal.ModelPrinting
private import CaptureModels::ModelGeneratorInput as ModelGeneratorInput
private import CaptureModels::ModelGeneratorCommonInput as ModelGeneratorInput
private module ModelPrintingLang implements ModelPrintingLangSig {
class Callable = J::Callable;

View File

@@ -2,7 +2,8 @@ private import java
private import semmle.code.java.Collections
private import semmle.code.java.dataflow.internal.ContainerFlow
private import CaptureModels as CaptureModels
private import CaptureModels::ModelGeneratorInput as ModelGeneratorInput
private import CaptureModels::ModelGeneratorCommonInput as ModelGeneratorInput
private import CaptureModels::SummaryModelGeneratorInput as SummaryModelGeneratorInput
private import CaptureModelsPrinting
/**
@@ -284,21 +285,19 @@ private predicate output(Callable callable, TypeVariable tv, string output) {
functionalSink(callable, tv, output)
}
module ModelPrintingInput implements ModelPrintingSig {
module ModelPrintingInput implements ModelPrintingSummarySig {
class SummaryApi = TypeBasedFlowTargetApi;
class SourceOrSinkApi = ModelGeneratorInput::SourceOrSinkTargetApi;
string getProvenance() { result = "tb-generated" }
}
private module Printing = ModelPrinting<ModelPrintingInput>;
private module Printing = ModelPrintingSummary<ModelPrintingInput>;
/**
* A class of callables that are relevant generating summaries for based
* on the Theorems for Free approach.
*/
class TypeBasedFlowTargetApi extends ModelGeneratorInput::SummaryTargetApi {
class TypeBasedFlowTargetApi extends SummaryModelGeneratorInput::SummaryTargetApi {
/**
* Gets the string representation of all type based summaries for `this`
* inspired by the Theorems for Free approach.

View File

@@ -1,5 +1,6 @@
import java
import utils.modelgenerator.internal.CaptureModels
import SummaryModels
import utils.test.InlineMadTest
module InlineMadTestConfig implements InlineMadTestConfigSig {

View File

@@ -1,5 +1,6 @@
import java
import utils.modelgenerator.internal.CaptureModels
import SummaryModels
import utils.test.InlineMadTest
module InlineMadTestConfig implements InlineMadTestConfigSig {

View File

@@ -1,5 +1,6 @@
import java
import utils.modelgenerator.internal.CaptureModels
import SummaryModels
import utils.test.InlineMadTest
module InlineMadTestConfig implements InlineMadTestConfigSig {

View File

@@ -1,5 +1,6 @@
import java
import utils.modelgenerator.internal.CaptureModels
import SinkModels
import utils.test.InlineMadTest
module InlineMadTestConfig implements InlineMadTestConfigSig {

View File

@@ -1,5 +1,6 @@
import java
import utils.modelgenerator.internal.CaptureModels
import SourceModels
import utils.test.InlineMadTest
module InlineMadTestConfig implements InlineMadTestConfigSig {

View File

@@ -7,6 +7,7 @@
*/
import internal.CaptureModels
import SummaryModels
from DataFlowSummaryTargetApi api, string flow
where flow = ContentSensitive::captureFlow(api, _)

View File

@@ -7,6 +7,7 @@
*/
import internal.CaptureModels
import SummaryModels
from DataFlowSummaryTargetApi api, string noflow
where noflow = Heuristic::captureNoFlow(api)

View File

@@ -7,6 +7,7 @@
*/
import internal.CaptureModels
import SinkModels
from DataFlowSinkTargetApi api, string sink
where sink = Heuristic::captureSink(api)

View File

@@ -7,6 +7,7 @@
*/
import internal.CaptureModels
import SourceModels
from DataFlowSourceTargetApi api, string source
where source = Heuristic::captureSource(api)

View File

@@ -7,6 +7,7 @@
*/
import internal.CaptureModels
import SummaryModels
from DataFlowSummaryTargetApi api, string flow
where flow = captureFlow(api, _)

View File

@@ -10,6 +10,7 @@
private import codeql.rust.dataflow.DataFlow
import utils.modelgenerator.internal.CaptureModels
import SummaryModels
import PartialFlow::PartialPathGraph
int explorationLimit() { result = 3 }

View File

@@ -10,6 +10,7 @@
private import codeql.rust.dataflow.DataFlow
import utils.modelgenerator.internal.CaptureModels
import SummaryModels
import Heuristic
import PropagateFlow::PathGraph

View File

@@ -2,7 +2,7 @@ private import codeql.util.Unit
private import rust
private import rust as R
private import codeql.rust.dataflow.DataFlow
private import codeql.rust.dataflow.internal.DataFlowImpl
private import codeql.rust.dataflow.internal.DataFlowImpl as DataFlowImpl
private import codeql.rust.dataflow.internal.Node as Node
private import codeql.rust.dataflow.internal.Content
private import codeql.rust.dataflow.FlowSource as FlowSource
@@ -11,7 +11,25 @@ private import codeql.rust.dataflow.internal.TaintTrackingImpl
private import codeql.mad.modelgenerator.internal.ModelGeneratorImpl
private import codeql.rust.dataflow.internal.FlowSummaryImpl as FlowSummary
module ModelGeneratorInput implements ModelGeneratorInputSig<Location, RustDataFlow> {
private predicate relevant(Function api) {
// Only include functions that have a resolved path.
api.hasCrateOrigin() and
api.hasExtendedCanonicalPath() and
(
// This excludes closures (these are not exported API endpoints) and
// functions without a `pub` visiblity. A function can be `pub` without
// ultimately being exported by a crate, so this is an overapproximation.
api.hasVisibility()
or
// If a method implements a public trait it is exposed through the trait.
// We overapproximate this by including all trait method implementations.
exists(Impl impl | impl.hasTrait() and impl.getAssocItemList().getAssocItem(_) = api)
)
}
module ModelGeneratorCommonInput implements
ModelGeneratorCommonInputSig<Location, DataFlowImpl::RustDataFlow>
{
// NOTE: We are not using type information for now.
class Type = Unit;
@@ -23,42 +41,71 @@ module ModelGeneratorInput implements ModelGeneratorInputSig<Location, RustDataF
Type getType() { any() }
}
Callable getAsExprEnclosingCallable(NodeExtended node) { result = node.asExpr().getScope() }
Callable getEnclosingCallable(NodeExtended node) {
result = node.(Node::Node).getEnclosingCallable().asCfgScope()
}
Parameter asParameter(NodeExtended node) { result = node.asParameter() }
predicate isRelevantType(Type t) { any() }
private predicate relevant(Function api) {
// Only include functions that have a resolved path.
api.hasCrateOrigin() and
api.hasExtendedCanonicalPath() and
(
// This excludes closures (these are not exported API endpoints) and
// functions without a `pub` visiblity. A function can be `pub` without
// ultimately being exported by a crate, so this is an overapproximation.
api.hasVisibility()
or
// If a method implements a public trait it is exposed through the trait.
// We overapproximate this by including all trait method implementations.
exists(Impl impl | impl.hasTrait() and impl.getAssocItemList().getAssocItem(_) = api)
)
/**
* Gets the underlying type of the content `c`.
*/
Type getUnderlyingContentType(DataFlow::ContentSet c) { result = any(Type t) and exists(c) }
string qualifierString() { result = "Argument[self]" }
string parameterAccess(R::ParamBase p) {
result =
"Argument[" + any(DataFlowImpl::ParameterPosition pos | p = pos.getParameterIn(_)).toString() +
"]"
}
predicate isUninterestingForDataFlowModels(Callable api) { none() }
string parameterContentAccess(R::ParamBase p) { result = parameterAccess(p) }
predicate isUninterestingForHeuristicDataFlowModels(Callable api) { none() }
class SourceOrSinkTargetApi extends Callable {
SourceOrSinkTargetApi() { relevant(this) }
class InstanceParameterNode extends DataFlow::ParameterNode {
InstanceParameterNode() { this.asParameter() instanceof SelfParam }
}
class SinkTargetApi extends SourceOrSinkTargetApi { }
bindingset[c]
string paramReturnNodeAsOutput(Callable c, DataFlowImpl::ParameterPosition pos) {
result = paramReturnNodeAsContentOutput(c, pos)
}
class SourceTargetApi extends SourceOrSinkTargetApi { }
bindingset[c]
string paramReturnNodeAsContentOutput(Callable c, DataFlowImpl::ParameterPosition pos) {
result = parameterContentAccess(c.getParamList().getParam(pos.getPosition()))
or
pos.isSelf() and result = qualifierString()
}
Callable returnNodeEnclosingCallable(DataFlow::Node ret) {
result = ret.(Node::Node).getEnclosingCallable().asCfgScope()
}
predicate isOwnInstanceAccessNode(DataFlowImpl::RustDataFlow::ReturnNode node) {
// This is probably not relevant to implement for Rust, as we only use
// `captureMixedFlow` which doesn't explicitly distinguish between
// functions that return `self` and those that don't.
none()
}
predicate containerContent(DataFlow::ContentSet c) {
c.(SingletonContentSet).getContent() instanceof ElementContent
}
string partialModelRow(Callable api, int i) {
i = 0 and result = api.(Function).getCrateOrigin() // crate
or
i = 1 and result = api.(Function).getExtendedCanonicalPath() // name
}
string partialNeutralModelRow(Callable api, int i) { result = partialModelRow(api, i) }
}
private import ModelGeneratorCommonInput
private import MakeModelGeneratorFactory<Location, DataFlowImpl::RustDataFlow, RustTaintTracking, ModelGeneratorCommonInput>
private module SummaryModelGeneratorInput implements SummaryModelGeneratorInputSig {
class SummaryTargetApi extends Callable {
private Callable lift;
@@ -72,74 +119,9 @@ module ModelGeneratorInput implements ModelGeneratorInputSig<Location, RustDataF
predicate isRelevant() { relevant(this) }
}
predicate isRelevantType(Type t) { any() }
Callable getAsExprEnclosingCallable(NodeExtended node) { result = node.asExpr().getScope() }
/**
* Gets the underlying type of the content `c`.
*/
Type getUnderlyingContentType(DataFlow::ContentSet c) { result = any(Type t) and exists(c) }
string qualifierString() { result = "Argument[self]" }
string parameterAccess(R::ParamBase p) {
result = "Argument[" + any(ParameterPosition pos | p = pos.getParameterIn(_)).toString() + "]"
}
string parameterContentAccess(R::ParamBase p) { result = parameterAccess(p) }
class InstanceParameterNode extends DataFlow::ParameterNode {
InstanceParameterNode() { this.asParameter() instanceof SelfParam }
}
bindingset[c]
string paramReturnNodeAsOutput(Callable c, ParameterPosition pos) {
result = paramReturnNodeAsContentOutput(c, pos)
}
bindingset[c]
string paramReturnNodeAsContentOutput(Callable c, ParameterPosition pos) {
result = parameterContentAccess(c.getParamList().getParam(pos.getPosition()))
or
pos.isSelf() and result = qualifierString()
}
Callable returnNodeEnclosingCallable(DataFlow::Node ret) {
result = ret.(Node::Node).getEnclosingCallable().asCfgScope()
}
predicate isOwnInstanceAccessNode(RustDataFlow::ReturnNode node) {
// This is probably not relevant to implement for Rust, as we only use
// `captureMixedFlow` which doesn't explicitly distinguish between
// functions that return `self` and those that don't.
none()
}
predicate sinkModelSanitizer(DataFlow::Node node) { none() }
/**
* Holds if `source` is an API entrypoint, i.e., a source of input where data
* can flow in to a library. This is used for creating sink models, as we
* only want to mark functions as sinks if input to the function can reach
* (from an input source) a known sink.
*/
predicate apiSource(DataFlow::Node source) { source instanceof DataFlow::ParameterNode }
bindingset[sourceEnclosing, api]
predicate irrelevantSourceSinkApi(Callable sourceEnclosing, SourceTargetApi api) { none() }
string getInputArgument(DataFlow::Node source) {
result = "Argument[" + source.(Node::SourceParameterNode).getPosition().toString() + "]"
}
bindingset[kind]
predicate isRelevantSinkKind(string kind) { any() }
bindingset[kind]
predicate isRelevantSourceKind(string kind) { any() }
predicate containerContent(DataFlow::ContentSet c) {
c.(SingletonContentSet).getContent() instanceof ElementContent
}
Parameter asParameter(NodeExtended node) { result = node.asParameter() }
predicate isAdditionalContentFlowStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { none() }
@@ -159,7 +141,7 @@ module ModelGeneratorInput implements ModelGeneratorInputSig<Location, RustDataF
private string encodeContent(ContentSet cs, string arg) {
result = FlowSummary::Input::encodeContent(cs, arg)
or
exists(Content c | cs = TSingletonContentSet(c) |
exists(Content c | cs = DataFlowImpl::TSingletonContentSet(c) |
exists(int pos |
pos = c.(FunctionCallArgumentContent).getPosition() and
result = "Parameter" and
@@ -176,18 +158,36 @@ module ModelGeneratorInput implements ModelGeneratorInputSig<Location, RustDataF
if arg = "" then result = name else result = name + "[" + arg + "]"
)
}
}
string partialModelRow(Callable api, int i) {
i = 0 and result = api.(Function).getCrateOrigin() // crate
or
i = 1 and result = api.(Function).getExtendedCanonicalPath() // name
private module SourceModelGeneratorInput implements SourceModelGeneratorInputSig {
class SourceTargetApi extends Callable {
SourceTargetApi() { relevant(this) }
}
string partialNeutralModelRow(Callable api, int i) { result = partialModelRow(api, i) }
predicate sourceNode(DataFlow::Node node, string kind) { FlowSource::sourceNode(node, kind) }
}
private module SinkModelGeneratorInput implements SinkModelGeneratorInputSig {
class SinkTargetApi extends Callable {
SinkTargetApi() { relevant(this) }
}
/**
* Holds if `source` is an API entrypoint, i.e., a source of input where data
* can flow in to a library. This is used for creating sink models, as we
* only want to mark functions as sinks if input to the function can reach
* (from an input source) a known sink.
*/
predicate apiSource(DataFlow::Node source) { source instanceof DataFlow::ParameterNode }
string getInputArgument(DataFlow::Node source) {
result = "Argument[" + source.(Node::SourceParameterNode).getPosition().toString() + "]"
}
predicate sinkNode(DataFlow::Node node, string kind) { FlowSink::sinkNode(node, kind) }
}
import MakeModelGenerator<Location, RustDataFlow, RustTaintTracking, ModelGeneratorInput>
import MakeSummaryModelGenerator<SummaryModelGeneratorInput> as SummaryModels
import MakeSourceModelGenerator<SourceModelGeneratorInput> as SourceModels
import MakeSinkModelGenerator<SinkModelGeneratorInput> as SinkModels

View File

@@ -1,6 +1,6 @@
private import rust as R
private import codeql.mad.modelgenerator.internal.ModelPrinting
private import CaptureModels::ModelGeneratorInput as ModelGeneratorInput
private import CaptureModels::ModelGeneratorCommonInput as ModelGeneratorInput
private module ModelPrintingLang implements ModelPrintingLangSig {
class Callable = R::Callable;

View File

@@ -1,5 +1,6 @@
import rust
import utils.modelgenerator.internal.CaptureModels
import SinkModels
import utils.test.InlineMadTest
module InlineMadTestConfig implements InlineMadTestConfigSig {

View File

@@ -1,5 +1,6 @@
import rust
import utils.modelgenerator.internal.CaptureModels
import SourceModels
import utils.test.InlineMadTest
import codeql.rust.dataflow.internal.ModelsAsData

View File

@@ -1,5 +1,6 @@
import rust
import utils.modelgenerator.internal.CaptureModels
import SummaryModels
import utils.test.InlineMadTest
module InlineMadTestConfig implements InlineMadTestConfigSig {

View File

@@ -16,7 +16,7 @@ signature module ModelPrintingLangSig {
}
module ModelPrintingImpl<ModelPrintingLangSig Lang> {
signature module ModelPrintingSig {
signature module ModelPrintingSummarySig {
/**
* The class of APIs relevant for model generation.
*/
@@ -24,6 +24,16 @@ module ModelPrintingImpl<ModelPrintingLangSig Lang> {
Lang::Callable lift();
}
/**
* Gets the string representation of the provenance of the models.
*/
string getProvenance();
}
signature module ModelPrintingSourceOrSinkSig {
/**
* The class of APIs relevant for model generation.
*/
class SourceOrSinkApi extends Lang::Callable;
/**
@@ -32,14 +42,14 @@ module ModelPrintingImpl<ModelPrintingLangSig Lang> {
string getProvenance();
}
module ModelPrinting<ModelPrintingSig Printing> {
/**
* Computes the first columns for MaD rows used for summaries, sources and sinks.
*/
private string asPartialModel(Lang::Callable api) {
result = strictconcat(int i | | Lang::partialModelRow(api, i), ";" order by i) + ";"
}
/**
* Computes the first columns for MaD rows used for summaries, sources and sinks.
*/
private string asPartialModel(Lang::Callable api) {
result = strictconcat(int i | | Lang::partialModelRow(api, i), ";" order by i) + ";"
}
module ModelPrintingSummary<ModelPrintingSummarySig Printing> {
/**
* Computes the first columns for neutral MaD rows.
*/
@@ -106,7 +116,9 @@ module ModelPrintingImpl<ModelPrintingLangSig Lang> {
preservesValue = false and
result = asSummaryModel(api, input, output, "taint", lift)
}
}
module ModelPrintingSourceOrSink<ModelPrintingSourceOrSinkSig Printing> {
/**
* Gets the sink model for `api` with `input` and `kind`.
*/