aboutsummaryrefslogtreecommitdiffstats
path: root/pkg/ipc
diff options
context:
space:
mode:
authorVictor Chibotaru <tchibo@google.com>2017-08-16 21:18:04 +0200
committerDmitry Vyukov <dvyukov@google.com>2017-08-30 18:40:14 +0200
commit07c84b670b4a25a7795e9fb8d47abe0922d2976b (patch)
tree97487c90593aeb8c8773762790f133ac0bc6a3b7 /pkg/ipc
parent1336586b42f6118b19c3da932fd615e85a47c0b5 (diff)
executor, ipc: modify the IO between KCOV<->executor<->fuzzer
Now executor is able to read comparisons data from KCOV and write them to fuzzer.
Diffstat (limited to 'pkg/ipc')
-rw-r--r--pkg/ipc/ipc.go95
1 files changed, 89 insertions, 6 deletions
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
}