aboutsummaryrefslogtreecommitdiffstats
path: root/prog
diff options
context:
space:
mode:
authorAleksandr Nogikh <nogikh@google.com>2024-03-06 16:09:41 +0100
committerAleksandr Nogikh <nogikh@google.com>2024-03-08 10:56:47 +0000
commit8e75c913b6f9b09cab2ad31fd7d66ea0d1703de8 (patch)
tree08556bc4e03b3550324770d80f9a02b057b12ad4 /prog
parent4097c8d7a8596ddbc9a9db7b7f39c5cbdb1bd742 (diff)
prog: auto-set proper conditional fields in Deserialize()
Treat all default union arguments as transient and reevaluate them after the call was fully parsed. Before conditional field patching, we do need to have performed arg validation, which also reevaluates conditions. To break the cycle, make validation configurable.
Diffstat (limited to 'prog')
-rw-r--r--prog/encoding.go13
-rw-r--r--prog/expr.go12
-rw-r--r--prog/minimization.go2
-rw-r--r--prog/rand.go2
-rw-r--r--prog/test/fuzz_test.go3
-rw-r--r--prog/types.go10
-rw-r--r--prog/validation.go14
7 files changed, 41 insertions, 15 deletions
diff --git a/prog/encoding.go b/prog/encoding.go
index b5d5114ca..e925e7918 100644
--- a/prog/encoding.go
+++ b/prog/encoding.go
@@ -257,9 +257,13 @@ func (target *Target) Deserialize(data []byte, mode DeserializeMode) (*Prog, err
// This validation is done even in non-debug mode because deserialization
// procedure does not catch all bugs (e.g. mismatched types).
// And we can receive bad programs from corpus and hub.
- if err := prog.validate(); err != nil {
+ if err := prog.validateWithOpts(validationOptions{
+ // Don't validate auto-set conditional fields. We'll patch them later.
+ ignoreTransient: true,
+ }); err != nil {
return nil, err
}
+ p.fixupConditionals(prog)
if p.autos != nil {
p.fixupAutos(prog)
}
@@ -1154,6 +1158,13 @@ func (p *parser) fixupAutos(prog *Prog) {
}
}
+func (p *parser) fixupConditionals(prog *Prog) {
+ for _, c := range prog.Calls {
+ // Only overwrite transient union fields.
+ c.setDefaultConditions(p.target, true)
+ }
+}
+
func (p *parser) Scan() bool {
if p.e != nil || len(p.data) == 0 {
return false
diff --git a/prog/expr.go b/prog/expr.go
index 5ecbef8eb..38358b214 100644
--- a/prog/expr.go
+++ b/prog/expr.go
@@ -174,7 +174,7 @@ func matchingUnionArgs(typ *UnionType, finder ArgFinder) []int {
func (p *Prog) checkConditions() error {
for _, c := range p.Calls {
- err := c.checkConditions(p.Target)
+ err := c.checkConditions(p.Target, false)
if err != nil {
return err
}
@@ -184,12 +184,15 @@ func (p *Prog) checkConditions() error {
var ErrViolatedConditions = errors.New("conditional fields rules violation")
-func (c *Call) checkConditions(target *Target) error {
+func (c *Call) checkConditions(target *Target, ignoreTransient bool) error {
var ret error
makeArgFinder := argFinderConstructor(target, c)
forEachStaleUnion(target, c, makeArgFinder,
func(a *UnionArg, t *UnionType, okIndices []int) {
+ if ignoreTransient && a.transient {
+ return
+ }
ret = fmt.Errorf("%w union %s field is #%d(%s), but %v satisfy conditions",
ErrViolatedConditions, t.Name(), a.Index, t.Fields[a.Index].Name,
okIndices)
@@ -197,7 +200,7 @@ func (c *Call) checkConditions(target *Target) error {
return ret
}
-func (c *Call) setDefaultConditions(target *Target) bool {
+func (c *Call) setDefaultConditions(target *Target, transientOnly bool) bool {
var anyReplaced bool
// Replace stale conditions with the default values of their correct types.
for {
@@ -205,6 +208,9 @@ func (c *Call) setDefaultConditions(target *Target) bool {
makeArgFinder := argFinderConstructor(target, c)
forEachStaleUnion(target, c, makeArgFinder,
func(unionArg *UnionArg, unionType *UnionType, okIndices []int) {
+ if transientOnly && !unionArg.transient {
+ return
+ }
// If several union options match, take the first one.
idx := okIndices[0]
field := unionType.Fields[idx]
diff --git a/prog/minimization.go b/prog/minimization.go
index a9864aca2..1760d180f 100644
--- a/prog/minimization.go
+++ b/prog/minimization.go
@@ -281,7 +281,7 @@ func minimizeInt(ctx *minimizeArgsCtx, arg Arg, path string) bool {
// By mutating an integer, we risk violating conditional fields.
// If the fields are patched, the minimization process must be restarted.
- patched := ctx.call.setDefaultConditions(ctx.p.Target)
+ patched := ctx.call.setDefaultConditions(ctx.p.Target, false)
if ctx.pred(ctx.p, ctx.callIndex0) {
*ctx.p0 = ctx.p
ctx.triedPaths[path] = true
diff --git a/prog/rand.go b/prog/rand.go
index aca3163bd..742dbaa7c 100644
--- a/prog/rand.go
+++ b/prog/rand.go
@@ -886,7 +886,7 @@ func (a *UnionType) generate(r *randGen, s *state, dir Dir) (arg Arg, calls []*C
if a.isConditional() {
// Conditions may reference other fields that may not have already
// been generated. We'll fill them in later.
- return a.DefaultTransientArg(dir), nil
+ return a.DefaultArg(dir), nil
}
index := r.Intn(len(a.Fields))
optType, optDir := a.Fields[index].Type, a.Fields[index].Dir(dir)
diff --git a/prog/test/fuzz_test.go b/prog/test/fuzz_test.go
index f01905ea3..2d063c469 100644
--- a/prog/test/fuzz_test.go
+++ b/prog/test/fuzz_test.go
@@ -30,6 +30,9 @@ test$res2()
test$res2()
test$res2()
`,
+ `r=test$res0()
+test$recur2(&(293324893027559)={r})
+`,
} {
t.Logf("test #%v: %q", i, data)
inp := []byte(data)[:len(data):len(data)]
diff --git a/prog/types.go b/prog/types.go
index 00440163b..4e9d8455e 100644
--- a/prog/types.go
+++ b/prog/types.go
@@ -735,13 +735,9 @@ func (t *UnionType) String() string {
func (t *UnionType) DefaultArg(dir Dir) Arg {
idx := t.defaultField()
f := t.Fields[idx]
- return MakeUnionArg(t, dir, f.DefaultArg(f.Dir(dir)), idx)
-}
-
-func (t *UnionType) DefaultTransientArg(dir Dir) Arg {
- unionArg := t.DefaultArg(dir).(*UnionArg)
- unionArg.transient = true
- return unionArg
+ arg := MakeUnionArg(t, dir, f.DefaultArg(f.Dir(dir)), idx)
+ arg.transient = t.isConditional()
+ return arg
}
func (t *UnionType) defaultField() int {
diff --git a/prog/validation.go b/prog/validation.go
index 13b7c33db..f38dcd1e8 100644
--- a/prog/validation.go
+++ b/prog/validation.go
@@ -21,15 +21,25 @@ func (p *Prog) debugValidate() {
}
}
+func (p *Prog) validate() error {
+ return p.validateWithOpts(validationOptions{})
+}
+
type validCtx struct {
target *Target
+ opts validationOptions
args map[Arg]bool
uses map[Arg]Arg
}
-func (p *Prog) validate() error {
+type validationOptions struct {
+ ignoreTransient bool
+}
+
+func (p *Prog) validateWithOpts(opts validationOptions) error {
ctx := &validCtx{
target: p.Target,
+ opts: opts,
args: make(map[Arg]bool),
uses: make(map[Arg]Arg),
}
@@ -65,7 +75,7 @@ func (ctx *validCtx) validateCall(c *Call) error {
return err
}
}
- if err := c.checkConditions(ctx.target); err != nil {
+ if err := c.checkConditions(ctx.target, ctx.opts.ignoreTransient); err != nil {
return err
}
return ctx.validateRet(c)