aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJiao, Joey <quic_jiangenj@quicinc.com>2024-11-06 13:41:21 +0800
committerAleksandr Nogikh <nogikh@google.com>2024-11-13 11:16:59 +0000
commit4dfba277487a7023ab9f5783302da4a9b5e9bef8 (patch)
tree72c5a0b86f7e38922230b4421ebe50c5654e5cdc
parentbb3f84250514d5990939e57b5d1ff8badc566033 (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.md3
-rw-r--r--pkg/ast/ast.go1
-rw-r--r--pkg/ast/format.go2
-rw-r--r--pkg/ast/parser.go11
-rw-r--r--pkg/ast/scanner.go4
-rw-r--r--pkg/ast/testdata/all.txt3
-rw-r--r--pkg/ast/testdata/errors.txt8
-rw-r--r--pkg/compiler/gen.go1
-rw-r--r--pkg/fuzzer/job_test.go2
-rw-r--r--prog/expr.go5
-rw-r--r--prog/expr_test.go23
-rw-r--r--prog/types.go1
-rw-r--r--sys/test/expressions.txt1
-rw-r--r--sys/test/test/expressions8
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)