diff options
| author | Paul Chaignon <paul.chaignon@gmail.com> | 2023-10-05 12:32:04 +0200 |
|---|---|---|
| committer | Aleksandr Nogikh <nogikh@google.com> | 2023-10-09 10:22:31 +0000 |
| commit | 7236594a2c63f3be360ed0a3feb63b4621530e27 (patch) | |
| tree | 721ff6a37c87fb7435fb1bbe1281926b8529ae9c | |
| parent | 5e837c76f52db819969341086d19d650156d1dc1 (diff) | |
prog: skip optional input resources
If trying to fuzz only bpf$PROG_LOAD, the executors fail with:
SYZFATAL: Manager.Check call failed: machine check failed: all
system calls are disabled
That is happening because it detects a dependency on fd_bpf_map via two
paths:
1. bpf_prog_t.fd_array is an optional pointer to an array of fd_bpf_map.
2. The bpf_insn union contains descriptions for two instructions,
bpf_insn_map_fd and bpf_insn_map_value, that reference fd_bpf_map.
Both of those cases point to optional uses of fd_bpf_map, but syzkaller
isn't able to recognize that today.
This commit addresses the first case, when a resource or one of the
types using it are explicitly marked as optional. Before this commit,
syzkaller was only able to recognize the case where the resource itself
is marked as optional. However, in the case of e.g. bpf_prog_t.fd_array,
it's the pointer to the array of fd_bpf_map that is marked optional.
To fix this, we propagate the optional bit when walking down the AST. We
then pass this propagated bit to the callback function via the context.
This change was tested on the above bpf$PROG_LOAD case 1, by removing
bpf_insn_map_fd and bpf_insn_map_value from the bpf(2) description to
avoid hitting case 2. Addressing case 2 will require more changes to the
same logic.
Signed-off-by: Paul Chaignon <paul.chaignon@gmail.com>
| -rw-r--r-- | prog/resources.go | 2 | ||||
| -rw-r--r-- | prog/resources_test.go | 2 | ||||
| -rw-r--r-- | prog/types.go | 30 |
3 files changed, 19 insertions, 15 deletions
diff --git a/prog/resources.go b/prog/resources.go index 95480a8a0..61b30581f 100644 --- a/prog/resources.go +++ b/prog/resources.go @@ -151,7 +151,7 @@ func (target *Target) getInputResources(c *Syscall) []*ResourceDesc { } switch typ1 := typ.(type) { case *ResourceType: - if !typ1.IsOptional && !dedup[typ1.Desc] { + if !ctx.Optional && !dedup[typ1.Desc] { dedup[typ1.Desc] = true resources = append(resources, typ1.Desc) } diff --git a/prog/resources_test.go b/prog/resources_test.go index e48efa65c..f0ba81adf 100644 --- a/prog/resources_test.go +++ b/prog/resources_test.go @@ -153,7 +153,7 @@ func testCreateResource(t *testing.T, target *Target, calls map[*Syscall]bool, r if res, ok := typ.(*ResourceType); ok && ctx.Dir != DirOut { s := newState(target, ct, nil) arg, calls := r.createResource(s, res, DirIn) - if arg == nil && !res.Optional() { + if arg == nil && !ctx.Optional { t.Fatalf("failed to create resource %v", res.Name()) } if arg != nil && len(calls) == 0 { diff --git a/prog/types.go b/prog/types.go index 4fd6a9daa..45136f079 100644 --- a/prog/types.go +++ b/prog/types.go @@ -680,10 +680,11 @@ type ConstValue struct { } type TypeCtx struct { - Meta *Syscall - Dir Dir - Ptr *Type - Stop bool // If set by the callback, subtypes of this type are not visited. + Meta *Syscall + Dir Dir + Ptr *Type + Optional bool + Stop bool // If set by the callback, subtypes of this type are not visited. } func ForeachType(syscalls []*Syscall, f func(t Type, ctx *TypeCtx)) { @@ -707,9 +708,12 @@ func foreachTypeImpl(meta *Syscall, preorder bool, f func(t Type, ctx *TypeCtx)) // It would prune recursion more (across syscalls), but lots of users need to // visit each struct per-syscall (e.g. prio, used resources). seen := make(map[Type]bool) - var rec func(*Type, Dir) - rec = func(ptr *Type, dir Dir) { - ctx := &TypeCtx{Meta: meta, Dir: dir, Ptr: ptr} + var rec func(*Type, Dir, bool) + rec = func(ptr *Type, dir Dir, optional bool) { + if _, ref := (*ptr).(Ref); !ref { + optional = optional || (*ptr).Optional() + } + ctx := &TypeCtx{Meta: meta, Dir: dir, Ptr: ptr, Optional: optional} if preorder { f(*ptr, ctx) if ctx.Stop { @@ -718,16 +722,16 @@ func foreachTypeImpl(meta *Syscall, preorder bool, f func(t Type, ctx *TypeCtx)) } switch a := (*ptr).(type) { case *PtrType: - rec(&a.Elem, a.ElemDir) + rec(&a.Elem, a.ElemDir, optional) case *ArrayType: - rec(&a.Elem, dir) + rec(&a.Elem, dir, optional) case *StructType: if seen[a] { break // prune recursion via pointers to structs/unions } seen[a] = true for i, f := range a.Fields { - rec(&a.Fields[i].Type, f.Dir(dir)) + rec(&a.Fields[i].Type, f.Dir(dir), optional) } case *UnionType: if seen[a] { @@ -735,7 +739,7 @@ func foreachTypeImpl(meta *Syscall, preorder bool, f func(t Type, ctx *TypeCtx)) } seen[a] = true for i, f := range a.Fields { - rec(&a.Fields[i].Type, f.Dir(dir)) + rec(&a.Fields[i].Type, f.Dir(dir), optional) } case *ResourceType, *BufferType, *VmaType, *LenType, *FlagsType, *ConstType, *IntType, *ProcType, *CsumType: @@ -752,10 +756,10 @@ func foreachTypeImpl(meta *Syscall, preorder bool, f func(t Type, ctx *TypeCtx)) } } for i := range meta.Args { - rec(&meta.Args[i].Type, DirIn) + rec(&meta.Args[i].Type, DirIn, false) } if meta.Ret != nil { - rec(&meta.Ret, DirOut) + rec(&meta.Ret, DirOut, false) } } |
