| // Copyright 2015 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 report |
| |
| import ( |
| "fmt" |
| "testing" |
| |
| "github.com/google/syzkaller/pkg/mgrconfig" |
| "github.com/google/syzkaller/pkg/symbolizer" |
| ) |
| |
| func TestLinuxIgnores(t *testing.T) { |
| cfg := &mgrconfig.Config{ |
| TargetOS: "linux", |
| } |
| reporter, err := NewReporter(cfg) |
| if err != nil { |
| t.Fatal(err) |
| } |
| cfg.Ignores = []string{"BUG: bug3"} |
| reporter1, err := NewReporter(cfg) |
| if err != nil { |
| t.Fatal(err) |
| } |
| cfg.Ignores = []string{"BUG: bug3", "BUG: bug1"} |
| reporter2, err := NewReporter(cfg) |
| if err != nil { |
| t.Fatal(err) |
| } |
| cfg.Ignores = []string{"BUG: bug3", "BUG: bug1", "BUG: bug2"} |
| reporter3, err := NewReporter(cfg) |
| if err != nil { |
| t.Fatal(err) |
| } |
| |
| const log = ` |
| [ 0.000000] BUG: bug1 |
| [ 0.000000] BUG: bug2 |
| ` |
| if !reporter.ContainsCrash([]byte(log)) { |
| t.Fatalf("no crash") |
| } |
| if rep := reporter.Parse([]byte(log)); rep.Title != "BUG: bug1" { |
| t.Fatalf("want `BUG: bug1`, found `%v`", rep.Title) |
| } |
| |
| if !reporter1.ContainsCrash([]byte(log)) { |
| t.Fatalf("no crash") |
| } |
| if rep := reporter1.Parse([]byte(log)); rep.Title != "BUG: bug1" { |
| t.Fatalf("want `BUG: bug1`, found `%v`", rep.Title) |
| } |
| |
| if !reporter2.ContainsCrash([]byte(log)) { |
| t.Fatalf("no crash") |
| } |
| if rep := reporter2.Parse([]byte(log)); rep.Title != "BUG: bug2" { |
| t.Fatalf("want `BUG: bug2`, found `%v`", rep.Title) |
| } |
| |
| if reporter3.ContainsCrash([]byte(log)) { |
| t.Fatalf("found crash, should be ignored") |
| } |
| if rep := reporter3.Parse([]byte(log)); rep != nil { |
| t.Fatalf("found `%v`, should be ignored", rep.Title) |
| } |
| } |
| |
| func TestLinuxSymbolizeLine(t *testing.T) { |
| tests := []struct { |
| line string |
| result string |
| }{ |
| // Normal symbolization. |
| { |
| "[ 2713.153531] [<ffffffff82d1b1d9>] foo+0x101/0x185\n", |
| "[ 2713.153531] [<ffffffff82d1b1d9>] foo+0x101/0x185 foo.c:555\n", |
| }, |
| { |
| "RIP: 0010:[<ffffffff8188c0e6>] [<ffffffff8188c0e6>] foo+0x101/0x185\n", |
| "RIP: 0010:[<ffffffff8188c0e6>] [<ffffffff8188c0e6>] foo+0x101/0x185 foo.c:555\n", |
| }, |
| // Strip "./" file prefix. |
| { |
| "[ 2713.153531] [<ffffffff82d1b1d9>] foo+0x111/0x185\n", |
| "[ 2713.153531] [<ffffffff82d1b1d9>] foo+0x111/0x185 foo.h:111\n", |
| }, |
| // Needs symbolization, but symbolizer returns nothing. |
| { |
| "[ 2713.153531] [<ffffffff82d1b1d9>] foo+0x121/0x185\n", |
| "[ 2713.153531] [<ffffffff82d1b1d9>] foo+0x121/0x185\n", |
| }, |
| // Needs symbolization, but symbolizer returns error. |
| { |
| "[ 2713.153531] [<ffffffff82d1b1d9>] foo+0x131/0x185\n", |
| "[ 2713.153531] [<ffffffff82d1b1d9>] foo+0x131/0x185\n", |
| }, |
| // Needs symbolization, but symbol is missing. |
| { |
| "[ 2713.153531] [<ffffffff82d1b1d9>] bar+0x131/0x185\n", |
| "[ 2713.153531] [<ffffffff82d1b1d9>] bar+0x131/0x185\n", |
| }, |
| // Bad offset. |
| { |
| "[ 2713.153531] [<ffffffff82d1b1d9>] bar+0xffffffffffffffffffff/0x185\n", |
| "[ 2713.153531] [<ffffffff82d1b1d9>] bar+0xffffffffffffffffffff/0x185\n", |
| }, |
| // Should not be symbolized. |
| { |
| "WARNING: CPU: 2 PID: 2636 at ipc/shm.c:162 foo+0x101/0x185\n", |
| "WARNING: CPU: 2 PID: 2636 at ipc/shm.c:162 foo+0x101/0x185 foo.c:555\n", |
| }, |
| // Tricky function name. |
| { |
| " [<ffffffff84e5bea0>] do_ipv6_setsockopt.isra.7.part.3+0x101/0x2830 \n", |
| " [<ffffffff84e5bea0>] do_ipv6_setsockopt.isra.7.part.3+0x101/0x2830 net.c:111 \n", |
| }, |
| // Old KASAN frame format (with tab). |
| { |
| "[ 50.419727] baz+0x101/0x200\n", |
| "[ 50.419727] baz+0x101/0x200 baz.c:100\n", |
| }, |
| // Inlined frames. |
| { |
| " [<ffffffff84e5bea0>] foo+0x141/0x185\n", |
| " [<ffffffff84e5bea0>] inlined1 net.c:111 [inline]\n" + |
| " [<ffffffff84e5bea0>] inlined2 mm.c:222 [inline]\n" + |
| " [<ffffffff84e5bea0>] foo+0x141/0x185 kasan.c:333\n", |
| }, |
| // Several symbols with the same name. |
| { |
| "[<ffffffff82d1b1d9>] baz+0x101/0x200\n", |
| "[<ffffffff82d1b1d9>] baz+0x101/0x200 baz.c:100\n", |
| }, |
| } |
| symbols := map[string][]symbolizer.Symbol{ |
| "foo": { |
| {Addr: 0x1000000, Size: 0x190}, |
| }, |
| "do_ipv6_setsockopt.isra.7.part.3": { |
| {Addr: 0x2000000, Size: 0x2830}, |
| }, |
| "baz": { |
| {Addr: 0x3000000, Size: 0x100}, |
| {Addr: 0x4000000, Size: 0x200}, |
| {Addr: 0x5000000, Size: 0x300}, |
| }, |
| } |
| symb := func(bin string, pc uint64) ([]symbolizer.Frame, error) { |
| if bin != "vmlinux" { |
| return nil, fmt.Errorf("unknown pc 0x%x", pc) |
| } |
| switch pc { |
| case 0x1000100: |
| return []symbolizer.Frame{ |
| { |
| File: "/linux/foo.c", |
| Line: 555, |
| }, |
| }, nil |
| case 0x1000110: |
| return []symbolizer.Frame{ |
| { |
| File: "/linux/./foo.h", |
| Line: 111, |
| }, |
| }, nil |
| case 0x1000120: |
| return nil, nil |
| case 0x1000130: |
| return nil, fmt.Errorf("unknown pc 0x%x", pc) |
| case 0x2000100: |
| return []symbolizer.Frame{ |
| { |
| File: "/linux/net.c", |
| Line: 111, |
| }, |
| }, nil |
| case 0x1000140: |
| return []symbolizer.Frame{ |
| { |
| Func: "inlined1", |
| File: "/linux/net.c", |
| Line: 111, |
| Inline: true, |
| }, |
| { |
| Func: "inlined2", |
| File: "/linux/mm.c", |
| Line: 222, |
| Inline: true, |
| }, |
| { |
| Func: "noninlined3", |
| File: "/linux/kasan.c", |
| Line: 333, |
| Inline: false, |
| }, |
| }, nil |
| case 0x4000100: |
| return []symbolizer.Frame{ |
| { |
| File: "/linux/baz.c", |
| Line: 100, |
| }, |
| }, nil |
| default: |
| return nil, fmt.Errorf("unknown pc 0x%x", pc) |
| } |
| } |
| for i, test := range tests { |
| t.Run(fmt.Sprint(i), func(t *testing.T) { |
| result := symbolizeLine(symb, symbols, "vmlinux", "/linux/", []byte(test.line)) |
| if test.result != string(result) { |
| t.Errorf("want %q\n\t get %q", test.result, string(result)) |
| } |
| }) |
| } |
| } |