Skip to content

Commit

Permalink
Merge pull request #16374 from michaelnebel/java/narrowsuperimpl
Browse files Browse the repository at this point in the history
Java: Improve finding best type for models and lifting.
  • Loading branch information
michaelnebel committed May 14, 2024
2 parents e65a62c + 64145cf commit 79c6834
Show file tree
Hide file tree
Showing 10 changed files with 209 additions and 91 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ private import CaptureModelsSpecific
private import CaptureModelsPrinting

class DataFlowTargetApi extends TargetApiSpecific {
DataFlowTargetApi() { isRelevantForDataFlowModels(this) }
DataFlowTargetApi() { not isUninterestingForDataFlowModels(this) }
}

private module Printing implements PrintingSig {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,16 +51,18 @@ private predicate isRelevantForModels(CS::Callable api) {
}

/**
* Holds if it is relevant to generate models for `api` based on data flow analysis.
* Holds if it is irrelevant to generate models for `api` based on data flow analysis.
*
* This serves as an extra filter for the `relevant` predicate.
*/
predicate isRelevantForDataFlowModels(CS::Callable api) {
isRelevantForModels(api) and not isHigherOrder(api)
}
predicate isUninterestingForDataFlowModels(CS::Callable api) { isHigherOrder(api) }

/**
* Holds if it is relevant to generate models for `api` based on its type.
* Holds if it is irrelevant to generate models for `api` based on type-based analysis.
*
* This serves as an extra filter for the `relevant` predicate.
*/
predicate isRelevantForTypeBasedFlowModels = isRelevantForModels/1;
predicate isUninterestingForTypeBasedFlowModels(CS::Callable api) { none() }

/**
* A class of callables that are relevant generating summary, source and sinks models for.
Expand All @@ -71,7 +73,8 @@ predicate isRelevantForTypeBasedFlowModels = isRelevantForModels/1;
class TargetApiSpecific extends CS::Callable {
TargetApiSpecific() {
this.fromSource() and
this.isUnboundDeclaration()
this.isUnboundDeclaration() and
isRelevantForModels(this)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ private module ModelPrinting = PrintingImpl<Printing>;
* on the Theorems for Free approach.
*/
class TypeBasedFlowTargetApi extends Specific::TargetApiSpecific {
TypeBasedFlowTargetApi() { Specific::isRelevantForTypeBasedFlowModels(this) }
TypeBasedFlowTargetApi() { not Specific::isUninterestingForTypeBasedFlowModels(this) }

/**
* Gets the string representation of all type based summaries for `this`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ private import CaptureModelsSpecific
private import CaptureModelsPrinting

class DataFlowTargetApi extends TargetApiSpecific {
DataFlowTargetApi() { isRelevantForDataFlowModels(this) }
DataFlowTargetApi() { not isUninterestingForDataFlowModels(this) }
}

private module Printing implements PrintingSig {
Expand Down
150 changes: 86 additions & 64 deletions java/ql/src/utils/modelgenerator/internal/CaptureModelsSpecific.qll
Original file line number Diff line number Diff line change
Expand Up @@ -24,38 +24,59 @@ class Unit = J::Unit;

class Callable = J::Callable;

private J::Method superImpl(J::Method m) {
result = m.getAnOverride() and
not exists(result.getAnOverride()) and
not m instanceof J::ToStringMethod
}

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
}

/**
* Holds if it is relevant to generate models for `api`.
* 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 predicate isRelevantForModels(Callable api) {
not isUninterestingForModels(api) and
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()
private J::Callable liftedImpl(J::Callable m) {
(
result = getARelevantOverride(m)
or
result = m and relevant(m)
) and
not exists(getARelevantOverride(result))
}

private predicate hasManualModel(Callable api) {
api = any(FlowSummaryImpl::Public::SummarizedCallable sc | sc.applyManualModel()).asCallable() or
api = any(FlowSummaryImpl::Public::NeutralSummaryCallable sc | sc.hasManualModel()).asCallable()
}

/**
* Holds if it is relevant to generate models for `api` based on data flow analysis.
* Holds if it is irrelevant to generate models for `api` based on data flow analysis.
*
* This serves as an extra filter for the `relevant` predicate.
*/
predicate isRelevantForDataFlowModels(Callable api) {
isRelevantForModels(api) and
(not api.getDeclaringType() instanceof J::Interface or exists(api.getBody()))
predicate isUninterestingForDataFlowModels(Callable api) {
api.getDeclaringType() instanceof J::Interface and not exists(api.getBody())
}

predicate isRelevantForTypeBasedFlowModels = isRelevantForModels/1;
/**
* Holds if it is irrelevant to generate models for `api` based on type-based analysis.
*
* This serves as an extra filter for the `relevant` predicate.
*/
predicate isUninterestingForTypeBasedFlowModels(Callable api) { none() }

/**
* A class of Callables that are relevant for generating summary, source and sinks models for.
Expand All @@ -64,63 +85,54 @@ predicate isRelevantForTypeBasedFlowModels = isRelevantForModels/1;
* from outside the library itself.
*/
class TargetApiSpecific extends Callable {
private Callable lift;

TargetApiSpecific() {
this.isPublic() and
this.fromSource() and
(
this.getDeclaringType().isPublic() or
superImpl(this).getDeclaringType().isPublic()
) and
isRelevantForModels(this)
lift = liftedImpl(this) and
not hasManualModel(lift)
}

/**
* Gets the callable that a model will be lifted to, if any.
* Gets the callable that a model will be lifted to.
*/
Callable lift() {
exists(Method m | m = superImpl(this) and m.fromSource() | result = m)
or
not exists(superImpl(this)) and result = this
}
Callable lift() { result = lift }
}

private string isExtensible(J::RefType ref) {
if ref.isFinal() then result = "false" else result = "true"
}

private string typeAsModel(J::RefType type) {
result =
type.getCompilationUnit().getPackage().getName() + ";" +
type.getErasure().(J::RefType).nestedName()
}

private J::RefType bestTypeForModel(TargetApiSpecific api) {
result = api.lift().getDeclaringType()
private string isExtensible(Callable c) {
if c.getDeclaringType().isFinal() then result = "false" else result = "true"
}

/**
* Returns the appropriate type name for the model. Either the type
* declaring the method or the supertype introducing the method.
* Returns the appropriate type name for the model.
*/
private string typeAsSummaryModel(TargetApiSpecific api) {
result = typeAsModel(bestTypeForModel(api))
private string typeAsModel(Callable c) {
exists(RefType type | type = c.getDeclaringType() |
result =
type.getCompilationUnit().getPackage().getName() + ";" +
type.getErasure().(J::RefType).nestedName()
)
}

private predicate partialModel(TargetApiSpecific api, string type, string name, string parameters) {
type = typeAsSummaryModel(api) and
name = api.getName() and
parameters = ExternalFlow::paramsString(api)
private predicate partialLiftedModel(
TargetApiSpecific api, string type, string extensible, string name, string parameters
) {
exists(Callable c | c = api.lift() |
type = typeAsModel(c) and
extensible = isExtensible(c) and
name = c.getName() and
parameters = ExternalFlow::paramsString(c)
)
}

/**
* Computes the first 6 columns for MaD rows.
*/
string asPartialModel(TargetApiSpecific api) {
exists(string type, string name, string parameters |
partialModel(api, type, name, parameters) and
exists(string type, string extensible, string name, string parameters |
partialLiftedModel(api, type, extensible, name, parameters) and
result =
type + ";" //
+ isExtensible(bestTypeForModel(api)) + ";" //
+ extensible + ";" //
+ name + ";" //
+ parameters + ";" //
+ /* ext + */ ";" //
Expand All @@ -132,7 +144,7 @@ string asPartialModel(TargetApiSpecific api) {
*/
string asPartialNeutralModel(TargetApiSpecific api) {
exists(string type, string name, string parameters |
partialModel(api, type, name, parameters) and
partialLiftedModel(api, type, _, name, parameters) and
result =
type + ";" //
+ name + ";" //
Expand Down Expand Up @@ -228,6 +240,15 @@ predicate sinkModelSanitizer(DataFlow::Node node) {
)
}

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

/**
* Holds if `source` is an api entrypoint relevant for creating sink models.
*/
Expand All @@ -236,14 +257,15 @@ predicate apiSource(DataFlow::Node source) {
source.asExpr().(J::FieldAccess).isOwnFieldAccess() or
source instanceof DataFlow::ParameterNode
) and
source.getEnclosingCallable().isPublic() and
exists(J::RefType t |
t = source.getEnclosingCallable().getDeclaringType().getAnAncestor() and
not t instanceof J::TypeObject and
t.isPublic()
) and
isRelevantForModels(source.getEnclosingCallable()) and
exists(asPartialModel(source.getEnclosingCallable()))
exists(Callable enclosing | enclosing = source.getEnclosingCallable() |
exists(liftedImpl(enclosing)) and
not enclosing instanceof ManualNeutralSinkCallable and
exists(J::RefType t |
t = enclosing.getDeclaringType().getAnAncestor() and
not t instanceof J::TypeObject and
t.isPublic()
)
)
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,7 @@ private module ModelPrinting = PrintingImpl<Printing>;
* on the Theorems for Free approach.
*/
class TypeBasedFlowTargetApi extends Specific::TargetApiSpecific {
TypeBasedFlowTargetApi() { Specific::isRelevantForTypeBasedFlowModels(this) }
TypeBasedFlowTargetApi() { not Specific::isUninterestingForTypeBasedFlowModels(this) }

/**
* Gets the string representation of all type based summaries for `this`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@

public abstract class AbstractImplOfExternalSPI implements FileFilter {

@Override
public boolean accept(File pathname) {
return false;
}

}
// neutral=p;AbstractImplOfExternalSPI;accept;(File);summary;df-generated
@Override
public boolean accept(File pathname) {
return false;
}
}
21 changes: 11 additions & 10 deletions java/ql/test/utils/modelgenerator/dataflow/p/ImplOfExternalSPI.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@

public class ImplOfExternalSPI extends AbstractImplOfExternalSPI {

@Override
public boolean accept(File pathname) {
try {
Files.createFile(pathname.toPath());
} catch (IOException e) {
e.printStackTrace();
}
return false;
// sink=p;AbstractImplOfExternalSPI;true;accept;(File);;Argument[0];path-injection;df-generated
// neutral=p;AbstractImplOfExternalSPI;accept;(File);summary;df-generated
@Override
public boolean accept(File pathname) {
try {
Files.createFile(pathname.toPath());
} catch (IOException e) {
e.printStackTrace();
}

}
return false;
}
}

0 comments on commit 79c6834

Please sign in to comment.