@@ -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