mirror of
https://github.com/reactos/syzkaller.git
synced 2024-12-03 08:50:55 +00:00
181 lines
4.3 KiB
Go
181 lines
4.3 KiB
Go
// 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.
|
|
|
|
// Conservative resource-related analysis of programs.
|
|
// The analysis figures out what files descriptors are [potentially] opened
|
|
// at a particular point in program, what pages are [potentially] mapped,
|
|
// what files were already referenced in calls, etc.
|
|
|
|
package prog
|
|
|
|
import (
|
|
"fmt"
|
|
)
|
|
|
|
const (
|
|
maxPages = 4 << 10
|
|
)
|
|
|
|
type state struct {
|
|
target *Target
|
|
ct *ChoiceTable
|
|
files map[string]bool
|
|
resources map[string][]Arg
|
|
strings map[string]bool
|
|
pages [maxPages]bool
|
|
}
|
|
|
|
// analyze analyzes the program p up to but not including call c.
|
|
func analyze(ct *ChoiceTable, p *Prog, c *Call) *state {
|
|
s := newState(p.Target, ct)
|
|
for _, c1 := range p.Calls {
|
|
if c1 == c {
|
|
break
|
|
}
|
|
s.analyze(c1)
|
|
}
|
|
return s
|
|
}
|
|
|
|
func newState(target *Target, ct *ChoiceTable) *state {
|
|
s := &state{
|
|
target: target,
|
|
ct: ct,
|
|
files: make(map[string]bool),
|
|
resources: make(map[string][]Arg),
|
|
strings: make(map[string]bool),
|
|
}
|
|
return s
|
|
}
|
|
|
|
func (s *state) analyze(c *Call) {
|
|
foreachArgArray(&c.Args, c.Ret, func(arg, base Arg, _ *[]Arg) {
|
|
switch typ := arg.Type().(type) {
|
|
case *ResourceType:
|
|
if typ.Dir() != DirIn {
|
|
s.resources[typ.Desc.Name] = append(s.resources[typ.Desc.Name], arg)
|
|
// TODO: negative PIDs and add them as well (that's process groups).
|
|
}
|
|
case *BufferType:
|
|
a := arg.(*DataArg)
|
|
if typ.Dir() != DirOut && len(a.Data()) != 0 {
|
|
switch typ.Kind {
|
|
case BufferString:
|
|
s.strings[string(a.Data())] = true
|
|
case BufferFilename:
|
|
s.files[string(a.Data())] = true
|
|
}
|
|
}
|
|
}
|
|
})
|
|
start, npages, mapped := s.target.AnalyzeMmap(c)
|
|
if npages != 0 {
|
|
if start+npages > uint64(len(s.pages)) {
|
|
panic(fmt.Sprintf("address is out of bounds: page=%v len=%v bound=%v",
|
|
start, npages, len(s.pages)))
|
|
}
|
|
for i := uint64(0); i < npages; i++ {
|
|
s.pages[start+i] = mapped
|
|
}
|
|
}
|
|
}
|
|
|
|
func foreachSubargImpl(arg Arg, parent *[]Arg, f func(arg, base Arg, parent *[]Arg)) {
|
|
var rec func(arg, base Arg, parent *[]Arg)
|
|
rec = func(arg, base Arg, parent *[]Arg) {
|
|
f(arg, base, parent)
|
|
switch a := arg.(type) {
|
|
case *GroupArg:
|
|
for _, arg1 := range a.Inner {
|
|
parent1 := parent
|
|
if _, ok := arg.Type().(*StructType); ok {
|
|
parent1 = &a.Inner
|
|
}
|
|
rec(arg1, base, parent1)
|
|
}
|
|
case *PointerArg:
|
|
if a.Res != nil {
|
|
rec(a.Res, arg, parent)
|
|
}
|
|
case *UnionArg:
|
|
rec(a.Option, base, parent)
|
|
}
|
|
}
|
|
rec(arg, nil, parent)
|
|
}
|
|
|
|
func ForeachSubarg(arg Arg, f func(arg, base Arg, parent *[]Arg)) {
|
|
foreachSubargImpl(arg, nil, f)
|
|
}
|
|
|
|
func foreachArgArray(args *[]Arg, ret Arg, f func(arg, base Arg, parent *[]Arg)) {
|
|
for _, arg := range *args {
|
|
foreachSubargImpl(arg, args, f)
|
|
}
|
|
if ret != nil {
|
|
foreachSubargImpl(ret, nil, f)
|
|
}
|
|
}
|
|
|
|
func foreachArg(c *Call, f func(arg, base Arg, parent *[]Arg)) {
|
|
foreachArgArray(&c.Args, nil, f)
|
|
}
|
|
|
|
func foreachSubargOffset(arg Arg, f func(arg Arg, offset uint64)) {
|
|
var rec func(Arg, uint64) uint64
|
|
rec = func(arg1 Arg, offset uint64) uint64 {
|
|
switch a := arg1.(type) {
|
|
case *GroupArg:
|
|
f(arg1, offset)
|
|
var totalSize uint64
|
|
for _, arg2 := range a.Inner {
|
|
size := rec(arg2, offset)
|
|
if !arg2.Type().BitfieldMiddle() {
|
|
offset += size
|
|
totalSize += size
|
|
}
|
|
}
|
|
if totalSize > arg1.Size() {
|
|
panic(fmt.Sprintf("bad group arg size %v, should be <= %v for %+v", totalSize, arg1.Size(), arg1))
|
|
}
|
|
case *UnionArg:
|
|
f(arg1, offset)
|
|
size := rec(a.Option, offset)
|
|
offset += size
|
|
if size > arg1.Size() {
|
|
panic(fmt.Sprintf("bad union arg size %v, should be <= %v for arg %+v with type %+v", size, arg1.Size(), arg1, arg1.Type()))
|
|
}
|
|
default:
|
|
f(arg1, offset)
|
|
}
|
|
return arg1.Size()
|
|
}
|
|
rec(arg, 0)
|
|
}
|
|
|
|
func RequiresBitmasks(p *Prog) bool {
|
|
result := false
|
|
for _, c := range p.Calls {
|
|
foreachArg(c, func(arg, _ Arg, _ *[]Arg) {
|
|
if a, ok := arg.(*ConstArg); ok {
|
|
if a.Type().BitfieldOffset() != 0 || a.Type().BitfieldLength() != 0 {
|
|
result = true
|
|
}
|
|
}
|
|
})
|
|
}
|
|
return result
|
|
}
|
|
|
|
func RequiresChecksums(p *Prog) bool {
|
|
result := false
|
|
for _, c := range p.Calls {
|
|
foreachArg(c, func(arg, _ Arg, _ *[]Arg) {
|
|
if _, ok := arg.Type().(*CsumType); ok {
|
|
result = true
|
|
}
|
|
})
|
|
}
|
|
return result
|
|
}
|