Skip to content

Commit 79ce1ef

Browse files
l46kokcopybara-github
authored andcommitted
Generate satisfiable model for isSatisfiable
PiperOrigin-RevId: 950258249
1 parent ff7bccf commit 79ce1ef

13 files changed

Lines changed: 427 additions & 133 deletions

verifier/README.md

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -229,16 +229,6 @@ public class PolicyVerifierExample {
229229

230230
### Limitations
231231

232-
* **Cross-Type Numeric Comparisons:**
233-
* **Equality (`==`, `!=`):** Equality comparisons between different
234-
numeric types (e.g., `int` vs `double` or `uint` vs `double`) are
235-
currently not supported and will evaluate to `false` during
236-
verification, even if they have the same mathematical value (e.g.,
237-
`dyn(1) == dyn(1.0)` is false). Note that `int` vs `uint` equality *is*
238-
supported.
239-
* **Relational Operators (`<`, `>`, `<=`, `>=`):** Cross-type relational
240-
comparisons are fully supported mathematically across all numeric
241-
combinations (`int`, `uint`, and `double`).
242232
* **Unsupported Standard Functions (Uninterpreted Functions):** Not all
243233
CEL standard library functions have SMT axioms defined yet. Unsupported
244234
functions are treated as *uninterpreted functions* by Z3 (the solver

verifier/src/main/java/dev/cel/verifier/CelAstToZ3Translator.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -564,7 +564,7 @@ private TranslatedValue translateSelect(CelExpr celExpr, CelAbstractSyntaxTree a
564564
typeConstraints.add(createTypeConstraint(fieldAccess, exprId, ast));
565565

566566
return TranslatedValue.propagateStrict(
567-
ctx, typeSystem, fieldAccess, celExpr, Arrays.asList(operandTv));
567+
ctx, typeSystem, fieldAccess, celExpr, ImmutableList.of(operandTv));
568568
}
569569

570570
private TranslatedValue translateBlock(

verifier/src/main/java/dev/cel/verifier/CelVerificationResult.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,15 +34,20 @@ public enum VerificationStatus {
3434
public abstract VerificationStatus status();
3535

3636
/**
37-
* Returns a message detailing why the verification failed or was inconclusive (e.g., the
38-
* counterexample input or truncation reason). Empty if status is VERIFIED.
37+
* Returns a message detailing the outcome of the verification check, such as a counterexample
38+
* input, satisfying model assignments, or truncation reason. May be empty if status is VERIFIED
39+
* and no model inputs apply (e.g., when verifying isAlwaysTrue without counterexamples).
3940
*/
4041
public abstract String message();
4142

4243
static CelVerificationResult verified() {
4344
return new AutoValue_CelVerificationResult(VerificationStatus.VERIFIED, "");
4445
}
4546

47+
static CelVerificationResult verified(String message) {
48+
return new AutoValue_CelVerificationResult(VerificationStatus.VERIFIED, message);
49+
}
50+
4651
static CelVerificationResult failed(String message) {
4752
return new AutoValue_CelVerificationResult(VerificationStatus.VIOLATED, message);
4853
}

verifier/src/main/java/dev/cel/verifier/CelVerifier.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@
2222
public interface CelVerifier {
2323

2424
/**
25-
* Returns verified if there is at least one input combination where the AST evaluates to true.
25+
* Returns verified if there is at least one input combination where the AST evaluates to true. If
26+
* the expression is satisfiable and depends on input variables, the result message will contain a
27+
* satisfying model (witness) with concrete variable assignments.
2628
*
2729
* @param ast The input expression to verify. Must be a type-checked AST.
2830
*/

verifier/src/main/java/dev/cel/verifier/CelVerifierZ3Impl.java

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -192,13 +192,21 @@ public CelVerificationResult verifyEquivalence(
192192
return CelVerificationResult.failed(
193193
"Equivalence violation detected."
194194
+ getCounterexampleString(
195-
ctx, translator.getTypeSystem(), result.model, /* isApproximate= */ false));
195+
ctx,
196+
translator.getTypeSystem(),
197+
result.model,
198+
/* isApproximate= */ false,
199+
/* isCounterexample= */ true));
196200
case APPROXIMATE_MATCH:
197201
return CelVerificationResult.inconclusive(
198202
"Inconclusive: a divergence may exist, but it depends on approximations, missing"
199203
+ " theories, or loop bounds."
200204
+ getCounterexampleString(
201-
ctx, translator.getTypeSystem(), result.model, /* isApproximate= */ true));
205+
ctx,
206+
translator.getTypeSystem(),
207+
result.model,
208+
/* isApproximate= */ true,
209+
/* isCounterexample= */ true));
202210
case TRUNCATED:
203211
return CelVerificationResult.inconclusive(
204212
"Inconclusive: expressions are equivalent within the current loop unroll limit, but"
@@ -250,8 +258,16 @@ private CelVerificationResult checkSatisfiability(
250258
ctx,
251259
translator.getTypeSystem(),
252260
result.model,
253-
/* isApproximate= */ false))
254-
: CelVerificationResult.verified();
261+
/* isApproximate= */ false,
262+
/* isCounterexample= */ true))
263+
: CelVerificationResult.verified(
264+
"Condition is satisfiable."
265+
+ getCounterexampleString(
266+
ctx,
267+
translator.getTypeSystem(),
268+
result.model,
269+
/* isApproximate= */ false,
270+
/* isCounterexample= */ false));
255271

256272
case APPROXIMATE_MATCH:
257273
String prefix =
@@ -263,7 +279,11 @@ private CelVerificationResult checkSatisfiability(
263279
return CelVerificationResult.inconclusive(
264280
prefix
265281
+ getCounterexampleString(
266-
ctx, translator.getTypeSystem(), result.model, /* isApproximate= */ true));
282+
ctx,
283+
translator.getTypeSystem(),
284+
result.model,
285+
/* isApproximate= */ true,
286+
/* isCounterexample= */ searchForCounterexample));
267287

268288
case TRUNCATED:
269289
return CelVerificationResult.inconclusive(
@@ -357,8 +377,13 @@ private Solver newSolver(Context ctx) {
357377
}
358378

359379
private static String getCounterexampleString(
360-
Context ctx, CelZ3TypeSystem typeSystem, Model model, boolean isApproximate) {
361-
return CelZ3CounterexampleGenerator.generate(ctx, typeSystem, model, isApproximate);
380+
Context ctx,
381+
CelZ3TypeSystem typeSystem,
382+
Model model,
383+
boolean isApproximate,
384+
boolean isCounterexample) {
385+
return CelZ3CounterexampleGenerator.generate(
386+
ctx, typeSystem, model, isApproximate, isCounterexample);
362387
}
363388

364389
CelVerifierZ3Impl(

verifier/src/main/java/dev/cel/verifier/CelZ3CounterexampleGenerator.java

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,11 @@ final class CelZ3CounterexampleGenerator {
3636
private CelZ3CounterexampleGenerator() {}
3737

3838
static String generate(
39-
Context ctx, CelZ3TypeSystem typeSystem, Model model, boolean isApproximate) {
39+
Context ctx,
40+
CelZ3TypeSystem typeSystem,
41+
Model model,
42+
boolean isApproximate,
43+
boolean isCounterexample) {
4044
FuncDecl[] constDecls = model.getConstDecls();
4145

4246
List<String> bindings = new ArrayList<>();
@@ -55,10 +59,17 @@ static String generate(
5559
}
5660

5761
if (bindings.isEmpty()) {
58-
return " (The expression fails unconditionally, regardless of input state)";
62+
return isCounterexample
63+
? " (The expression fails unconditionally, regardless of input state)"
64+
: " (The expression is satisfiable unconditionally, regardless of input state)";
5965
}
6066

61-
String prefix = isApproximate ? " Potential counterexample input:" : " Counterexample input:";
67+
String prefix;
68+
if (isCounterexample) {
69+
prefix = isApproximate ? " Potential counterexample input:" : " Counterexample input:";
70+
} else {
71+
prefix = isApproximate ? " Potential satisfying input:" : " Satisfying input:";
72+
}
6273
return prefix + String.join("", bindings);
6374
}
6475

@@ -228,6 +239,9 @@ private static void extractKeys(Expr<?> arrayExpr, List<Expr<?>> keys) {
228239
if (++iterations > 100_000) {
229240
throw new IllegalStateException("Exceeded maximum number of extractKeys iterations.");
230241
}
242+
if (!arrayExpr.isApp()) {
243+
break;
244+
}
231245
FuncDecl<?> decl = arrayExpr.getFuncDecl();
232246
String declName = decl.getName().toString();
233247

0 commit comments

Comments
 (0)