diff options
| author | Dmitry Vyukov <dvyukov@google.com> | 2018-02-26 15:00:46 +0100 |
|---|---|---|
| committer | Dmitry Vyukov <dvyukov@google.com> | 2018-02-26 15:00:46 +0100 |
| commit | 14dae29c2abebd8886909c7a09c5795ffdd11515 (patch) | |
| tree | 50ed602aa0d992d51393bee841b2925a459ae0d4 | |
| parent | 41f6f2579b51e89b33bff9f02830510d2b74d7c3 (diff) | |
executor: use proper default values for resources
We currently use -1 as default value for resources
when the actual value is not available.
-1 is good for fd's, but is not the right default
value for pointers/keys/etc.
Pass from prog and use in executor proper default
value for resources.
| -rw-r--r-- | executor/executor.h | 42 | ||||
| -rw-r--r-- | pkg/csource/csource.go | 94 | ||||
| -rw-r--r-- | prog/any_test.go | 2 | ||||
| -rw-r--r-- | prog/decodeexec.go | 38 | ||||
| -rw-r--r-- | prog/encodingexec.go | 1 | ||||
| -rw-r--r-- | prog/encodingexec_test.go | 2 | ||||
| -rw-r--r-- | prog/minimization_test.go | 4 | ||||
| -rw-r--r-- | prog/prog.go | 4 |
8 files changed, 98 insertions, 89 deletions
diff --git a/executor/executor.h b/executor/executor.h index 5f4db5115..ee48981a9 100644 --- a/executor/executor.h +++ b/executor/executor.h @@ -76,11 +76,6 @@ bool collide; ALIGNED(64 << 10) char input_data[kMaxInput]; -// We use the default value instead of results of failed syscalls. -// -1 is an invalid fd and an invalid address and deterministic, -// so good enough for our purposes. -const uint64 default_value = -1; - // Checksum kinds. const uint64 arg_csum_inet = 0; @@ -187,7 +182,7 @@ uint64 read_arg(uint64** input_posp); uint64 read_const_arg(uint64** input_posp, uint64* size_p, uint64* bf_off_p, uint64* bf_len_p); uint64 read_result(uint64** input_posp); void copyin(char* addr, uint64 val, uint64 size, uint64 bf_off, uint64 bf_len); -uint64 copyout(char* addr, uint64 size); +bool copyout(char* addr, uint64 size, uint64* res); void cover_open(); void cover_enable(thread_t* th); void cover_reset(thread_t* th); @@ -503,13 +498,15 @@ void handle_completion(thread_t* th) switch (instr) { case instr_copyout: { uint64 index = read_input(&th->copyout_pos); - char* addr = (char*)read_input(&th->copyout_pos); - uint64 size = read_input(&th->copyout_pos); - uint64 val = copyout(addr, size); if (index >= kMaxCommands) fail("result idx %lld overflows kMaxCommands", index); - results[index].executed = true; - results[index].val = val; + char* addr = (char*)read_input(&th->copyout_pos); + uint64 size = read_input(&th->copyout_pos); + uint64 val = 0; + if (copyout(addr, size, &val)) { + results[index].executed = true; + results[index].val = val; + } debug("copyout 0x%llx from %p\n", val, addr); break; } @@ -706,26 +703,29 @@ void copyin(char* addr, uint64 val, uint64 size, uint64 bf_off, uint64 bf_len) }); } -uint64 copyout(char* addr, uint64 size) +bool copyout(char* addr, uint64 size, uint64* res) { - uint64 res = default_value; - NONFAILING(switch (size) { + bool ok = false; + NONFAILING( + switch (size) { case 1: - res = *(uint8*)addr; + *res = *(uint8*)addr; break; case 2: - res = *(uint16*)addr; + *res = *(uint16*)addr; break; case 4: - res = *(uint32*)addr; + *res = *(uint32*)addr; break; case 8: - res = *(uint64*)addr; + *res = *(uint64*)addr; break; default: fail("copyout: bad argument size %llu", size); - }); - return res; + } + __atomic_store_n(&ok, true, __ATOMIC_RELEASE); + ); + return ok; } uint64 read_arg(uint64** input_posp) @@ -778,9 +778,9 @@ uint64 read_result(uint64** input_posp) uint64 idx = read_input(input_posp); uint64 op_div = read_input(input_posp); uint64 op_add = read_input(input_posp); + uint64 arg = read_input(input_posp); if (idx >= kMaxCommands) fail("command refers to bad result %lld", idx); - uint64 arg = default_value; if (results[idx].executed) { arg = results[idx].val; if (op_div != 0) diff --git a/pkg/csource/csource.go b/pkg/csource/csource.go index 5c1b0208e..904e0287e 100644 --- a/pkg/csource/csource.go +++ b/pkg/csource/csource.go @@ -27,7 +27,7 @@ func Write(p *prog.Prog, opts Options) ([]byte, error) { calls: make(map[string]uint64), } - calls, nvar, err := ctx.generateProgCalls(ctx.p) + calls, vars, err := ctx.generateProgCalls(ctx.p) if err != nil { return nil, err } @@ -53,15 +53,22 @@ func Write(p *prog.Prog, opts Options) ([]byte, error) { ctx.generateSyscallDefines() - if nvar != 0 { - ctx.printf("long r[%v];\n", nvar) + if len(vars) != 0 { + ctx.printf("uint64_t r[%v] = {", len(vars)) + for i, v := range vars { + if i != 0 { + ctx.printf(", ") + } + ctx.printf("0x%x", v) + } + ctx.printf("};\n") } if opts.Procs > 1 { ctx.printf("uint64_t procid;\n") } if !opts.Repeat { - ctx.generateTestFunc(calls, nvar, "loop") + ctx.generateTestFunc(calls, len(vars) != 0, "loop") ctx.print("int main()\n{\n") for _, c := range mmapCalls { @@ -85,7 +92,7 @@ func Write(p *prog.Prog, opts Options) ([]byte, error) { } ctx.print("\treturn 0;\n}\n") } else { - ctx.generateTestFunc(calls, nvar, "test") + ctx.generateTestFunc(calls, len(vars) != 0, "test") if opts.Procs <= 1 { ctx.print("int main()\n{\n") for _, c := range mmapCalls { @@ -196,10 +203,13 @@ func (ctx *context) printf(str string, args ...interface{}) { ctx.print(fmt.Sprintf(str, args...)) } -func (ctx *context) generateTestFunc(calls []string, nvar uint64, name string) { +func (ctx *context) generateTestFunc(calls []string, hasVars bool, name string) { opts := ctx.opts if !opts.Threaded && !opts.Collide { ctx.printf("void %v()\n{\n", name) + if hasVars { + ctx.printf("\tlong res;") + } if opts.Debug { // Use debug to avoid: error: ‘debug’ defined but not used. ctx.printf("\tdebug(\"%v\\n\");\n", name) @@ -207,15 +217,15 @@ func (ctx *context) generateTestFunc(calls []string, nvar uint64, name string) { if opts.Repro { ctx.printf("\tsyscall(SYS_write, 1, \"executing program\\n\", strlen(\"executing program\\n\"));\n") } - if nvar != 0 { - ctx.printf("\tmemset(r, -1, sizeof(r));\n") - } for _, c := range calls { ctx.printf("%s", c) } ctx.printf("}\n\n") } else { ctx.printf("void execute_call(int call)\n{\n") + if hasVars { + ctx.printf("\tlong res;") + } ctx.printf("\tswitch (call) {\n") for i, c := range calls { ctx.printf("\tcase %v:\n", i) @@ -233,9 +243,6 @@ func (ctx *context) generateTestFunc(calls []string, nvar uint64, name string) { if opts.Repro { ctx.printf("\tsyscall(SYS_write, 1, \"executing program\\n\", strlen(\"executing program\\n\"));\n") } - if nvar != 0 { - ctx.printf("\tmemset(r, -1, sizeof(r));\n") - } ctx.printf("\texecute(%v);\n", len(calls)) if opts.Collide { ctx.printf("\tcollide = 1;\n") @@ -265,21 +272,21 @@ func (ctx *context) generateSyscallDefines() { ctx.printf("\n") } -func (ctx *context) generateProgCalls(p *prog.Prog) ([]string, uint64, error) { +func (ctx *context) generateProgCalls(p *prog.Prog) ([]string, []uint64, error) { exec := make([]byte, prog.ExecBufferSize) progSize, err := p.SerializeForExec(exec) if err != nil { - return nil, 0, fmt.Errorf("failed to serialize program: %v", err) + return nil, nil, fmt.Errorf("failed to serialize program: %v", err) } decoded, err := ctx.target.DeserializeExec(exec[:progSize]) if err != nil { - return nil, 0, err + return nil, nil, err } - calls, nvar := ctx.generateCalls(decoded) - return calls, nvar, nil + calls, vars := ctx.generateCalls(decoded) + return calls, vars, nil } -func (ctx *context) generateCalls(p prog.ExecProg) ([]string, uint64) { +func (ctx *context) generateCalls(p prog.ExecProg) ([]string, []uint64) { var calls []string csumSeq := 0 for ci, call := range p.Calls { @@ -337,7 +344,6 @@ func (ctx *context) generateCalls(p prog.ExecProg) ([]string, uint64) { callName := call.Meta.CallName resCopyout := call.Index != prog.ExecNoCopyout argCopyout := len(call.Copyout) != 0 - argCopyoutMultiple := len(call.Copyout) > 1 emitCall := ctx.opts.EnableTun || callName != "syz_emit_ethernet" && callName != "syz_extract_tcp_res" // TODO: if we don't emit the call we must also not emit copyin, copyout and fault injection. @@ -345,14 +351,8 @@ func (ctx *context) generateCalls(p prog.ExecProg) ([]string, uint64) { if emitCall { native := !strings.HasPrefix(callName, "syz_") fmt.Fprintf(w, "\t") - if argCopyout { - fmt.Fprintf(w, "if (") - if resCopyout { - fmt.Fprintf(w, "(") - } - } - if resCopyout { - fmt.Fprintf(w, "r[%v] = ", call.Index) + if resCopyout || argCopyout { + fmt.Fprintf(w, "res = ") } if native { fmt.Fprintf(w, "syscall(%v%v", ctx.sysTarget.SyscallPrefix, callName) @@ -372,33 +372,31 @@ func (ctx *context) generateCalls(p prog.ExecProg) ([]string, uint64) { panic(fmt.Sprintf("unknown arg type: %+v", arg)) } } - fmt.Fprintf(w, ")") - if argCopyout { - if resCopyout { - fmt.Fprintf(w, ")") - } - fmt.Fprintf(w, " != -1)") - if argCopyoutMultiple { - fmt.Fprintf(w, " {") - } - } else { - fmt.Fprintf(w, ";") - } - fmt.Fprintf(w, "\n") + fmt.Fprintf(w, ");\n") } // Copyout. - for _, copyout := range call.Copyout { - fmt.Fprintf(w, "\t\tNONFAILING(r[%v] = *(uint%v_t*)0x%x);\n", - copyout.Index, copyout.Size*8, copyout.Addr) - } - if emitCall && argCopyoutMultiple { - fmt.Fprintf(w, "\t}\n") + if resCopyout || argCopyout { + fmt.Fprintf(w, "\tif (res != -1)") + copyoutMultiple := len(call.Copyout) > 1 || resCopyout && len(call.Copyout) > 0 + if copyoutMultiple { + fmt.Fprintf(w, " {") + } + fmt.Fprintf(w, "\n") + if resCopyout { + fmt.Fprintf(w, "\t\tr[%v] = res;\n", call.Index) + } + for _, copyout := range call.Copyout { + fmt.Fprintf(w, "\t\tNONFAILING(r[%v] = *(uint%v_t*)0x%x);\n", + copyout.Index, copyout.Size*8, copyout.Addr) + } + if copyoutMultiple { + fmt.Fprintf(w, "\t}\n") + } } - calls = append(calls, w.String()) } - return calls, p.NumVars + return calls, p.Vars } func (ctx *context) constArgToStr(arg prog.ExecArgConst) string { diff --git a/prog/any_test.go b/prog/any_test.go index eff50e946..104f77f9d 100644 --- a/prog/any_test.go +++ b/prog/any_test.go @@ -40,7 +40,7 @@ func TestSquash(t *testing.T) { }{ { `foo$any0(&(0x7f0000000000)={0x11, 0x11223344, 0x2233, 0x1122334455667788, [{0x0, @res32=0x0, 0x0, @i8=0x44, "aabb"}, {0x0, @res64=0x1, 0x0, @i32=0x11223344, "1122334455667788"}]})`, - `foo$any0(&(0x7f0000000000)=ANY=[@ANYBLOB="1100000044332211223300000000000088776655443322110000000000000000", @ANYRES32, @ANYBLOB="00000000000000000000000044aabb000000000000000000", @ANYRES64=0x1, @ANYBLOB="0000000000000000443322111122334455667788"])`, + `foo$any0(&(0x7f0000000000)=ANY=[@ANYBLOB="1100000044332211223300000000000088776655443322110000000000000000", @ANYRES32=0x0, @ANYBLOB="00000000000000000000000044aabb000000000000000000", @ANYRES64=0x1, @ANYBLOB="0000000000000000443322111122334455667788"])`, }, } for i, test := range tests { diff --git a/prog/decodeexec.go b/prog/decodeexec.go index a16461782..f8c29a517 100644 --- a/prog/decodeexec.go +++ b/prog/decodeexec.go @@ -8,8 +8,8 @@ import ( ) type ExecProg struct { - Calls []ExecCall - NumVars uint64 + Calls []ExecCall + Vars []uint64 } type ExecCall struct { @@ -43,10 +43,11 @@ type ExecArgConst struct { } type ExecArgResult struct { - Size uint64 - Index uint64 - DivOp uint64 - AddOp uint64 + Size uint64 + Index uint64 + DivOp uint64 + AddOp uint64 + Default uint64 } type ExecArgData struct { @@ -71,9 +72,13 @@ func (target *Target) DeserializeExec(exec []byte) (ExecProg, error) { if dec.err != nil { return ExecProg{}, dec.err } + if uint64(len(dec.vars)) != dec.numVars { + return ExecProg{}, fmt.Errorf("mismatching number of vars: %v/%v", + len(dec.vars), dec.numVars) + } p := ExecProg{ - Calls: dec.calls, - NumVars: dec.numVars, + Calls: dec.calls, + Vars: dec.vars, } return p, nil } @@ -83,6 +88,7 @@ type execDecoder struct { data []byte err error numVars uint64 + vars []uint64 call ExecCall calls []ExecCall } @@ -139,12 +145,18 @@ func (dec *execDecoder) readArg() ExecArg { BigEndian: (meta & (1 << 8)) != 0, } case execArgResult: - return ExecArgResult{ - Size: dec.read(), - Index: dec.read(), - DivOp: dec.read(), - AddOp: dec.read(), + arg := ExecArgResult{ + Size: dec.read(), + Index: dec.read(), + DivOp: dec.read(), + AddOp: dec.read(), + Default: dec.read(), + } + for uint64(len(dec.vars)) <= arg.Index { + dec.vars = append(dec.vars, 0) } + dec.vars[arg.Index] = arg.Default + return arg case execArgData: return ExecArgData{ Data: dec.readBlob(dec.read()), diff --git a/prog/encodingexec.go b/prog/encodingexec.go index 8ecec3a03..ea01560d9 100644 --- a/prog/encodingexec.go +++ b/prog/encodingexec.go @@ -247,6 +247,7 @@ func (w *execContext) writeArg(arg Arg) { w.write(info.Idx) w.write(a.OpDiv) w.write(a.OpAdd) + w.write(a.Type().(*ResourceType).Default()) } case *PointerArg: w.writeConstArg(a.Size(), w.target.PhysicalAddr(a), 0, 0, 0, false) diff --git a/prog/encodingexec_test.go b/prog/encodingexec_test.go index 2ad19598e..1b3fb14b4 100644 --- a/prog/encodingexec_test.go +++ b/prog/encodingexec_test.go @@ -58,7 +58,6 @@ func TestSerializeForExec(t *testing.T) { Index: ExecNoCopyout, }, }, - NumVars: 0, }, }, { @@ -341,7 +340,6 @@ func TestSerializeForExec(t *testing.T) { }, }, }, - NumVars: 0, }, }, { diff --git a/prog/minimization_test.go b/prog/minimization_test.go index 5979ce7a2..70137fa65 100644 --- a/prog/minimization_test.go +++ b/prog/minimization_test.go @@ -20,7 +20,7 @@ func TestMinimize(t *testing.T) { { "mmap(&(0x7f0000000000/0x1000)=nil, 0x1000, 0x3, 0x32, 0xffffffffffffffff, 0x0)\n" + "sched_yield()\n" + - "pipe2(&(0x7f0000000000)={0x0, 0x0}, 0x0)\n", + "pipe2(&(0x7f0000000000), 0x0)\n", 2, func(p *Prog, callIndex int) bool { if len(p.Calls) == 0 { @@ -47,7 +47,7 @@ func TestMinimize(t *testing.T) { return len(p.Calls) == 2 && p.Calls[0].Meta.Name == "mmap" && p.Calls[1].Meta.Name == "pipe2" }, "mmap(&(0x7f0000000000/0x1000)=nil, 0x1000, 0x0, 0x0, 0xffffffffffffffff, 0x0)\n" + - "pipe2(&(0x7f0000000000)={0xffffffffffffffff, 0xffffffffffffffff}, 0x0)\n", + "pipe2(&(0x7f0000000000), 0x0)\n", 1, }, // Remove two dependent calls. diff --git a/prog/prog.go b/prog/prog.go index dadc02cd5..f9f088760 100644 --- a/prog/prog.go +++ b/prog/prog.go @@ -333,7 +333,7 @@ func (target *Target) defaultArg(t Type) Arg { case *IntType, *ConstType, *FlagsType, *LenType, *ProcType, *CsumType: return MakeConstArg(t, t.Default()) case *ResourceType: - return MakeResultArg(t, nil, typ.Desc.Type.Default()) + return MakeResultArg(t, nil, typ.Default()) case *BufferType: if t.Dir() == DirOut { var sz uint64 @@ -432,7 +432,7 @@ func (target *Target) isDefaultArg(arg Arg) bool { case *ResultArg: t := a.Type().(*ResourceType) return a.Res == nil && a.OpDiv == 0 && a.OpAdd == 0 && - len(a.uses) == 0 && a.Val == t.Desc.Type.Default() + len(a.uses) == 0 && a.Val == t.Default() } return false } |
