Skip to content

Commit

Permalink
C#: Make classes for Source and Sink callables relevant for modelling…
Browse files Browse the repository at this point in the history
… and re-write the model generator to use these.
  • Loading branch information
michaelnebel committed Jun 11, 2024
1 parent dec34ed commit d98d377
Show file tree
Hide file tree
Showing 9 changed files with 64 additions and 61 deletions.
2 changes: 1 addition & 1 deletion csharp/ql/src/utils/modelgenerator/CaptureNeutralModels.ql
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@ import semmle.code.csharp.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
import internal.CaptureModels
import internal.CaptureSummaryFlowQuery

from DataFlowTargetApi api, string noflow
from DataFlowSummaryTargetApi api, string noflow
where noflow = captureNoFlow(api)
select noflow order by noflow
2 changes: 1 addition & 1 deletion csharp/ql/src/utils/modelgenerator/CaptureSinkModels.ql
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@

import internal.CaptureModels

from DataFlowTargetApi api, string sink
from DataFlowSinkTargetApi api, string sink
where sink = captureSink(api)
select sink order by sink
2 changes: 1 addition & 1 deletion csharp/ql/src/utils/modelgenerator/CaptureSourceModels.ql
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@

import internal.CaptureModels

from DataFlowTargetApi api, string source
from DataFlowSourceTargetApi api, string source
where source = captureSource(api)
select source order by source
2 changes: 1 addition & 1 deletion csharp/ql/src/utils/modelgenerator/CaptureSummaryModels.ql
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@ import semmle.code.csharp.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
import internal.CaptureModels
import internal.CaptureSummaryFlowQuery

from DataFlowTargetApi api, string flow
from DataFlowSummaryTargetApi api, string flow
where flow = captureFlow(api)
select flow order by flow
26 changes: 16 additions & 10 deletions csharp/ql/src/utils/modelgenerator/internal/CaptureModels.qll
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,16 @@ private class ReturnNodeExt extends DataFlow::Node {
}
}

class DataFlowTargetApi extends TargetApiSpecific {
DataFlowTargetApi() { not isUninterestingForDataFlowModels(this) }
class DataFlowSummaryTargetApi extends SummaryTargetApi {
DataFlowSummaryTargetApi() { not isUninterestingForDataFlowModels(this) }
}

class DataFlowSourceTargetApi = SourceTargetApi;

class DataFlowSinkTargetApi = SinkTargetApi;

private module Printing implements PrintingSig {
class Api = DataFlowTargetApi;
class Api = TargetApiBase;

string getProvenance() { result = "df-generated" }
}
Expand Down Expand Up @@ -87,7 +91,7 @@ string asInputArgument(DataFlow::Node source) { result = asInputArgumentSpecific
/**
* Gets the summary model of `api`, if it follows the `fluent` programming pattern (returns `this`).
*/
string captureQualifierFlow(TargetApiSpecific api) {
string captureQualifierFlow(TargetApiBase api) {
exists(ReturnNodeExt ret |
api = returnNodeEnclosingCallable(ret) and
isOwnInstanceAccessNode(ret)
Expand Down Expand Up @@ -148,7 +152,7 @@ module PropagateFlowConfig implements DataFlow::StateConfigSig {

predicate isSource(DataFlow::Node source, FlowState state) {
source instanceof DataFlow::ParameterNode and
source.getEnclosingCallable() instanceof DataFlowTargetApi and
source.getEnclosingCallable() instanceof DataFlowSummaryTargetApi and
state.(TaintRead).getStep() = 0
}

Expand Down Expand Up @@ -193,7 +197,7 @@ private module PropagateFlow = TaintTracking::GlobalWithState<PropagateFlowConfi
/**
* Gets the summary model(s) of `api`, if there is flow from parameters to return value or parameter.
*/
string captureThroughFlow(DataFlowTargetApi api) {
string captureThroughFlow(DataFlowSummaryTargetApi api) {
exists(DataFlow::ParameterNode p, ReturnNodeExt returnNodeExt, string input, string output |
PropagateFlow::flow(p, returnNodeExt) and
returnNodeExt.(DataFlow::Node).getEnclosingCallable() = api and
Expand Down Expand Up @@ -221,7 +225,7 @@ module PropagateFromSourceConfig implements DataFlow::ConfigSig {

predicate isSink(DataFlow::Node sink) {
sink instanceof ReturnNodeExt and
apiSink(sink)
sink.getEnclosingCallable() instanceof DataFlowSourceTargetApi
}

DataFlow::FlowFeature getAFeature() { result instanceof DataFlow::FeatureHasSinkCallContext }
Expand All @@ -240,7 +244,7 @@ private module PropagateFromSource = TaintTracking::Global<PropagateFromSourceCo
/**
* Gets the source model(s) of `api`, if there is flow from an existing known source to the return of `api`.
*/
string captureSource(DataFlowTargetApi api) {
string captureSource(DataFlowSourceTargetApi api) {
exists(DataFlow::Node source, ReturnNodeExt sink, string kind |
PropagateFromSource::flow(source, sink) and
ExternalFlow::sourceNode(source, kind) and
Expand All @@ -258,7 +262,9 @@ string captureSource(DataFlowTargetApi api) {
* into an existing known sink (then the API itself becomes a sink).
*/
module PropagateToSinkConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { apiSource(source) }
predicate isSource(DataFlow::Node source) {
apiSource(source) and source.getEnclosingCallable() instanceof DataFlowSinkTargetApi
}

predicate isSink(DataFlow::Node sink) {
exists(string kind | isRelevantSinkKind(kind) and ExternalFlow::sinkNode(sink, kind))
Expand All @@ -278,7 +284,7 @@ private module PropagateToSink = TaintTracking::Global<PropagateToSinkConfig>;
/**
* Gets the sink model(s) of `api`, if there is flow from a parameter to an existing known sink.
*/
string captureSink(DataFlowTargetApi api) {
string captureSink(DataFlowSinkTargetApi api) {
exists(DataFlow::Node src, DataFlow::Node sink, string kind |
PropagateToSink::flow(src, sink) and
ExternalFlow::sinkNode(sink, kind) and
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ signature module PrintingSig {
/**
* The class of APIs relevant for model generation.
*/
class Api extends TargetApiSpecific;
class Api extends TargetApiBase;

/**
* Gets the string representation of the provenance of the models.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,11 +82,21 @@ private Callable liftedImpl(Callable api) {
not exists(getARelevantOverrideeOrImplementee(result))
}

private predicate hasManualModel(Callable 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::SourceCallable sc | sc.hasManualModel()) or
api = any(FlowSummaryImpl::Public::NeutralSourceCallable sc | sc.hasManualModel())
}

private predicate hasManualSinkModel(Callable api) {
api = any(FlowSummaryImpl::Public::SinkCallable sc | sc.hasManualModel()) or
api = any(FlowSummaryImpl::Public::NeutralSinkCallable sc | sc.hasManualModel())
}

/**
* Holds if it is irrelevant to generate models for `api` based on data flow analysis.
*
Expand All @@ -101,20 +111,39 @@ predicate isUninterestingForDataFlowModels(CS::Callable api) { isHigherOrder(api
*/
predicate isUninterestingForTypeBasedFlowModels(CS::Callable api) { none() }

/**
* A class of callables that are potentially relevant for generating summary and
* neutral models.
*/
class SummaryTargetApi extends TargetApiBase {
SummaryTargetApi() { not hasManualSummaryModel(this) }
}

/**
* A class of callables that are potentially relevant for generating sink models.
*/
class SinkTargetApi extends TargetApiBase {
SinkTargetApi() { not hasManualSinkModel(this) }
}

/**
* A class of callables that are potentially relevant for generating source models.
*/
class SourceTargetApi extends TargetApiBase {
SourceTargetApi() { not hasManualSourceModel(this) }
}

/**
* A class of callables that are potentially relevant for generating summary, source, sink
* and neutral models.
*
* In the Standard library and 3rd party libraries it is the callables (or callables that have a
* super implementation) that can be called from outside the library itself.
*/
class TargetApiSpecific extends Callable {
class TargetApiBase extends Callable {
private Callable lift;

TargetApiSpecific() {
lift = liftedImpl(this) and
not hasManualModel(lift)
}
TargetApiBase() { lift = liftedImpl(this) }

/**
* Gets the callable that a model will be lifted to.
Expand All @@ -130,9 +159,9 @@ class TargetApiSpecific extends Callable {
predicate isRelevant() { relevant(this) }
}

string asPartialModel(TargetApiSpecific api) { result = ExternalFlow::asPartialModel(api.lift()) }
string asPartialModel(TargetApiBase api) { result = ExternalFlow::asPartialModel(api.lift()) }

string asPartialNeutralModel(TargetApiSpecific api) { result = ExternalFlow::getSignature(api) }
string asPartialNeutralModel(TargetApiBase api) { result = ExternalFlow::getSignature(api) }

/**
* Holds if `t` is a type that is generally used for bulk data in collection types.
Expand Down Expand Up @@ -228,43 +257,11 @@ private predicate isRelevantMemberAccess(DataFlow::Node node) {

predicate sinkModelSanitizer(DataFlow::Node node) { none() }

private class ManualNeutralSinkCallable extends Callable {
ManualNeutralSinkCallable() {
this =
any(FlowSummaryImpl::Public::NeutralCallable nc |
nc.hasManualModel() and nc.getKind() = "sink"
)
}
}

/**
* Holds if `source` is an api entrypoint relevant for creating sink models.
*/
predicate apiSource(DataFlow::Node source) {
(isRelevantMemberAccess(source) or source instanceof DataFlow::ParameterNode) and
exists(Callable enclosing | enclosing = source.getEnclosingCallable() |
relevant(enclosing) and
not enclosing instanceof ManualNeutralSinkCallable
)
}

private class ManualNeutralSourceCallable extends Callable {
ManualNeutralSourceCallable() {
this =
any(FlowSummaryImpl::Public::NeutralCallable nc |
nc.hasManualModel() and nc.getKind() = "source"
)
}
}

/**
* Holds if `sink` is an api entrypoint relevantfor creating source models.
*/
predicate apiSink(DataFlow::Node sink) {
exists(Callable enclosing | enclosing = sink.getEnclosingCallable() |
relevant(enclosing) and
not enclosing instanceof ManualNeutralSourceCallable
)
isRelevantMemberAccess(source) or source instanceof DataFlow::ParameterNode
}

/**
Expand All @@ -273,7 +270,7 @@ predicate apiSink(DataFlow::Node sink) {
*/
bindingset[source, api]
pragma[inline_late]
predicate irrelevantSourceSinkApi(Callable source, TargetApiSpecific api) {
predicate irrelevantSourceSinkApi(Callable source, SourceTargetApi api) {
not api = source.getEnclosingCallable*()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ private import CaptureModels
* Captured Model:
* ```Summaries;BasicFlow;false;AssignToArray;(System.Int32,System.Int32[]);Argument[0];Argument[1].Element;taint;df-generated```
*/
string captureFlow(DataFlowTargetApi api) {
string captureFlow(DataFlowSummaryTargetApi api) {
result = captureQualifierFlow(api) or
result = captureThroughFlow(api)
}
Expand All @@ -86,8 +86,8 @@ string captureFlow(DataFlowTargetApi api) {
* a summary model that applies to `api` and if it relevant to generate
* a model for `api`.
*/
string captureNoFlow(DataFlowTargetApi api) {
not exists(DataFlowTargetApi api0 | exists(captureFlow(api0)) and api0.lift() = api.lift()) and
string captureNoFlow(DataFlowSummaryTargetApi api) {
not exists(DataFlowSummaryTargetApi api0 | exists(captureFlow(api0)) and api0.lift() = api.lift()) and
api.isRelevant() and
result = ModelPrinting::asNeutralSummaryModel(api)
}
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ private module ModelPrinting = PrintingImpl<Printing>;
* A class of callables that are relevant generating summaries for based
* on the Theorems for Free approach.
*/
class TypeBasedFlowTargetApi extends Specific::TargetApiSpecific {
class TypeBasedFlowTargetApi extends Specific::SummaryTargetApi {
TypeBasedFlowTargetApi() { not Specific::isUninterestingForTypeBasedFlowModels(this) }

/**
Expand Down

0 comments on commit d98d377

Please sign in to comment.