Skip to content

Commit 14b87f9

Browse files
committed
Shared: Extend the shared Guards library with support for custom wrappers.
1 parent 1d75008 commit 14b87f9

File tree

1 file changed

+175
-28
lines changed

1 file changed

+175
-28
lines changed

shared/controlflow/codeql/controlflow/Guards.qll

Lines changed: 175 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,17 @@ module Make<LocationSig Location, InputSig<Location> Input> {
485485
default predicate additionalNullCheck(PreGuard guard, GuardValue val, Expr e, boolean isNull) {
486486
none()
487487
}
488+
489+
/**
490+
* Holds if the assumption that `g1` has been evaluated to `v1` implies that
491+
* `g2` has been evaluated to `v2`, that is, the evaluation of `g2` to `v2`
492+
* dominates the evaluation of `g1` to `v1`.
493+
*
494+
* This predicate can be instantiated with `CustomGuard<..>::additionalImpliesStep`.
495+
*/
496+
default predicate additionalImpliesStep(PreGuard g1, GuardValue v1, PreGuard g2, GuardValue v2) {
497+
none()
498+
}
488499
}
489500

490501
module Logic<LogicInputSig LogicInput> {
@@ -695,41 +706,177 @@ module Make<LocationSig Location, InputSig<Location> Input> {
695706
)
696707
}
697708

698-
pragma[nomagic]
709+
private signature predicate baseGuardValueSig(Guard guard, GuardValue v);
710+
711+
/**
712+
* Calculates the transitive closure of all the guard implication steps
713+
* starting from a given set of base cases.
714+
*/
715+
private module ImpliesTC<baseGuardValueSig/2 baseGuardValue> {
716+
pragma[nomagic]
717+
predicate guardControls(Guard guard, GuardValue v, Guard tgtGuard, GuardValue tgtVal) {
718+
baseGuardValue(tgtGuard, tgtVal) and
719+
guard = tgtGuard and
720+
v = tgtVal
721+
or
722+
exists(Guard g0, GuardValue v0 |
723+
guardControls(g0, v0, tgtGuard, tgtVal) and
724+
impliesStep(g0, v0, guard, v)
725+
)
726+
or
727+
exists(Guard g0, GuardValue v0 |
728+
guardControls(g0, v0, tgtGuard, tgtVal) and
729+
unboundImpliesStep(g0, v0, guard, v)
730+
)
731+
or
732+
exists(SsaDefinition def0, GuardValue v0 |
733+
ssaControls(def0, v0, tgtGuard, tgtVal) and
734+
impliesStepSsaGuard(def0, v0, guard, v)
735+
)
736+
or
737+
exists(Guard g0, GuardValue v0 |
738+
guardControls(g0, v0, tgtGuard, tgtVal) and
739+
additionalImpliesStep(g0, v0, guard, v)
740+
)
741+
}
742+
743+
pragma[nomagic]
744+
predicate ssaControls(SsaDefinition def, GuardValue v, Guard tgtGuard, GuardValue tgtVal) {
745+
exists(Guard g0 |
746+
guardControls(g0, v, tgtGuard, tgtVal) and
747+
guardReadsSsaVar(g0, def)
748+
)
749+
or
750+
exists(SsaDefinition def0 |
751+
ssaControls(def0, v, tgtGuard, tgtVal) and
752+
impliesStepSsa(def0, v, def)
753+
)
754+
}
755+
}
756+
757+
private predicate hasAValueBranchEdge(Guard guard, GuardValue v) {
758+
guard.hasValueBranchEdge(_, _, v)
759+
}
760+
761+
private module BranchImplies = ImpliesTC<hasAValueBranchEdge/2>;
762+
699763
private predicate guardControlsBranchEdge(
700764
Guard guard, BasicBlock bb1, BasicBlock bb2, GuardValue v
701765
) {
702-
guard.hasValueBranchEdge(bb1, bb2, v)
703-
or
704766
exists(Guard g0, GuardValue v0 |
705-
guardControlsBranchEdge(g0, bb1, bb2, v0) and
706-
impliesStep(g0, v0, guard, v)
707-
)
708-
or
709-
exists(Guard g0, GuardValue v0 |
710-
guardControlsBranchEdge(g0, bb1, bb2, v0) and
711-
unboundImpliesStep(g0, v0, guard, v)
712-
)
713-
or
714-
exists(SsaDefinition def0, GuardValue v0 |
715-
ssaControlsBranchEdge(def0, bb1, bb2, v0) and
716-
impliesStepSsaGuard(def0, v0, guard, v)
767+
g0.hasValueBranchEdge(bb1, bb2, v0) and
768+
BranchImplies::guardControls(guard, v, g0, v0)
717769
)
718770
}
719771

720-
pragma[nomagic]
721-
private predicate ssaControlsBranchEdge(
722-
SsaDefinition def, BasicBlock bb1, BasicBlock bb2, GuardValue v
723-
) {
724-
exists(Guard g0 |
725-
guardControlsBranchEdge(g0, bb1, bb2, v) and
726-
guardReadsSsaVar(g0, def)
727-
)
728-
or
729-
exists(SsaDefinition def0 |
730-
ssaControlsBranchEdge(def0, bb1, bb2, v) and
731-
impliesStepSsa(def0, v, def)
732-
)
772+
signature module CustomGuardInputSig {
773+
class ParameterPosition {
774+
/** Gets a textual representation of this element. */
775+
bindingset[this]
776+
string toString();
777+
}
778+
779+
class ArgumentPosition {
780+
/** Gets a textual representation of this element. */
781+
bindingset[this]
782+
string toString();
783+
}
784+
785+
/**
786+
* Holds if the parameter position `ppos` matches the argument position
787+
* `apos`.
788+
*/
789+
predicate parameterMatch(ParameterPosition ppos, ArgumentPosition apos);
790+
791+
/** A non-overridable method with a boolean return value. */
792+
class BooleanMethod {
793+
SsaDefinition getParameter(ParameterPosition ppos);
794+
795+
Expr getAReturnExpr();
796+
}
797+
798+
class BooleanMethodCall extends Expr {
799+
BooleanMethod getMethod();
800+
801+
Expr getArgument(ArgumentPosition apos);
802+
}
803+
}
804+
805+
/**
806+
* Provides an implementation of guard implication logic for custom
807+
* wrappers. This can be used to instantiate the `additionalImpliesStep`
808+
* predicate.
809+
*/
810+
module CustomGuard<CustomGuardInputSig CustomGuardInput> {
811+
private import CustomGuardInput
812+
813+
private class ReturnExpr extends ExprFinal {
814+
ReturnExpr() { any(BooleanMethod m).getAReturnExpr() = this }
815+
816+
pragma[nomagic]
817+
BasicBlock getBasicBlock() { result = super.getBasicBlock() }
818+
}
819+
820+
private predicate booleanReturnGuard(Guard guard, GuardValue val) {
821+
guard instanceof ReturnExpr and exists(val.asBooleanValue())
822+
}
823+
824+
private module ReturnImplies = ImpliesTC<booleanReturnGuard/2>;
825+
826+
/**
827+
* Holds if `ret` is a return expression in a non-overridable method that
828+
* on a return value of `retval` allows the conclusion that the `ppos`th
829+
* parameter has the value `val`.
830+
*/
831+
private predicate validReturnInCustomGuard(
832+
ReturnExpr ret, ParameterPosition ppos, boolean retval, GuardValue val
833+
) {
834+
exists(BooleanMethod m, SsaDefinition param |
835+
m.getAReturnExpr() = ret and
836+
m.getParameter(ppos) = param and
837+
not val instanceof TCaseMatch
838+
|
839+
exists(Guard g0, GuardValue v0 |
840+
g0.directlyValueControls(ret.getBasicBlock(), v0) and
841+
BranchImplies::ssaControls(param, val, g0, v0) and
842+
retval = [true, false]
843+
)
844+
or
845+
ReturnImplies::ssaControls(param, val, ret,
846+
any(GuardValue r | r.asBooleanValue() = retval))
847+
)
848+
}
849+
850+
/**
851+
* Gets a non-overridable method with a boolean return value that performs a check
852+
* on the `ppos`th parameter. A return value equal to `retval` allows us to conclude
853+
* that the argument has the value `val`.
854+
*/
855+
private BooleanMethod customGuard(ParameterPosition ppos, boolean retval, GuardValue val) {
856+
forex(ReturnExpr ret |
857+
result.getAReturnExpr() = ret and
858+
not ret.(ConstantExpr).asBooleanValue() = retval.booleanNot()
859+
|
860+
validReturnInCustomGuard(ret, ppos, retval, val)
861+
)
862+
}
863+
864+
/**
865+
* Holds if the assumption that `g1` has been evaluated to `v1` implies that
866+
* `g2` has been evaluated to `v2`, that is, the evaluation of `g2` to `v2`
867+
* dominates the evaluation of `g1` to `v1`.
868+
*
869+
* This predicate covers the implication steps that arise from calls to
870+
* custom guard wrappers.
871+
*/
872+
predicate additionalImpliesStep(PreGuard g1, GuardValue v1, PreGuard g2, GuardValue v2) {
873+
exists(BooleanMethodCall call, ParameterPosition ppos, ArgumentPosition apos |
874+
g1 = call and
875+
call.getMethod() = customGuard(ppos, v1.asBooleanValue(), v2) and
876+
call.getArgument(apos) = g2 and
877+
parameterMatch(pragma[only_bind_out](ppos), pragma[only_bind_out](apos))
878+
)
879+
}
733880
}
734881

735882
/**

0 commit comments

Comments
 (0)