| // Copyright 2017 syzkaller project authors. All rights reserved. |
| // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. |
| |
| package prog |
| |
| import ( |
| "encoding/hex" |
| "fmt" |
| "math/rand" |
| "reflect" |
| "sort" |
| "testing" |
| |
| "github.com/google/go-cmp/cmp" |
| "github.com/google/syzkaller/pkg/image" |
| ) |
| |
| type ConstArgTest struct { |
| name string |
| in uint64 |
| size uint64 |
| bitsize uint64 |
| comps CompMap |
| res []uint64 |
| } |
| |
| type DataArgTest struct { |
| name string |
| in string |
| comps CompMap |
| res map[string]bool |
| } |
| |
| // Tests checkConstArg(). Is not intended to check correctness of any mutations. |
| // Mutation are checked in their own tests. |
| func TestHintsCheckConstArg(t *testing.T) { |
| target := initTargetTest(t, "test", "64") |
| var tests = []ConstArgTest{ |
| { |
| name: "one-replacer-test", |
| in: 0xdeadbeef, |
| size: 4, |
| comps: CompMap{0xdeadbeef: compSet(0xdeadbeef, 0xcafebabe)}, |
| res: []uint64{0xcafebabe}, |
| }, |
| // Test for cases when there's multiple comparisons (op1, op2), (op1, op3), ... |
| // Checks that for every such operand a program is generated. |
| { |
| name: "multiple-replacers-test", |
| in: 0xabcd, |
| size: 2, |
| comps: CompMap{0xabcd: compSet(0x2, 0x3)}, |
| res: []uint64{0x2, 0x3}, |
| }, |
| // Checks that special ints are not used. |
| { |
| name: "special-ints-test", |
| in: 0xabcd, |
| size: 2, |
| comps: CompMap{0xabcd: compSet(0x1, 0x2)}, |
| res: []uint64{0x2}, |
| }, |
| |
| // The following tests check the size limits for each replacer and for the initial value |
| // of the argument. The checks are made for positive and negative values and also for bitfields. |
| { |
| name: "int8-invalid-positive-value", |
| in: 0x1234, |
| size: 1, |
| comps: CompMap{ |
| // void test8(i8 el) { |
| // i16 w = (i16) el |
| // if (w == 0x88) {...} |
| // i16 other = 0xfffe |
| // if (w == other) |
| // }; test8(i8(0x1234)); |
| 0x34: compSet(0x88, 0x1122, 0xfffffffffffffffe, 0xffffffffffffff0a), |
| // This following args should be iggnored. |
| 0x1234: compSet(0xa1), |
| 0xffffffffffffff34: compSet(0xaa), |
| }, |
| res: []uint64{0x88, 0xfe}, |
| }, |
| { |
| name: "int8-invalid-negative-value", |
| in: 0x12ab, |
| size: 1, |
| comps: CompMap{ |
| 0xab: compSet(0xab, 0xac, 0xabcd), |
| 0xffffffffffffffab: compSet(0x11, 0x22, 0xffffffffffffff34), |
| }, |
| res: []uint64{0x11, 0x22, 0xac}, |
| }, |
| { |
| name: "int16-valid-value-bitsize-12", |
| in: 0x3ab, |
| size: 2, |
| bitsize: 12, |
| comps: CompMap{ |
| 0x3ab: compSet(0x11, 0x1234, 0xfffffffffffffffe), |
| 0x13ab: compSet(0xab, 0xffa), |
| 0xffffffffffffffab: compSet(0xfffffffffffffff1), |
| 0xfffffffffffff3ab: compSet(0xff1, 0x12), |
| }, |
| res: []uint64{0x11, 0x3f1, 0xffe}, |
| }, |
| { |
| name: "int16-invalid-value-bitsize-12", |
| in: 0x71ab, |
| size: 2, |
| bitsize: 12, |
| comps: CompMap{ |
| 0x1ab: compSet(0x11, 0x1234, 0xfffffffffffffffe), |
| }, |
| res: []uint64{0x11, 0xffe}, |
| }, |
| { |
| name: "int16-negative-valid-value-bitsize-12", |
| in: 0x8ab, |
| size: 2, |
| bitsize: 12, |
| comps: CompMap{ |
| 0x8ab: compSet(0x11), |
| 0xffffffffffffffab: compSet(0x12, 0xffffffffffffff0a), |
| 0xfffffffffffff8ab: compSet(0x13, 0xffffffffffffff00), |
| }, |
| res: []uint64{0x11, 0x13, 0x80a, 0x812, 0xf00}, |
| }, |
| { |
| name: "int16-negative-invalid-value-bitsize-12", |
| in: 0x88ab, |
| size: 2, |
| bitsize: 12, |
| comps: CompMap{ |
| 0x8ab: compSet(0x13), |
| 0xfffffffffffff8ab: compSet(0x11, 0xffffffffffffff11), |
| }, |
| res: []uint64{0x11, 0x13, 0xf11}, |
| }, |
| { |
| name: "int32-invalid-value", |
| in: 0xaabaddcafe, |
| size: 4, |
| comps: CompMap{0xbaddcafe: compSet(0xab, 0xabcd, 0xbaddcafe, |
| 0xdeadbeef, 0xaabbccddeeff1122)}, |
| res: []uint64{0xab, 0xabcd, 0xdeadbeef}, |
| }, |
| { |
| name: "int64-valid-value", |
| in: 0xdeadc0debaddcafe, |
| size: 8, |
| comps: CompMap{0xdeadc0debaddcafe: compSet(0xab, 0xabcd, 0xdeadbeef, 0xdeadbeefdeadbeef)}, |
| res: []uint64{0xab, 0xabcd, 0xdeadbeef, 0xdeadbeefdeadbeef}, |
| }, |
| } |
| meta := target.SyscallMap["test$hint_int"] |
| structType := meta.Args[0].Type.(*PtrType).Elem.(*StructType) |
| types := make(map[string]Type) |
| for _, field := range structType.Fields { |
| types[field.Name] = field.Type |
| } |
| for _, test := range tests { |
| t.Run(fmt.Sprintf("%v", test.name), func(t *testing.T) { |
| var res []uint64 |
| typ := types[fmt.Sprintf("int%v_%v", test.size, test.bitsize)] |
| constArg := MakeConstArg(typ, DirIn, test.in) |
| checkConstArg(constArg, test.comps, func() { |
| res = append(res, constArg.Val) |
| }) |
| if !reflect.DeepEqual(res, test.res) { |
| t.Fatalf("\ngot : %v\nwant: %v", res, test.res) |
| } |
| }) |
| } |
| } |
| |
| // Tests checkDataArg(). Is not intended to check correctness of any mutations. |
| // Mutation are checked in their own tests. |
| func TestHintsCheckDataArg(t *testing.T) { |
| target := initTargetTest(t, "test", "64") |
| // All inputs are in Little-Endian. |
| var tests = []DataArgTest{ |
| { |
| "one-replacer-test", |
| "\xef\xbe\xad\xde", |
| CompMap{ |
| 0xdeadbeef: compSet(0xcafebabe, 0xdeadbeef), |
| 0xbeef: compSet(0xbeef), |
| 0xef: compSet(0xef), |
| }, |
| map[string]bool{ |
| "\xbe\xba\xfe\xca": true, |
| }, |
| }, |
| // Test for cases when there's multiple comparisons (op1, op2), (op1, op3), ... |
| // Checks that for every such operand a program is generated. |
| { |
| "multiple-replacers-test", |
| "\xcd\xab", |
| CompMap{0xabcd: compSet(0x2, 0x3)}, |
| map[string]bool{ |
| "\x02\x00": true, "\x03\x00": true, |
| }, |
| }, |
| // Checks that special ints are not used. |
| { |
| "special-ints-test", |
| "\xcd\xab", |
| CompMap{0xabcd: compSet(0x1, 0x2)}, |
| map[string]bool{ |
| "\x02\x00": true, |
| }, |
| }, |
| // Checks that ints of various sizes are extracted. |
| { |
| "different-sizes-test", |
| "\xef\xcd\xab\x90\x78\x56\x34\x12", |
| CompMap{ |
| 0xef: compSet(0x11), |
| 0xcdef: compSet(0x2222), |
| 0x90abcdef: compSet(0x33333333), |
| 0x1234567890abcdef: compSet(0x4444444444444444), |
| }, |
| map[string]bool{ |
| "\x11\xcd\xab\x90\x78\x56\x34\x12": true, |
| "\x22\x22\xab\x90\x78\x56\x34\x12": true, |
| "\x33\x33\x33\x33\x78\x56\x34\x12": true, |
| "\x44\x44\x44\x44\x44\x44\x44\x44": true, |
| }, |
| }, |
| // Checks that values with different offsets are extracted. |
| { |
| "different-offsets-test", |
| "\xab\xab\xab\xab\xab\xab\xab\xab\xab", |
| CompMap{ |
| 0xab: compSet(0x11), |
| 0xabab: compSet(0x2222), |
| 0xabababab: compSet(0x33333333), |
| 0xabababababababab: compSet(0x4444444444444444), |
| }, |
| map[string]bool{ |
| "\x11\xab\xab\xab\xab\xab\xab\xab\xab": true, |
| "\xab\x11\xab\xab\xab\xab\xab\xab\xab": true, |
| "\xab\xab\x11\xab\xab\xab\xab\xab\xab": true, |
| "\xab\xab\xab\x11\xab\xab\xab\xab\xab": true, |
| "\xab\xab\xab\xab\x11\xab\xab\xab\xab": true, |
| "\xab\xab\xab\xab\xab\x11\xab\xab\xab": true, |
| "\xab\xab\xab\xab\xab\xab\x11\xab\xab": true, |
| "\xab\xab\xab\xab\xab\xab\xab\x11\xab": true, |
| "\xab\xab\xab\xab\xab\xab\xab\xab\x11": true, |
| "\x22\x22\xab\xab\xab\xab\xab\xab\xab": true, |
| "\xab\x22\x22\xab\xab\xab\xab\xab\xab": true, |
| "\xab\xab\x22\x22\xab\xab\xab\xab\xab": true, |
| "\xab\xab\xab\x22\x22\xab\xab\xab\xab": true, |
| "\xab\xab\xab\xab\x22\x22\xab\xab\xab": true, |
| "\xab\xab\xab\xab\xab\x22\x22\xab\xab": true, |
| "\xab\xab\xab\xab\xab\xab\x22\x22\xab": true, |
| "\xab\xab\xab\xab\xab\xab\xab\x22\x22": true, |
| "\x33\x33\x33\x33\xab\xab\xab\xab\xab": true, |
| "\xab\x33\x33\x33\x33\xab\xab\xab\xab": true, |
| "\xab\xab\x33\x33\x33\x33\xab\xab\xab": true, |
| "\xab\xab\xab\x33\x33\x33\x33\xab\xab": true, |
| "\xab\xab\xab\xab\x33\x33\x33\x33\xab": true, |
| "\xab\xab\xab\xab\xab\x33\x33\x33\x33": true, |
| "\x44\x44\x44\x44\x44\x44\x44\x44\xab": true, |
| "\xab\x44\x44\x44\x44\x44\x44\x44\x44": true, |
| }, |
| }, |
| { |
| "replace-in-the-middle-of-a-larger-blob", |
| "\xef\xcd\xab\x90\x78\x56\x34\x12", |
| CompMap{0xffffffffffff90ab: compSet(0xffffffffffffaabb)}, |
| map[string]bool{ |
| "\xef\xcd\xbb\xaa\x78\x56\x34\x12": true, |
| }, |
| }, |
| { |
| |
| "big-endian-replace", |
| "\xef\xcd\xab\x90\x78\x56\x34\x12", |
| CompMap{ |
| // 0xff07 is reversed special int. |
| 0xefcd: compSet(0xaabb, 0xff07), |
| 0x3412: compSet(0xaabb, 0xff07), |
| 0x9078: compSet(0xaabb, 0x11223344, 0xff07), |
| 0x90785634: compSet(0xaabbccdd, 0x11223344), |
| 0xefcdab9078563412: compSet(0x1122334455667788), |
| }, |
| map[string]bool{ |
| "\xaa\xbb\xab\x90\x78\x56\x34\x12": true, |
| "\xef\xcd\xab\x90\x78\x56\xaa\xbb": true, |
| "\xef\xcd\xab\xaa\xbb\x56\x34\x12": true, |
| "\xef\xcd\xab\xaa\xbb\xcc\xdd\x12": true, |
| "\xef\xcd\xab\x11\x22\x33\x44\x12": true, |
| "\x11\x22\x33\x44\x55\x66\x77\x88": true, |
| }, |
| }, |
| } |
| meta := target.SyscallMap["test$hint_data"] |
| typ := meta.Args[0].Type.(*PtrType).Elem // array[int8] |
| for _, test := range tests { |
| t.Run(fmt.Sprintf("%v", test.name), func(t *testing.T) { |
| res := make(map[string]bool) |
| dataArg := MakeDataArg(typ, DirIn, []byte(test.in)) |
| checkDataArg(dataArg, test.comps, func() { |
| res[string(dataArg.Data())] = true |
| }) |
| if !reflect.DeepEqual(res, test.res) { |
| s := "\ngot: [" |
| for x := range res { |
| s += fmt.Sprintf("0x%x, ", x) |
| } |
| s += "]\nwant: [" |
| for x := range test.res { |
| s += fmt.Sprintf("0x%x, ", x) |
| } |
| s += "]\n" |
| t.Fatalf(s) |
| } |
| }) |
| } |
| } |
| |
| func TestHintsCompressedImage(t *testing.T) { |
| target := initTargetTest(t, "test", "64") |
| type Test struct { |
| input string |
| comps CompMap |
| output []string |
| } |
| var tests = []Test{ |
| { |
| "\x00\x11\x22\x33\x44\x55\x66\x77", |
| CompMap{ |
| // 1/2-bytes must not be replaced. |
| 0x00: compSet(0xaa), |
| 0x11: compSet(0xaa), |
| 0x1122: compSet(0xaabb), |
| 0x4455: compSet(0xaabb), |
| // Aligned 4-byte values are replaced in both little/big endian. |
| 0x00112233: compSet(0xaabbccdd), |
| 0x33221100: compSet(0xaabbccdd), |
| 0x44556677: compSet(0xaabbccdd), |
| 0x77665544: compSet(0xaabbccdd), |
| // Same for 8-byte values. |
| 0x0011223344556677: compSet(0xaabbccddeeff9988), |
| 0x7766554433221100: compSet(0xaabbccddeeff9988), |
| // Unaligned 4-bytes are not replaced. |
| 0x11223344: compSet(0xaabbccdd), |
| 0x22334455: compSet(0xaabbccdd), |
| }, |
| []string{ |
| // Mutants for 4-byte values. |
| "\xaa\xbb\xcc\xdd\x44\x55\x66\x77", |
| "\xdd\xcc\xbb\xaa\x44\x55\x66\x77", |
| "\x00\x11\x22\x33\xaa\xbb\xcc\xdd", |
| "\x00\x11\x22\x33\xdd\xcc\xbb\xaa", |
| // Mutants for 8-byte values. |
| "\xaa\xbb\xcc\xdd\xee\xff\x99\x88", |
| "\x88\x99\xff\xee\xdd\xcc\xbb\xaa", |
| }, |
| }, |
| { |
| "\x00\x11\x22\x33\x44\x55\x66\x77", |
| CompMap{ |
| // Special values are used as replacers. |
| 0x00112233: compSet(0, 0xffffffff), |
| }, |
| []string{ |
| // Mutants for 4-byte values. |
| "\x00\x00\x00\x00\x44\x55\x66\x77", |
| "\xff\xff\xff\xff\x44\x55\x66\x77", |
| }, |
| }, |
| { |
| // All 0s and 0xff must not be replaced. |
| "\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00", |
| CompMap{ |
| 0: compSet(0xaabbccdd), |
| 0xffffffffffffffff: compSet(0xaabbccddaabbccdd), |
| }, |
| nil, |
| }, |
| } |
| typ := target.SyscallMap["serialize3"].Args[0].Type.(*PtrType).Elem.(*BufferType) |
| if typ.Kind != BufferCompressed { |
| panic("wrong arg type") |
| } |
| for i, test := range tests { |
| t.Run(fmt.Sprintf("%v", i), func(t *testing.T) { |
| var res []string |
| arg := MakeDataArg(typ, DirIn, image.Compress([]byte(test.input))) |
| generateHints(test.comps, arg, func() { |
| res = append(res, string(arg.Data())) |
| }) |
| for i, compressed := range res { |
| data, dtor := image.MustDecompress([]byte(compressed)) |
| res[i] = string(data) |
| dtor() |
| } |
| sort.Strings(res) |
| sort.Strings(test.output) |
| if diff := cmp.Diff(test.output, res); diff != "" { |
| t.Fatalf("got wrong mutants: %v", diff) |
| } |
| data, dtor := image.MustDecompress(arg.Data()) |
| defer dtor() |
| if diff := cmp.Diff(test.input, string(data)); diff != "" { |
| t.Fatalf("argument got changed afterwards: %v", diff) |
| } |
| }) |
| } |
| } |
| |
| func TestHintsShrinkExpand(t *testing.T) { |
| t.Parallel() |
| // Naming conventions: |
| // b - byte variable (i8 or u8) |
| // w - word variable (i16 or u16) |
| // dw - dword variable (i32 or u32) |
| // qw - qword variable (i64 or u64) |
| // ----------------------------------------------------------------- |
| // Shrink tests: |
| var tests = []ConstArgTest{ |
| { |
| // Models the following code: |
| // void f(u16 w) { |
| // u8 b = (u8) w; |
| // if (b == 0xab) {...} |
| // if (w == 0xcdcd) {...} |
| // }; f(0x1234); |
| name: "shrink-16-test", |
| in: 0x1234, |
| comps: CompMap{ |
| 0x34: compSet(0xab), |
| 0x1234: compSet(0xcdcd), |
| }, |
| res: []uint64{0x12ab, 0xcdcd}, |
| }, |
| { |
| // Models the following code: |
| // void f(u32 dw) { |
| // u8 b = (u8) dw |
| // i16 w = (i16) dw |
| // if (b == 0xab) {...} |
| // if (w == 0xcdcd) {...} |
| // if (dw == 0xefefefef) {...} |
| // }; f(0x12345678); |
| name: "shrink-32-test", |
| in: 0x12345678, |
| comps: CompMap{ |
| 0x78: compSet(0xab), |
| 0x5678: compSet(0xcdcd), |
| 0x12345678: compSet(0xefefefef), |
| }, |
| res: []uint64{0x123456ab, 0x1234cdcd, 0xefefefef}, |
| }, |
| { |
| // Models the following code: |
| // void f(u64 qw) { |
| // u8 b = (u8) qw |
| // u16 w = (u16) qw |
| // u32 dw = (u32) qw |
| // if (b == 0xab) {...} |
| // if (w == 0xcdcd) {...} |
| // if (dw == 0xefefefef) {...} |
| // if (qw == 0x0101010101010101) {...} |
| // }; f(0x1234567890abcdef); |
| name: "shrink-64-test", |
| in: 0x1234567890abcdef, |
| comps: CompMap{ |
| 0xef: compSet(0xab, 0xef), |
| 0xcdef: compSet(0xcdcd), |
| 0x90abcdef: compSet(0xefefefef), |
| 0x1234567890abcdef: compSet(0x0101010101010101), |
| }, |
| res: []uint64{ |
| 0x0101010101010101, |
| 0x1234567890abcdab, |
| 0x1234567890abcdcd, |
| 0x12345678efefefef, |
| }, |
| }, |
| { |
| // Models the following code: |
| // void f(i16 w) { |
| // i8 b = (i8) w; |
| // i16 other = 0xabab; |
| // if (b == other) {...} |
| // }; f(0x1234); |
| // In such code the comparison will never be true, so we don't |
| // generate a hint for it. |
| name: "shrink-with-a-wider-replacer-test1", |
| in: 0x1234, |
| comps: CompMap{0x34: compSet(0x1bab)}, |
| res: nil, |
| }, |
| { |
| // Models the following code: |
| // void f(i16 w) { |
| // i8 b = (i8) w; |
| // i16 other = 0xfffd; |
| // if (b == other) {...} |
| // }; f(0x1234); |
| // In such code b will be sign extended to 0xff34 and, if we replace |
| // the lower byte, then the if statement will be true. |
| // Note that executor sign extends all the comparison operands to |
| // int64, so we model this accordingly. |
| name: "shrink-with-a-wider-replacer-test2", |
| in: 0x1234, |
| comps: CompMap{0x34: compSet(0xfffffffffffffffd)}, |
| res: []uint64{0x12fd}, |
| }, |
| // ----------------------------------------------------------------- |
| // Extend tests: |
| // Note that executor sign extends all the comparison operands to int64, |
| // so we model this accordingly. |
| { |
| // Models the following code: |
| // void f(i8 b) { |
| // i64 qw = (i64) b; |
| // if (qw == -2) {...}; |
| // }; f(-1); |
| name: "extend-8-test", |
| in: 0xff, |
| comps: CompMap{0xffffffffffffffff: compSet(0xfffffffffffffffe)}, |
| res: []uint64{0xfe}, |
| }, |
| { |
| // Models the following code: |
| // void f(i16 w) { |
| // i64 qw = (i64) w; |
| // if (qw == -2) {...}; |
| // }; f(-1); |
| name: "extend-16-test", |
| in: 0xffff, |
| comps: CompMap{0xffffffffffffffff: compSet(0xfffffffffffffffe)}, |
| res: []uint64{0xfffe}, |
| }, |
| { |
| // Models the following code: |
| // void f(i32 dw) { |
| // i64 qw = (i32) dw; |
| // if (qw == -2) {...}; |
| // }; f(-1); |
| name: "extend-32-test", |
| in: 0xffffffff, |
| comps: CompMap{0xffffffffffffffff: compSet(0xfffffffffffffffe)}, |
| res: []uint64{0xfffffffe}, |
| }, |
| { |
| // Models the following code: |
| // void f(i8 b) { |
| // i16 w = (i16) b; |
| // if (w == (i16) 0xfeff) {...}; |
| // }; f(-1); |
| // There's no value for b that will make the comparison true, |
| // so we don't generate hints. |
| name: "extend-with-a-wider-replacer-test", |
| in: 0xff, |
| comps: CompMap{0xffffffffffffffff: compSet(0xfffffffffffffeff)}, |
| res: nil, |
| }, |
| } |
| for _, test := range tests { |
| t.Run(fmt.Sprintf("%v", test.name), func(t *testing.T) { |
| res := shrinkExpand(test.in, test.comps, 64, false) |
| if !reflect.DeepEqual(res, test.res) { |
| t.Fatalf("\ngot : %v\nwant: %v", res, test.res) |
| } |
| }) |
| } |
| } |
| |
| func TestHintsRandom(t *testing.T) { |
| target, rs, iters := initTest(t) |
| ct := target.DefaultChoiceTable() |
| iters /= 10 // the test takes long |
| r := newRand(target, rs) |
| for i := 0; i < iters; i++ { |
| p := target.Generate(rs, 5, ct) |
| for i, c := range p.Calls { |
| vals := extractValues(c) |
| for j := 0; j < 5; j++ { |
| vals[r.randInt64()] = true |
| } |
| comps := make(CompMap) |
| for v := range vals { |
| comps.AddComp(v, r.randInt64()) |
| } |
| p.MutateWithHints(i, comps, func(p1 *Prog) {}) |
| } |
| } |
| } |
| |
| func extractValues(c *Call) map[uint64]bool { |
| vals := make(map[uint64]bool) |
| ForeachArg(c, func(arg Arg, _ *ArgCtx) { |
| if arg.Dir() == DirOut { |
| return |
| } |
| switch a := arg.(type) { |
| case *ConstArg: |
| vals[a.Val] = true |
| case *DataArg: |
| data := a.Data() |
| for i := range data { |
| vals[uint64(data[i])] = true |
| if i < len(data)-1 { |
| v := uint64(data[i]) | uint64(data[i+1])<<8 |
| vals[v] = true |
| } |
| if i < len(data)-3 { |
| v := uint64(data[i]) | uint64(data[i+1])<<8 | |
| uint64(data[i+2])<<16 | uint64(data[i+3])<<24 |
| vals[v] = true |
| } |
| if i < len(data)-7 { |
| v := uint64(data[i]) | uint64(data[i+1])<<8 | |
| uint64(data[i+2])<<16 | uint64(data[i+3])<<24 | |
| uint64(data[i+4])<<32 | uint64(data[i+5])<<40 | |
| uint64(data[i+6])<<48 | uint64(data[i+7])<<56 |
| vals[v] = true |
| } |
| } |
| } |
| }) |
| delete(vals, 0) // replacing 0 can yield too many condidates |
| return vals |
| } |
| |
| func TestHintsData(t *testing.T) { |
| target := initTargetTest(t, "test", "64") |
| type Test struct { |
| in string |
| comps CompMap |
| out []string |
| } |
| tests := []Test{ |
| { |
| in: "0809101112131415", |
| comps: CompMap{0x12111009: compSet(0x10)}, |
| out: []string{"0810000000131415"}, |
| }, |
| } |
| for _, test := range tests { |
| p, err := target.Deserialize([]byte(fmt.Sprintf("test$hint_data(&AUTO=\"%v\")", test.in)), Strict) |
| if err != nil { |
| t.Fatal(err) |
| } |
| var got []string |
| p.MutateWithHints(0, test.comps, func(newP *Prog) { |
| got = append(got, hex.EncodeToString( |
| newP.Calls[0].Args[0].(*PointerArg).Res.(*DataArg).Data())) |
| }) |
| sort.Strings(test.out) |
| sort.Strings(got) |
| if !reflect.DeepEqual(got, test.out) { |
| t.Fatalf("comps: %v\ninput: %v\ngot : %+v\nwant: %+v", |
| test.comps, test.in, got, test.out) |
| } |
| } |
| } |
| |
| func BenchmarkHints(b *testing.B) { |
| target, cleanup := initBench(b) |
| defer cleanup() |
| rs := rand.NewSource(0) |
| r := newRand(target, rs) |
| ct := target.DefaultChoiceTable() |
| p := target.Generate(rs, 30, ct) |
| comps := make([]CompMap, len(p.Calls)) |
| for i, c := range p.Calls { |
| vals := extractValues(c) |
| for j := 0; j < 5; j++ { |
| vals[r.randInt64()] = true |
| } |
| comps[i] = make(CompMap) |
| for v := range vals { |
| comps[i].AddComp(v, r.randInt64()) |
| } |
| } |
| b.RunParallel(func(pb *testing.PB) { |
| for pb.Next() { |
| for i := range p.Calls { |
| p.MutateWithHints(i, comps[i], func(p1 *Prog) {}) |
| } |
| } |
| }) |
| } |
| |
| func compSet(vals ...uint64) map[uint64]bool { |
| m := make(map[uint64]bool) |
| for _, v := range vals { |
| m[v] = true |
| } |
| return m |
| } |