mirror of
https://github.com/reactos/syzkaller-ros.git
synced 2025-03-03 17:08:10 +00:00
ipc, prog, fuzzer, execprog: add hints generation code
A hint is basically a tuple consisting of a pointer to an argument in one of the syscalls of a program and a value, which should be assigned to that argument. A simplified version of hints workflow looks like this: 1. Fuzzer launches a program and collects all the comparisons' data for every syscall in the program. 2. Next it tries to match the obtained comparison operands' values vs. the input arguments' values. 3. For every such match the fuzzer mutates the program by replacing the pointed argument with the saved value. 4. If a valid program is obtained, then fuzzer launches it and checks if new coverage is obtained. This commit includes: 1. All the code related to hints generation, parsing and mutations. 2. Fuzzer functions to launch the process. 3. Some new stats gathered by fuzzer and manager, related to hints. 4. An updated version of execprog to test the hints process.
This commit is contained in:
parent
07c84b670b
commit
49c11eb514
@ -4,14 +4,19 @@
|
||||
package prog
|
||||
|
||||
func (p *Prog) Clone() *Prog {
|
||||
p1, _ := p.cloneImpl(false)
|
||||
return p1
|
||||
}
|
||||
|
||||
func (p *Prog) cloneImpl(full bool) (*Prog, map[Arg]Arg) {
|
||||
p1 := new(Prog)
|
||||
newargs := make(map[Arg]Arg)
|
||||
for _, c := range p.Calls {
|
||||
c1 := new(Call)
|
||||
c1.Meta = c.Meta
|
||||
c1.Ret = clone(c.Ret, newargs)
|
||||
c1.Ret = clone(c.Ret, newargs, full)
|
||||
for _, arg := range c.Args {
|
||||
c1.Args = append(c1.Args, clone(arg, newargs))
|
||||
c1.Args = append(c1.Args, clone(arg, newargs, full))
|
||||
}
|
||||
p1.Calls = append(p1.Calls, c1)
|
||||
}
|
||||
@ -20,10 +25,10 @@ func (p *Prog) Clone() *Prog {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
return p1
|
||||
return p1, newargs
|
||||
}
|
||||
|
||||
func clone(arg Arg, newargs map[Arg]Arg) Arg {
|
||||
func clone(arg Arg, newargs map[Arg]Arg, full bool) Arg {
|
||||
var arg1 Arg
|
||||
switch a := arg.(type) {
|
||||
case *ConstArg:
|
||||
@ -35,7 +40,7 @@ func clone(arg Arg, newargs map[Arg]Arg) Arg {
|
||||
*a1 = *a
|
||||
arg1 = a1
|
||||
if a.Res != nil {
|
||||
a1.Res = clone(a.Res, newargs)
|
||||
a1.Res = clone(a.Res, newargs, full)
|
||||
}
|
||||
case *DataArg:
|
||||
a1 := new(DataArg)
|
||||
@ -48,13 +53,13 @@ func clone(arg Arg, newargs map[Arg]Arg) Arg {
|
||||
arg1 = a1
|
||||
a1.Inner = nil
|
||||
for _, arg2 := range a.Inner {
|
||||
a1.Inner = append(a1.Inner, clone(arg2, newargs))
|
||||
a1.Inner = append(a1.Inner, clone(arg2, newargs, full))
|
||||
}
|
||||
case *UnionArg:
|
||||
a1 := new(UnionArg)
|
||||
*a1 = *a
|
||||
arg1 = a1
|
||||
a1.Option = clone(a.Option, newargs)
|
||||
a1.Option = clone(a.Option, newargs, full)
|
||||
case *ResultArg:
|
||||
a1 := new(ResultArg)
|
||||
*a1 = *a
|
||||
@ -78,6 +83,8 @@ func clone(arg Arg, newargs map[Arg]Arg) Arg {
|
||||
if used, ok := arg1.(ArgUsed); ok {
|
||||
*used.Used() = nil // filled when we clone the referent
|
||||
newargs[arg] = arg1
|
||||
} else if full {
|
||||
newargs[arg] = arg1
|
||||
}
|
||||
return arg1
|
||||
}
|
||||
|
@ -28,7 +28,16 @@ type uint64Set map[uint64]bool
|
||||
// }.
|
||||
type CompMap map[uint64]uint64Set
|
||||
|
||||
var specialIntsSet uint64Set
|
||||
var (
|
||||
specialIntsSet uint64Set
|
||||
|
||||
// A set of calls for which hints should not be generated.
|
||||
hintNamesBlackList = map[string]bool{
|
||||
"mmap": true,
|
||||
"open": true,
|
||||
"close": true,
|
||||
}
|
||||
)
|
||||
|
||||
func (m CompMap) AddComp(arg1, arg2 uint64) {
|
||||
if _, ok := specialIntsSet[arg2]; ok {
|
||||
@ -42,6 +51,44 @@ func (m CompMap) AddComp(arg1, arg2 uint64) {
|
||||
m[arg1][arg2] = true
|
||||
}
|
||||
|
||||
// Mutates the program using the comparison operands stored in compMaps.
|
||||
// For each of the mutants executes the exec callback.
|
||||
func (p *Prog) MutateWithHints(compMaps []CompMap, exec func(newP *Prog)) {
|
||||
for i, c := range p.Calls {
|
||||
if _, ok := hintNamesBlackList[c.Meta.CallName]; ok {
|
||||
continue
|
||||
}
|
||||
foreachArg(c, func(arg, _ Arg, _ *[]Arg) {
|
||||
generateHints(p, compMaps[i], c, arg, exec)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func generateHints(p *Prog, compMap CompMap, c *Call, arg Arg, exec func(newP *Prog)) {
|
||||
candidate := func(newArg Arg) {
|
||||
newP, argMap := p.cloneImpl(true)
|
||||
oldArg := argMap[arg]
|
||||
newP.replaceArg(c, oldArg, newArg, nil)
|
||||
if err := newP.validate(); err != nil {
|
||||
panic("a program generated with hints did not pass validation: " +
|
||||
err.Error())
|
||||
}
|
||||
exec(newP)
|
||||
}
|
||||
switch a := arg.(type) {
|
||||
case *ConstArg:
|
||||
checkConstArg(a, compMap, candidate)
|
||||
// case *DataArg:
|
||||
// checkDataArg(a, compMap, candidate)
|
||||
}
|
||||
}
|
||||
|
||||
func checkConstArg(arg *ConstArg, compMap CompMap, cb func(newArg Arg)) {
|
||||
for v, _ := range compMap[arg.Val] {
|
||||
cb(constArg(arg.typ, v))
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
specialIntsSet = make(uint64Set)
|
||||
for _, v := range specialInts {
|
||||
|
@ -85,6 +85,8 @@ var (
|
||||
statExecMinimize uint64
|
||||
statExecSmash uint64
|
||||
statNewInput uint64
|
||||
statExecHints uint64
|
||||
statExecHintSeeds uint64
|
||||
|
||||
allTriaged uint32
|
||||
noCover bool
|
||||
@ -261,7 +263,7 @@ func main() {
|
||||
}
|
||||
}
|
||||
Logf(1, "executing candidate: %s", candidate.p)
|
||||
execute(pid, env, candidate.p, false, candidate.minimized, true, &statExecCandidate)
|
||||
execute(pid, env, candidate.p, false, false, candidate.minimized, true, &statExecCandidate)
|
||||
continue
|
||||
} else if len(triage) != 0 {
|
||||
last := len(triage) - 1
|
||||
@ -292,14 +294,14 @@ func main() {
|
||||
corpusMu.RUnlock()
|
||||
p := prog.Generate(rnd, programLength, ct)
|
||||
Logf(1, "#%v: generated: %s", i, p)
|
||||
execute(pid, env, p, false, false, false, &statExecGen)
|
||||
execute(pid, env, p, false, false, false, false, &statExecGen)
|
||||
} else {
|
||||
// Mutate an existing prog.
|
||||
p := corpus[rnd.Intn(len(corpus))].Clone()
|
||||
corpusMu.RUnlock()
|
||||
p.Mutate(rs, programLength, ct, corpus)
|
||||
Logf(1, "#%v: mutated: %s", i, p)
|
||||
execute(pid, env, p, false, false, false, &statExecFuzz)
|
||||
execute(pid, env, p, false, false, false, false, &statExecFuzz)
|
||||
}
|
||||
}
|
||||
}()
|
||||
@ -477,7 +479,10 @@ func smashInput(pid int, env *ipc.Env, ct *prog.ChoiceTable, rs rand.Source, inp
|
||||
p := inp.p.Clone()
|
||||
p.Mutate(rs, programLength, ct, corpus)
|
||||
Logf(1, "#%v: mutated: %s", pid, p)
|
||||
execute(pid, env, p, false, false, false, &statExecSmash)
|
||||
execute(pid, env, p, false, false, false, false, &statExecSmash)
|
||||
}
|
||||
if compsSupported {
|
||||
executeHintSeed(pid, env, inp.p)
|
||||
}
|
||||
}
|
||||
|
||||
@ -554,7 +559,7 @@ func triageInput(pid int, env *ipc.Env, inp Input) {
|
||||
}
|
||||
|
||||
inp.p, inp.call = prog.Minimize(inp.p, inp.call, func(p1 *prog.Prog, call1 int) bool {
|
||||
info := execute(pid, env, p1, false, false, false, &statExecMinimize)
|
||||
info := execute(pid, env, p1, false, false, false, false, &statExecMinimize)
|
||||
if len(info) == 0 || len(info[call1].Signal) == 0 {
|
||||
return false // The call was not executed.
|
||||
}
|
||||
@ -603,8 +608,37 @@ func triageInput(pid int, env *ipc.Env, inp Input) {
|
||||
}
|
||||
}
|
||||
|
||||
func execute(pid int, env *ipc.Env, p *prog.Prog, needCover, minimized, candidate bool, stat *uint64) []ipc.CallInfo {
|
||||
func executeHintSeed(pid int, env *ipc.Env, p *prog.Prog) {
|
||||
if !compsSupported {
|
||||
panic("compsSupported==false and executeHintSeed() called")
|
||||
}
|
||||
// First execute the original program to dump comparisons from KCOV.
|
||||
info := execute(pid, env, p, false, true, false, false, &statExecHintSeeds)
|
||||
|
||||
// Then extract the comparisons data.
|
||||
compMaps := ipc.GetCompMaps(info)
|
||||
|
||||
// Then mutate the initial program for every match between
|
||||
// a syscall argument and a comparison operand.
|
||||
// Execute each of such mutants to check if it gives new coverage.
|
||||
p.MutateWithHints(compMaps, func(p *prog.Prog) {
|
||||
execute(pid, env, p, false, false, false, false, &statExecHints)
|
||||
})
|
||||
}
|
||||
|
||||
func execute(pid int, env *ipc.Env, p *prog.Prog, needCover, needComps, minimized, candidate bool, stat *uint64) []ipc.CallInfo {
|
||||
opts := &ipc.ExecOpts{}
|
||||
if needComps {
|
||||
if !compsSupported {
|
||||
panic("compsSupported==false and execute() called with needComps")
|
||||
}
|
||||
if needCover {
|
||||
// Currently KCOV is able to dump only the coverage data or only
|
||||
// the comparisons data. We can't enable both modes at same time.
|
||||
panic("only one of the needComps and needCover should be true")
|
||||
}
|
||||
opts.Flags |= ipc.FlagCollectComps
|
||||
}
|
||||
if needCover {
|
||||
opts.Flags |= ipc.FlagCollectCover
|
||||
}
|
||||
|
@ -33,6 +33,7 @@ var (
|
||||
flagOutput = flag.String("output", "none", "write programs to none/stdout")
|
||||
flagFaultCall = flag.Int("fault_call", -1, "inject fault into this call (0-based)")
|
||||
flagFaultNth = flag.Int("fault_nth", 0, "inject fault on n-th operation (0-based)")
|
||||
flagHints = flag.Bool("hints", false, "do a hints-generation run")
|
||||
)
|
||||
|
||||
func main() {
|
||||
@ -73,6 +74,12 @@ func main() {
|
||||
execOpts.Flags |= ipc.FlagCollectCover
|
||||
execOpts.Flags &^= ipc.FlagDedupCover
|
||||
}
|
||||
if *flagHints {
|
||||
if execOpts.Flags&ipc.FlagCollectCover != 0 {
|
||||
execOpts.Flags ^= ipc.FlagCollectCover
|
||||
}
|
||||
execOpts.Flags |= ipc.FlagCollectComps
|
||||
}
|
||||
|
||||
if *flagFaultCall >= 0 {
|
||||
config.Flags |= ipc.FlagEnableFault
|
||||
@ -162,6 +169,13 @@ func main() {
|
||||
}
|
||||
}
|
||||
}
|
||||
if *flagHints {
|
||||
compMaps := ipc.GetCompMaps(info)
|
||||
p.MutateWithHints(compMaps, func(p *prog.Prog) {
|
||||
fmt.Printf("%v\n", string(p.Serialize()))
|
||||
})
|
||||
}
|
||||
|
||||
return true
|
||||
}() {
|
||||
return
|
||||
|
Loading…
x
Reference in New Issue
Block a user