diff options
| author | Jiao, Joey <quic_jiangenj@quicinc.com> | 2024-11-06 13:41:21 +0800 |
|---|---|---|
| committer | Aleksandr Nogikh <nogikh@google.com> | 2024-11-13 11:16:59 +0000 |
| commit | 4dfba277487a7023ab9f5783302da4a9b5e9bef8 (patch) | |
| tree | 72c5a0b86f7e38922230b4421ebe50c5654e5cdc | |
| parent | bb3f84250514d5990939e57b5d1ff8badc566033 (diff) | |
all: support || operator in syzlang if condition
ex. f3 field has logic or operator in if condition:
conditional_struct {
mask int32
f1 field1 (if[value[mask] & FIELD_FLAG1])
f2 int64 (if[value[mask] & FIELD_FLAG2])
f3 int64 (if[value[mask] == FIELD_FLAG1 || value[mask] == FIELD_FLAG2])
} [packed]
| -rw-r--r-- | docs/syscall_descriptions_syntax.md | 3 | ||||
| -rw-r--r-- | pkg/ast/ast.go | 1 | ||||
| -rw-r--r-- | pkg/ast/format.go | 2 | ||||
| -rw-r--r-- | pkg/ast/parser.go | 11 | ||||
| -rw-r--r-- | pkg/ast/scanner.go | 4 | ||||
| -rw-r--r-- | pkg/ast/testdata/all.txt | 3 | ||||
| -rw-r--r-- | pkg/ast/testdata/errors.txt | 8 | ||||
| -rw-r--r-- | pkg/compiler/gen.go | 1 | ||||
| -rw-r--r-- | pkg/fuzzer/job_test.go | 2 | ||||
| -rw-r--r-- | prog/expr.go | 5 | ||||
| -rw-r--r-- | prog/expr_test.go | 23 | ||||
| -rw-r--r-- | prog/types.go | 1 | ||||
| -rw-r--r-- | sys/test/expressions.txt | 1 | ||||
| -rw-r--r-- | sys/test/test/expressions | 8 |
14 files changed, 54 insertions, 19 deletions
diff --git a/docs/syscall_descriptions_syntax.md b/docs/syscall_descriptions_syntax.md index 9ffc827dc..63c86cc93 100644 --- a/docs/syscall_descriptions_syntax.md +++ b/docs/syscall_descriptions_syntax.md @@ -543,7 +543,7 @@ whose condition is satisfied. ### Expression syntax -Currently, only `==`, `!=` and `&` operators are supported. However, the +Currently, only `==`, `!=`, `&` and `||` operators are supported. However, the functionality was designed in such a way that adding more operators is easy. Feel free to file a GitHub issue or write us an email in case it's needed. @@ -565,6 +565,7 @@ struct { f3 sub_struct f4 int (if[value[f2] == 0x2]) # Reference a sibling field. f5 int (if[value[f3:f0] == 0x1]) # Reference a nested field. + f6 int (if[value[f3:f0] == 0x1 || value[f3:f0] == 0x2]) # Reference a nested field which either equals to 0x1 or 0x2. } [packed] call(a ptr[in, struct]) diff --git a/pkg/ast/ast.go b/pkg/ast/ast.go index 2726af602..bd2d97c23 100644 --- a/pkg/ast/ast.go +++ b/pkg/ast/ast.go @@ -258,6 +258,7 @@ const ( OperatorCompareEq = iota + 1 OperatorCompareNeq OperatorBinaryAnd + OperatorOr ) type BinaryExpression struct { diff --git a/pkg/ast/format.go b/pkg/ast/format.go index 4b54466c2..b16a62531 100644 --- a/pkg/ast/format.go +++ b/pkg/ast/format.go @@ -273,6 +273,8 @@ func fmtExpressionRec(sb *strings.Builder, t *Type, parentPrio int) { sb.WriteString("!=") case OperatorBinaryAnd: sb.WriteString("&") + case OperatorOr: + sb.WriteString("||") default: panic(fmt.Sprintf("unknown operator %q", be.Operator)) } diff --git a/pkg/ast/parser.go b/pkg/ast/parser.go index 839d8e6d6..273a4889f 100644 --- a/pkg/ast/parser.go +++ b/pkg/ast/parser.go @@ -438,17 +438,18 @@ type operatorInfo struct { prio int } -const maxOperatorPrio = 1 +const maxOperatorPrio = 2 // The highest priority is 0. var binaryOperators = map[token]operatorInfo{ - tokCmpEq: {op: OperatorCompareEq, prio: 0}, - tokCmpNeq: {op: OperatorCompareNeq, prio: 0}, - tokBinAnd: {op: OperatorBinaryAnd, prio: 1}, + tokOr: {op: OperatorOr, prio: 0}, + tokCmpEq: {op: OperatorCompareEq, prio: 1}, + tokCmpNeq: {op: OperatorCompareNeq, prio: 1}, + tokBinAnd: {op: OperatorBinaryAnd, prio: 2}, } // Parse out a single Type object, which can either be a plain object or an expression. -// For now, only expressions constructed via '(', ')', "==", "!=", '&' are supported. +// For now, only expressions constructed via '(', ')', "==", "!=", '&', '||' are supported. func (p *parser) parseType() *Type { return p.parseBinaryExpr(0) } diff --git a/pkg/ast/scanner.go b/pkg/ast/scanner.go index aaabb5bcc..9a5becf1c 100644 --- a/pkg/ast/scanner.go +++ b/pkg/ast/scanner.go @@ -39,6 +39,7 @@ const ( tokBinAnd tokCmpEq tokCmpNeq + tokOr tokEOF ) @@ -73,6 +74,7 @@ var tok2str = [...]string{ tokEOF: "EOF", tokCmpEq: "==", tokCmpNeq: "!=", + tokOr: "||", } func init() { @@ -192,6 +194,8 @@ func (s *scanner) Scan() (tok token, lit string, pos Pos) { tok = tokCmpEq case s.tryConsume("!="): tok = tokCmpNeq + case s.tryConsume("||"): + tok = tokOr default: tok = punctuation[s.ch] if tok == tokIllegal { diff --git a/pkg/ast/testdata/all.txt b/pkg/ast/testdata/all.txt index a274b3eff..d5587c027 100644 --- a/pkg/ast/testdata/all.txt +++ b/pkg/ast/testdata/all.txt @@ -14,6 +14,7 @@ expressions { f1 int8 (if[X & Y == Z]) f2 int8 (if[X & Y & Z == value[X] & A]) f3 int8 (if[X & (A == B) & Z != C]) + f5 int8 (if[value[X] == A || value[X] == B]) } intflags = 1, 2, 3, 4 @@ -29,4 +30,6 @@ condFields { f2 int16 (out, if[val[mask] & SOME_CONST == OTHER_CONST]) f3 int16 (out, if[val[mask] & SOME_CONST & OTHER_CONST == val[mask] & CONST_X]) f4 int16 (out, if[val[flags] & SOME_CONST]) + f5 int16 (out, if[val[flags] == SOME_CONST || val[flags] == OTHER_CONST]) + f6 int16 (out, if[val[flags] == SOME_CONST || val[flags] & OTHER_CONST]) } diff --git a/pkg/ast/testdata/errors.txt b/pkg/ast/testdata/errors.txt index b3d9e7f52..a8a4aa197 100644 --- a/pkg/ast/testdata/errors.txt +++ b/pkg/ast/testdata/errors.txt @@ -75,6 +75,14 @@ sCondFieldsError2 { f5 int16 (out, if[val[mask] & == val[mask]]) ### unexpected ==, expecting int, identifier, string } ### unexpected '}', expecting comment, define, include, resource, identifier +sCondFieldsError3 { + f6 int16 (out, if[val[mask] == SOME_CONST] || [val[mask]]) ### unexpected '[', expecting int, identifier, string +} ### unexpected '}', expecting comment, define, include, resource, identifier + +sCondFieldsError4 { + f7 int16 (out, if[val[mask] == SOME_CONST || val[mask] ==]) ### unexpected ']', expecting int, identifier, string +} ### unexpected '}', expecting comment, define, include, resource, identifier + type mybool8 int8 type net_port proc[1, 2, int16be] type mybool16 ### unexpected '\n', expecting '[', identifier diff --git a/pkg/compiler/gen.go b/pkg/compiler/gen.go index 891cd1644..104c2899b 100644 --- a/pkg/compiler/gen.go +++ b/pkg/compiler/gen.go @@ -608,6 +608,7 @@ var binaryOperatorMap = map[ast.Operator]prog.BinaryOperator{ ast.OperatorCompareEq: prog.OperatorCompareEq, ast.OperatorCompareNeq: prog.OperatorCompareNeq, ast.OperatorBinaryAnd: prog.OperatorBinaryAnd, + ast.OperatorOr: prog.OperatorOr, } func (comp *compiler) genExpression(t *ast.Type) prog.Expression { diff --git a/pkg/fuzzer/job_test.go b/pkg/fuzzer/job_test.go index d7c07b9d1..edb68fdc9 100644 --- a/pkg/fuzzer/job_test.go +++ b/pkg/fuzzer/job_test.go @@ -77,7 +77,7 @@ func TestDeflake(t *testing.T) { target, err := prog.GetTarget(targets.TestOS, targets.TestArch64Fuzz) assert.NoError(t, err) - const anyTestProg = `syz_compare(&AUTO="00000000", 0x4, &AUTO=@conditional={0x0, @void, @void}, AUTO)` + const anyTestProg = `syz_compare(&AUTO="00000000", 0x4, &AUTO=@conditional={0x0, @void, @void, @void}, AUTO)` prog, err := target.Deserialize([]byte(anyTestProg), prog.NonStrict) assert.NoError(t, err) diff --git a/prog/expr.go b/prog/expr.go index e9446ab2c..87fb056a9 100644 --- a/prog/expr.go +++ b/prog/expr.go @@ -30,6 +30,11 @@ func (bo *BinaryExpression) Evaluate(finder ArgFinder) (uint64, bool) { return 0, true case OperatorBinaryAnd: return left & right, true + case OperatorOr: + if left != 0 || right != 0 { + return 1, true + } + return 0, true } panic(fmt.Sprintf("unknown operator %q", bo.Operator)) } diff --git a/prog/expr_test.go b/prog/expr_test.go index 74818ea55..1558248f5 100644 --- a/prog/expr_test.go +++ b/prog/expr_test.go @@ -92,13 +92,20 @@ func TestEvaluateConditionalFields(t *testing.T) { }{ { good: []string{ - `test$conditional_struct(&AUTO={0x0, @void, @void})`, - `test$conditional_struct(&AUTO={0x4, @void, @value=0x123})`, - `test$conditional_struct(&AUTO={0x6, @value={AUTO}, @value=0x123})`, + `test$conditional_struct(&AUTO={0x0, @void, @void, @void})`, + `test$conditional_struct(&AUTO={0x2, @value={AUTO}, @void, @value=0x456})`, + `test$conditional_struct(&AUTO={0x4, @void, @value=0x123, @value=0x456})`, + `test$conditional_struct(&AUTO={0x6, @value={AUTO}, @value=0x123, @void})`, }, bad: []string{ - `test$conditional_struct(&AUTO={0x0, @void, @value=0x123})`, - `test$conditional_struct(&AUTO={0x0, @value={AUTO}, @value=0x123})`, + `test$conditional_struct(&AUTO={0x0, @void, @void, @value=0x123})`, + `test$conditional_struct(&AUTO={0x0, @value={AUTO}, @void, @value=0x123})`, + `test$conditional_struct(&AUTO={0x0, @void, @value=0x123, @void})`, + `test$conditional_struct(&AUTO={0x0, @void, @void, @value=0x123})`, + `test$conditional_struct(&AUTO={0x2, @value={AUTO}, @void, @void})`, + `test$conditional_struct(&AUTO={0x2, @value={AUTO}, @value=0x123, @void})`, + `test$conditional_struct(&AUTO={0x4, @void, @value=0x123, @void})`, + `test$conditional_struct(&AUTO={0x4, @void, @void, @value=0x123})`, }, }, { @@ -141,14 +148,14 @@ func TestConditionalMinimize(t *testing.T) { output string }{ { - input: `test$conditional_struct(&AUTO={0x6, @value={AUTO}, @value=0x123})`, + input: `test$conditional_struct(&AUTO={0x6, @value={AUTO}, @value=0x123, @void})`, pred: func(p *Prog, _ int) bool { return len(p.Calls) == 1 && p.Calls[0].Meta.Name == `test$conditional_struct` }, output: `test$conditional_struct(0x0)`, }, { - input: `test$conditional_struct(&(0x7f0000000040)={0x6, @value, @value=0x123})`, + input: `test$conditional_struct(&(0x7f0000000040)={0x6, @value, @value=0x123, @void})`, pred: func(p *Prog, _ int) bool { return bytes.Contains(p.Serialize(), []byte("0x123")) }, @@ -236,7 +243,7 @@ func parseConditionalStructCall(t *testing.T, c *Call) (bool, bool) { if !ok { t.Fatalf("expected GroupArg: %v", va.Res) } - if len(ga.Inner) != 3 { + if len(ga.Inner) != 4 { t.Fatalf("wrong number of struct args %v", len(ga.Inner)) } mask := ga.Inner[0].(*ConstArg).Val diff --git a/prog/types.go b/prog/types.go index 0d6fa1596..5f2360946 100644 --- a/prog/types.go +++ b/prog/types.go @@ -110,6 +110,7 @@ const ( OperatorCompareEq BinaryOperator = iota OperatorCompareNeq OperatorBinaryAnd + OperatorOr ) type BinaryExpression struct { diff --git a/sys/test/expressions.txt b/sys/test/expressions.txt index 500bdec20..c2ed572e4 100644 --- a/sys/test/expressions.txt +++ b/sys/test/expressions.txt @@ -14,6 +14,7 @@ conditional_struct { mask int32 f1 field1 (if[value[mask] & FIELD_FLAG1]) f2 int64 (if[value[mask] & FIELD_FLAG2]) + f3 int32 (if[value[mask] == FIELD_FLAG1 || value[mask] == FIELD_FLAG2]) } [packed] resource some_res[int32] diff --git a/sys/test/test/expressions b/sys/test/test/expressions index 63100c05c..0625be415 100644 --- a/sys/test/test/expressions +++ b/sys/test/test/expressions @@ -1,9 +1,9 @@ # requires: -bigendian -syz_compare(&AUTO="00000000", 0x4, &AUTO=@conditional={0x0, @void, @void}, AUTO) -syz_compare(&AUTO="02000000ffffffff", 0x8, &AUTO=@conditional={0x2, @value={AUTO}, @void}, AUTO) -syz_compare(&AUTO="04000000aaaa000000000000", 0xc, &AUTO=@conditional={0x4, @void, @value=0xaaaa}, AUTO) -syz_compare(&AUTO="06000000ffffffffaaaa000000000000", 0x10, &AUTO=@conditional={0x6, @value={AUTO}, @value=0xaaaa}, AUTO) +syz_compare(&AUTO="00000000", 0x4, &AUTO=@conditional={0x0, @void, @void, @void}, AUTO) +syz_compare(&AUTO="02000000ffffffff56340000", 0xc, &AUTO=@conditional={0x2, @value={AUTO}, @void, @value=0x3456}, AUTO) +syz_compare(&AUTO="04000000aaaa00000000000056340000", 0x10, &AUTO=@conditional={0x4, @void, @value=0xaaaa, @value=0x3456}, AUTO) +syz_compare(&AUTO="06000000ffffffffaaaa000000000000", 0x10, &AUTO=@conditional={0x6, @value={AUTO}, @value=0xaaaa, @void}, AUTO) syz_compare(&AUTO="00ff0000", 0x4, &AUTO=@conditional2={0x0, @void, 0xff}, AUTO) syz_compare(&AUTO="0134120000ff0000", 0x8, &AUTO=@conditional2={0x1, @value=0x1234, 0xff}, AUTO) syz_compare(&AUTO="1100220000330000", 0x8, &AUTO=@conditional3={0x11, {0x0, @void, 0x22}, 0x33}, AUTO) |
