From 07c84b670b4a25a7795e9fb8d47abe0922d2976b Mon Sep 17 00:00:00 2001 From: Victor Chibotaru Date: Wed, 16 Aug 2017 21:18:04 +0200 Subject: executor, ipc: modify the IO between KCOV<->executor<->fuzzer Now executor is able to read comparisons data from KCOV and write them to fuzzer. --- pkg/ipc/ipc.go | 95 ++++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 89 insertions(+), 6 deletions(-) (limited to 'pkg/ipc') diff --git a/pkg/ipc/ipc.go b/pkg/ipc/ipc.go index 674596d60..af0173c2e 100644 --- a/pkg/ipc/ipc.go +++ b/pkg/ipc/ipc.go @@ -64,6 +64,11 @@ const ( statusFail = 67 statusError = 68 statusRetry = 69 + + // Comparison types masks taken from KCOV headers. + compSizeMask = 6 + compSize8 = 6 + compConstMask = 1 ) var ( @@ -222,10 +227,19 @@ type CallInfo struct { Signal []uint32 // feedback signal, filled if FlagSignal is set Cover []uint32 // per-call coverage, filled if FlagSignal is set and cover == true, //if dedup == false, then cov effectively contains a trace, otherwise duplicates are removed - Errno int // call errno (0 if the call was successful) + Comps prog.CompMap // per-call comparison operands + Errno int // call errno (0 if the call was successful) FaultInjected bool } +func GetCompMaps(info []CallInfo) []prog.CompMap { + compMaps := make([]prog.CompMap, len(info)) + for i, inf := range info { + compMaps[i] = inf.Comps + } + return compMaps +} + // Exec starts executor binary to execute program p and returns information about the execution: // output: process output // info: per-call info @@ -264,7 +278,8 @@ func (env *Env) Exec(opts *ExecOpts, p *prog.Prog) (output []byte, info []CallIn return } - if env.config.Flags&FlagSignal == 0 || p == nil { + if p == nil || env.config.Flags&FlagSignal == 0 && + env.config.Flags&FlagCollectComps == 0 { return } info, err0 = env.readOutCoverage(p) @@ -282,9 +297,27 @@ func (env *Env) readOutCoverage(p *prog.Prog) (info []CallInfo, err0 error) { return true } + readOutAndSetErr := func(v *uint32, msg string, args ...interface{}) bool { + if !readOut(v) { + err0 = fmt.Errorf(msg, args) + return false + } + return true + } + + // Reads out a 64 bits int in Little-endian as two blocks of 32 bits. + readOut64 := func(v *uint64, msg string, args ...interface{}) bool { + var a, b uint32 + if !(readOutAndSetErr(&a, msg, args) && readOutAndSetErr(&b, msg, args)) { + return false + } + *v = uint64(a) + uint64(b)<<32 + return true + } + var ncmd uint32 - if !readOut(&ncmd) { - err0 = fmt.Errorf("executor %v: failed to read output coverage", env.pid) + if !readOutAndSetErr(&ncmd, + "executor %v: failed to read output coverage", env.pid) { return } info = make([]CallInfo, len(p.Calls)) @@ -303,8 +336,8 @@ func (env *Env) readOutCoverage(p *prog.Prog) (info []CallInfo, err0 error) { return buf.String() } for i := uint32(0); i < ncmd; i++ { - var callIndex, callNum, errno, faultInjected, signalSize, coverSize uint32 - if !readOut(&callIndex) || !readOut(&callNum) || !readOut(&errno) || !readOut(&faultInjected) || !readOut(&signalSize) || !readOut(&coverSize) { + var callIndex, callNum, errno, faultInjected, signalSize, coverSize, compsSize uint32 + if !readOut(&callIndex) || !readOut(&callNum) || !readOut(&errno) || !readOut(&faultInjected) || !readOut(&signalSize) || !readOut(&coverSize) || !readOut(&compsSize) { err0 = fmt.Errorf("executor %v: failed to read output coverage", env.pid) return } @@ -331,8 +364,10 @@ func (env *Env) readOutCoverage(p *prog.Prog) (info []CallInfo, err0 error) { env.pid, i, callIndex, signalSize, coverSize) return } + // Read out signals. info[callIndex].Signal = out[:signalSize:signalSize] out = out[signalSize:] + // Read out coverage. if coverSize > uint32(len(out)) { err0 = fmt.Errorf("executor %v: failed to read output coverage: record %v, call %v, signalsize=%v coversize=%v", env.pid, i, callIndex, signalSize, coverSize) @@ -340,6 +375,54 @@ func (env *Env) readOutCoverage(p *prog.Prog) (info []CallInfo, err0 error) { } info[callIndex].Cover = out[:coverSize:coverSize] out = out[coverSize:] + // Read out comparisons. + compMap := make(prog.CompMap) + for j := uint32(0); j < compsSize; j++ { + var typ uint32 + var op1, op2 uint64 + if !readOutAndSetErr(&typ, + "executor %v: failed while reading type of comparison %v", env.pid, j) { + return + } + if typ > compConstMask|compSizeMask { + err0 = fmt.Errorf("executor %v: got wrong value (%v) while reading type of comparison %v", + env.pid, typ, j) + return + } + + isSize8 := (typ & compSizeMask) == compSize8 + isConst := (typ & compConstMask) != 0 + arg1ErrString := "executor %v: failed while reading op1 of comparison %v" + arg2ErrString := "executor %v: failed while reading op2 of comparison %v" + if isSize8 { + var tmp1, tmp2 uint32 + if !readOutAndSetErr(&tmp1, arg1ErrString, env.pid, j) || + !readOutAndSetErr(&tmp2, arg2ErrString, env.pid, j) { + return + } + op1 = uint64(tmp1) + op2 = uint64(tmp2) + } else { + if !readOut64(&op1, arg1ErrString, env.pid, j) || + !readOut64(&op2, arg2ErrString, env.pid, j) { + return + } + } + if op1 == op2 { + // It's useless to store such comparisons. + continue + } + compMap.AddComp(op2, op1) + if isConst { + // If one of the operands was const, then this operand is always + // placed first in the instrumented callbacks. Such an operand + // could not be an argument of our syscalls (because otherwise + // it wouldn't be const), thus we simply ignore it. + continue + } + compMap.AddComp(op1, op2) + } + info[callIndex].Comps = compMap } return } -- cgit mrf-deployment