Merge pull request #15179 from michaelnebel/modelgenrespectmanual

C#/Java: Increase precision of model generation.
This commit is contained in:
Michael Nebel
2024-01-12 15:12:21 +01:00
committed by GitHub
12 changed files with 94 additions and 34 deletions

View File

@@ -5,6 +5,7 @@
private import java as J
private import semmle.code.java.dataflow.internal.DataFlowPrivate
private import semmle.code.java.dataflow.internal.ContainerFlow as ContainerFlow
private import semmle.code.java.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
private import semmle.code.java.dataflow.internal.ModelExclusions
private import semmle.code.java.dataflow.DataFlow as Df
private import semmle.code.java.dataflow.SSA as Ssa
@@ -21,6 +22,8 @@ class Type = J::Type;
class Unit = J::Unit;
class Callable = J::Callable;
private J::Method superImpl(J::Method m) {
result = m.getAnOverride() and
not exists(result.getAnOverride()) and
@@ -35,15 +38,19 @@ private predicate isInfrequentlyUsed(J::CompilationUnit cu) {
/**
* Holds if it is relevant to generate models for `api`.
*/
private predicate isRelevantForModels(J::Callable api) {
private predicate isRelevantForModels(Callable api) {
not isUninterestingForModels(api) and
not isInfrequentlyUsed(api.getCompilationUnit())
not isInfrequentlyUsed(api.getCompilationUnit()) and
// Disregard all APIs that have a manual model.
not api = any(FlowSummaryImpl::Public::SummarizedCallable sc | sc.applyManualModel()).asCallable() and
not api =
any(FlowSummaryImpl::Public::NeutralSummaryCallable sc | sc.hasManualModel()).asCallable()
}
/**
* Holds if it is relevant to generate models for `api` based on data flow analysis.
*/
predicate isRelevantForDataFlowModels(J::Callable api) {
predicate isRelevantForDataFlowModels(Callable api) {
isRelevantForModels(api) and
(not api.getDeclaringType() instanceof J::Interface or exists(api.getBody()))
}
@@ -56,7 +63,7 @@ predicate isRelevantForTypeBasedFlowModels = isRelevantForModels/1;
* In the Standard library and 3rd party libraries it the Callables that can be called
* from outside the library itself.
*/
class TargetApiSpecific extends J::Callable {
class TargetApiSpecific extends Callable {
TargetApiSpecific() {
this.isPublic() and
this.fromSource() and
@@ -66,6 +73,15 @@ class TargetApiSpecific extends J::Callable {
) and
isRelevantForModels(this)
}
/**
* Gets the callable that a model will be lifted to, if any.
*/
Callable lift() {
exists(Method m | m = superImpl(this) and m.fromSource() | result = m)
or
not exists(superImpl(this)) and result = this
}
}
private string isExtensible(J::RefType ref) {
@@ -79,9 +95,7 @@ private string typeAsModel(J::RefType type) {
}
private J::RefType bestTypeForModel(TargetApiSpecific api) {
if exists(superImpl(api))
then superImpl(api).fromSource() and result = superImpl(api).getDeclaringType()
else result = api.getDeclaringType()
result = api.lift().getDeclaringType()
}
/**
@@ -195,7 +209,7 @@ string returnNodeAsOutput(DataFlowImplCommon::ReturnNodeExt node) {
/**
* Gets the enclosing callable of `ret`.
*/
J::Callable returnNodeEnclosingCallable(DataFlowImplCommon::ReturnNodeExt ret) {
Callable returnNodeEnclosingCallable(DataFlowImplCommon::ReturnNodeExt ret) {
result = DataFlowImplCommon::getNodeEnclosingCallable(ret).asCallable()
}

View File

@@ -73,10 +73,11 @@ string captureFlow(DataFlowTargetApi api) {
}
/**
* Gets the neutral summary for `api`, if any.
* A neutral model is generated, if there does not exist any summary model.
* Gets the neutral summary model for `api`, if any.
* A neutral summary model is generated, if we are not generating
* a summary model that applies to `api`.
*/
string captureNoFlow(DataFlowTargetApi api) {
not exists(captureFlow(api)) and
not exists(DataFlowTargetApi api0 | exists(captureFlow(api0)) and api0.lift() = api.lift()) and
result = ModelPrinting::asNeutralSummaryModel(api)
}

View File

@@ -22,6 +22,7 @@
| p;Joiner;false;setEmptyValue;(CharSequence);;Argument[0];Argument[this];taint;df-generated |
| p;Joiner;false;setEmptyValue;(CharSequence);;Argument[this];ReturnValue;value;df-generated |
| p;Joiner;false;toString;();;Argument[this];ReturnValue;taint;df-generated |
| p;MultipleImpl2$IInterface;true;m;(Object);;Argument[0];ReturnValue;taint;df-generated |
| p;MultipleImpls$Strat2;true;getValue;();;Argument[this];ReturnValue;taint;df-generated |
| p;MultipleImpls$Strategy;true;doSomething;(String);;Argument[0];Argument[this];taint;df-generated |
| p;MultipleImpls$Strategy;true;doSomething;(String);;Argument[0];ReturnValue;taint;df-generated |

View File

@@ -0,0 +1,23 @@
package p;
class MultipleImpl2 {
// Multiple implementations of the same interface.
// This is used to test that we only generate a summary model and
// not neutral summary model for `IInterface.m`.
public interface IInterface {
Object m(Object value);
}
public class Impl1 implements IInterface {
public Object m(Object value) {
return null;
}
}
public class Impl2 implements IInterface {
public Object m(Object value) {
return value;
}
}
}