From 88e3a1226bc591d81c1fb98e83cb63cd4f341c6e Mon Sep 17 00:00:00 2001 From: Andrey Artemiev Date: Sat, 6 Aug 2022 05:17:33 -0700 Subject: pkg/csource, pkg/instance, pkg/ipc, pkg/mgrconfig, tools/syz-prog2c, syz-manager: introduce a new setting 'sandbox_arg' (#3263) --- executor/common_linux.h | 11 ++--- executor/executor.cc | 6 ++- pkg/csource/csource.go | 22 ++++++---- pkg/csource/csource_test.go | 35 ++++++++++++++++ pkg/csource/generated.go | 11 ++--- pkg/csource/options.go | 89 ++++++++++++++++++++++++++++++---------- pkg/csource/options_test.go | 53 ++++++++++++++++++++++++ pkg/instance/execprog.go | 2 +- pkg/instance/instance.go | 19 +++++---- pkg/instance/instance_test.go | 10 ++--- pkg/ipc/ipc.go | 17 ++++---- pkg/ipc/ipc_test.go | 2 + pkg/ipc/ipcconfig/ipcconfig.go | 14 ++++--- pkg/mgrconfig/config.go | 5 +++ syz-manager/manager.go | 5 ++- tools/syz-prog2c/prog2c.go | 4 +- tools/syz-runtest/runtest.go | 3 +- tools/syz-testbuild/testbuild.go | 26 ++++++------ 18 files changed, 251 insertions(+), 83 deletions(-) diff --git a/executor/common_linux.h b/executor/common_linux.h index 24f553848..21bde3bb6 100644 --- a/executor/common_linux.h +++ b/executor/common_linux.h @@ -4163,7 +4163,7 @@ static void setfilecon(const char* path, const char* context) #define SYZ_HAVE_SANDBOX_ANDROID 1 -static int do_sandbox_android(int sandbox_arg) +static int do_sandbox_android(uint64 sandbox_arg) { setup_common(); #if SYZ_EXECUTOR || SYZ_VHCI_INJECTION @@ -4192,6 +4192,7 @@ static int do_sandbox_android(int sandbox_arg) size_t num_groups = UNTRUSTED_APP_NUM_GROUPS; const gid_t* groups = UNTRUSTED_APP_GROUPS; gid_t gid = UNTRUSTED_APP_GID; + debug("executor received sandbox_arg=%llu\n", sandbox_arg); if (sandbox_arg == 1) { uid = SYSTEM_UID; num_groups = SYSTEM_NUM_GROUPS; @@ -4201,13 +4202,13 @@ static int do_sandbox_android(int sandbox_arg) debug("fuzzing under SYSTEM account\n"); } if (chown(".", uid, uid) != 0) - failmsg("do_sandbox_android: chmod failed", "sandbox_arg=%d", sandbox_arg); + failmsg("do_sandbox_android: chmod failed", "sandbox_arg=%llu", sandbox_arg); if (setgroups(num_groups, groups) != 0) - failmsg("do_sandbox_android: setgroups failed", "sandbox_arg=%d", sandbox_arg); + failmsg("do_sandbox_android: setgroups failed", "sandbox_arg=%llu", sandbox_arg); if (setresgid(gid, gid, gid) != 0) - failmsg("do_sandbox_android: setresgid failed", "sandbox_arg=%d", sandbox_arg); + failmsg("do_sandbox_android: setresgid failed", "sandbox_arg=%llu", sandbox_arg); setup_binderfs(); @@ -4222,7 +4223,7 @@ static int do_sandbox_android(int sandbox_arg) #endif if (setresuid(uid, uid, uid) != 0) - failmsg("do_sandbox_android: setresuid failed", "sandbox_arg=%d", sandbox_arg); + failmsg("do_sandbox_android: setresuid failed", "sandbox_arg=%llu", sandbox_arg); // setresuid and setresgid clear the parent-death signal. prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0); diff --git a/executor/executor.cc b/executor/executor.cc index 392c63a32..3c76738d2 100644 --- a/executor/executor.cc +++ b/executor/executor.cc @@ -300,6 +300,7 @@ struct handshake_req { uint64 magic; uint64 flags; // env flags uint64 pid; + uint64 sandbox_arg; }; struct handshake_reply { @@ -415,7 +416,7 @@ static void setup_features(char** enable, int n); #include "test.h" #if SYZ_HAVE_SANDBOX_ANDROID -static int sandbox_arg = 0; +static uint64 sandbox_arg = 0; #endif int main(int argc, char** argv) @@ -631,6 +632,9 @@ void receive_handshake() failmsg("handshake read failed", "read=%d", n); if (req.magic != kInMagic) failmsg("bad handshake magic", "magic=0x%llx", req.magic); +#if SYZ_HAVE_SANDBOX_ANDROID + sandbox_arg = req.sandbox_arg; +#endif parse_env_flags(req.flags); procid = req.pid; } diff --git a/pkg/csource/csource.go b/pkg/csource/csource.go index 7d3b133bf..df35b54b8 100644 --- a/pkg/csource/csource.go +++ b/pkg/csource/csource.go @@ -28,6 +28,7 @@ import ( "fmt" "regexp" "sort" + "strconv" "strings" "time" @@ -58,6 +59,18 @@ type context struct { calls map[string]uint64 // CallName -> NR } +func generateSandboxFunctionSignature(sandboxName string, sandboxArg int) string { + if sandboxName == "" { + return "loop();" + } + + arguments := "();" + if sandboxName == "android" { + arguments = "(" + strconv.Itoa(sandboxArg) + ");" + } + return "do_sandbox_" + sandboxName + arguments +} + func (ctx *context) generateSource() ([]byte, error) { ctx.filterCalls() calls, vars, err := ctx.generateProgCalls(ctx.p, ctx.opts.Trace) @@ -94,14 +107,7 @@ func (ctx *context) generateSource() ([]byte, error) { fmt.Fprintf(varsBuf, "};\n") } - sandboxFunc := "loop();" - if ctx.opts.Sandbox != "" { - arguments := "();" - if ctx.opts.Sandbox == "android" { - arguments = "(0);" - } - sandboxFunc = "do_sandbox_" + ctx.opts.Sandbox + arguments - } + sandboxFunc := generateSandboxFunctionSignature(ctx.opts.Sandbox, ctx.opts.SandboxArg) replacements := map[string]string{ "PROCS": fmt.Sprint(ctx.opts.Procs), "REPEAT_TIMES": fmt.Sprint(ctx.opts.RepeatTimes), diff --git a/pkg/csource/csource_test.go b/pkg/csource/csource_test.go index 91bd2cccf..491b84b03 100644 --- a/pkg/csource/csource_test.go +++ b/pkg/csource/csource_test.go @@ -18,6 +18,7 @@ import ( "github.com/google/syzkaller/prog" _ "github.com/google/syzkaller/sys" "github.com/google/syzkaller/sys/targets" + "github.com/stretchr/testify/assert" ) func init() { @@ -227,3 +228,37 @@ syscall(SYS_csource6, 0x20000140ul); }) } } + +func generateSandboxFunctionSignatureTestCase(t *testing.T, sandbox string, sandboxArg int, expected, message string) { + actual := generateSandboxFunctionSignature(sandbox, sandboxArg) + assert.Equal(t, actual, expected, message) +} + +func TestGenerateSandboxFunctionSignature(t *testing.T) { + // This test-case intentionally omits the following edge cases: + // - sandbox name as whitespaces, tabs + // - control chars \r, \n and unprintables + // - unsuitable chars - punctuation, emojis, '#', '*', etc + // - character case mismatching function prototype defined in common_linux.h. + // For example 'do_sandbox_android' and 'AnDroid'. + // - non english letters, unicode compound characters + // and focuses on correct handling of sandboxes supporting and not 'sandbox_arg' + // config setting. + generateSandboxFunctionSignatureTestCase(t, + "", // sandbox name + 0, // sandbox arg + "loop();", // expected + "Empty sandbox name should produce 'loop();'") + + generateSandboxFunctionSignatureTestCase(t, + "abrakadabra", // sandbox name + 0, // sandbox arg + "do_sandbox_abrakadabra();", // expected + "Empty sandbox name should produce 'loop();'") + + generateSandboxFunctionSignatureTestCase(t, + "android", // sandbox name + -1234, // sandbox arg + "do_sandbox_android(-1234);", // expected + "Android sandbox function requires an argument") +} diff --git a/pkg/csource/generated.go b/pkg/csource/generated.go index 82799a0c3..874c8d72d 100644 --- a/pkg/csource/generated.go +++ b/pkg/csource/generated.go @@ -9798,7 +9798,7 @@ static void setfilecon(const char* path, const char* context) #define SYZ_HAVE_SANDBOX_ANDROID 1 -static int do_sandbox_android(int sandbox_arg) +static int do_sandbox_android(uint64 sandbox_arg) { setup_common(); #if SYZ_EXECUTOR || SYZ_VHCI_INJECTION @@ -9823,6 +9823,7 @@ static int do_sandbox_android(int sandbox_arg) size_t num_groups = UNTRUSTED_APP_NUM_GROUPS; const gid_t* groups = UNTRUSTED_APP_GROUPS; gid_t gid = UNTRUSTED_APP_GID; + debug("executor received sandbox_arg=%llu\n", sandbox_arg); if (sandbox_arg == 1) { uid = SYSTEM_UID; num_groups = SYSTEM_NUM_GROUPS; @@ -9832,13 +9833,13 @@ static int do_sandbox_android(int sandbox_arg) debug("fuzzing under SYSTEM account\n"); } if (chown(".", uid, uid) != 0) - failmsg("do_sandbox_android: chmod failed", "sandbox_arg=%d", sandbox_arg); + failmsg("do_sandbox_android: chmod failed", "sandbox_arg=%llu", sandbox_arg); if (setgroups(num_groups, groups) != 0) - failmsg("do_sandbox_android: setgroups failed", "sandbox_arg=%d", sandbox_arg); + failmsg("do_sandbox_android: setgroups failed", "sandbox_arg=%llu", sandbox_arg); if (setresgid(gid, gid, gid) != 0) - failmsg("do_sandbox_android: setresgid failed", "sandbox_arg=%d", sandbox_arg); + failmsg("do_sandbox_android: setresgid failed", "sandbox_arg=%llu", sandbox_arg); setup_binderfs(); @@ -9850,7 +9851,7 @@ static int do_sandbox_android(int sandbox_arg) #endif if (setresuid(uid, uid, uid) != 0) - failmsg("do_sandbox_android: setresuid failed", "sandbox_arg=%d", sandbox_arg); + failmsg("do_sandbox_android: setresuid failed", "sandbox_arg=%llu", sandbox_arg); prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0); setfilecon(".", SELINUX_LABEL_APP_DATA_FILE); diff --git a/pkg/csource/options.go b/pkg/csource/options.go index 3fc549282..b150e79a0 100644 --- a/pkg/csource/options.go +++ b/pkg/csource/options.go @@ -24,6 +24,7 @@ type Options struct { Procs int `json:"procs"` Slowdown int `json:"slowdown"` Sandbox string `json:"sandbox"` + SandboxArg int `json:"sandbox_arg"` Leak bool `json:"leak,omitempty"` // do leak checking @@ -197,35 +198,27 @@ func (opts Options) Serialize() []byte { return data } -func DeserializeOptions(data []byte) (Options, error) { - opts := Options{ - Slowdown: 1, - // Before CloseFDs was added, close_fds() was always called, so default to true. - CloseFDs: true, - } - if err := json.Unmarshal(data, &opts); err == nil { - return opts, nil - } - // Support for legacy formats. - data = bytes.Replace(data, []byte("Sandbox: "), []byte("Sandbox:empty "), -1) +func deserializeLegacyOptions1(data string, opts *Options) error { waitRepeat, debug := false, false - n, err := fmt.Sscanf(string(data), + n, err := fmt.Sscanf(data, "{Threaded:%t Collide:%t Repeat:%t Procs:%d Sandbox:%s"+ " Fault:%t FaultCall:%d FaultNth:%d EnableTun:%t UseTmpDir:%t"+ " HandleSegv:%t WaitRepeat:%t Debug:%t Repro:%t}", &opts.Threaded, &opts.Collide, &opts.Repeat, &opts.Procs, &opts.Sandbox, &opts.Fault, &opts.FaultCall, &opts.FaultNth, &opts.NetInjection, &opts.UseTmpDir, &opts.HandleSegv, &waitRepeat, &debug, &opts.Repro) + if err == nil { if want := 14; n != want { - return opts, fmt.Errorf("failed to parse repro options: got %v fields, want %v", n, want) + return fmt.Errorf("failed to parse repro options: got %v fields, want %v", n, want) } - if opts.Sandbox == "empty" { - opts.Sandbox = "" - } - return opts, nil } - n, err = fmt.Sscanf(string(data), + return err +} + +func deserializeLegacyOptions2(data string, opts *Options) error { + waitRepeat, debug := false, false + n, err := fmt.Sscanf(data, "{Threaded:%t Collide:%t Repeat:%t Procs:%d Sandbox:%s"+ " Fault:%t FaultCall:%d FaultNth:%d EnableTun:%t UseTmpDir:%t"+ " EnableCgroups:%t HandleSegv:%t WaitRepeat:%t Debug:%t Repro:%t}", @@ -234,13 +227,67 @@ func DeserializeOptions(data []byte) (Options, error) { &opts.Cgroups, &opts.HandleSegv, &waitRepeat, &debug, &opts.Repro) if err == nil { if want := 15; n != want { - return opts, fmt.Errorf("failed to parse repro options: got %v fields, want %v", n, want) + return fmt.Errorf("failed to parse repro options 2: got %v fields, want %v", n, want) } - if opts.Sandbox == "empty" { - opts.Sandbox = "" + } + return err +} + +// Android format. +func deserializeLegacyOptions3(data string, opts *Options) error { + waitRepeat, debug := false, false + n, err := fmt.Sscanf(data, + "{Threaded:%t Collide:%t Repeat:%t Procs:%d Sandbox:%s SandboxArg:%d"+ + " Fault:%t FaultCall:%d FaultNth:%d EnableTun:%t UseTmpDir:%t"+ + " EnableCgroups:%t HandleSegv:%t WaitRepeat:%t Debug:%t Repro:%t}", + &opts.Threaded, &opts.Collide, &opts.Repeat, &opts.Procs, &opts.Sandbox, &opts.SandboxArg, + &opts.Fault, &opts.FaultCall, &opts.FaultNth, &opts.NetInjection, &opts.UseTmpDir, + &opts.Cgroups, &opts.HandleSegv, &waitRepeat, &debug, &opts.Repro) + if err == nil { + if want := 16; n != want { + return fmt.Errorf("failed to parse repro options 3: got %v fields, want %v", n, want) } + } + return err +} + +var parsers = []func(string, *Options) error{ + deserializeLegacyOptions1, + deserializeLegacyOptions2, + deserializeLegacyOptions3, +} + +// Support for legacy formats. +func deserializeLegacyFormats(data []byte, opts *Options) error { + data = bytes.Replace(data, []byte("Sandbox: "), []byte("Sandbox:empty "), -1) + strData := string(data) + + // We can distinguish between legacy formats by the number + // of fields. The formats we support have 14, 15 and 16. + // Each field can be recognized by ':' char. + version := strings.Count(strData, ":") + if version < 14 || version > 16 { + return fmt.Errorf("unrecognized options format") + } + index := version - 14 + err := parsers[index](strData, opts) + + if opts.Sandbox == "empty" { + opts.Sandbox = "" + } + return err +} + +func DeserializeOptions(data []byte) (Options, error) { + opts := Options{ + Slowdown: 1, + // Before CloseFDs was added, close_fds() was always called, so default to true. + CloseFDs: true, + } + if err := json.Unmarshal(data, &opts); err == nil { return opts, nil } + err := deserializeLegacyFormats(data, &opts) return opts, err } diff --git a/pkg/csource/options_test.go b/pkg/csource/options_test.go index 5e971a9d1..41d7cdbab 100644 --- a/pkg/csource/options_test.go +++ b/pkg/csource/options_test.go @@ -5,6 +5,7 @@ package csource import ( "fmt" + "math" "reflect" "testing" @@ -79,6 +80,32 @@ func TestParseOptionsCanned(t *testing.T) { FaultNth: 2, }, }, + `{"threaded":true,"collide":true,"repeat":true,"procs":10,"sandbox":"android", + "sandbox_arg":9,"fault":true,"fault_call":1,"fault_nth":2,"tun":true,"tmpdir":true,"cgroups":true, + "netdev":true,"resetnet":true, + "segv":true,"waitrepeat":true,"debug":true,"repro":true}`: { + Threaded: true, + Repeat: true, + Procs: 10, + Slowdown: 1, + Sandbox: "android", + SandboxArg: 9, + NetInjection: true, + NetDevices: true, + NetReset: true, + Cgroups: true, + BinfmtMisc: false, + CloseFDs: true, + UseTmpDir: true, + HandleSegv: true, + Repro: true, + LegacyOptions: LegacyOptions{ + Collide: true, + Fault: true, + FaultCall: 1, + FaultNth: 2, + }, + }, "{Threaded:true Collide:true Repeat:true Procs:1 Sandbox:none Fault:false FaultCall:-1 FaultNth:0 EnableTun:true UseTmpDir:true HandleSegv:true WaitRepeat:true Debug:false Repro:false}": { Threaded: true, Repeat: true, @@ -139,6 +166,27 @@ func TestParseOptionsCanned(t *testing.T) { FaultNth: 0, }, }, + "{Threaded:false Collide:true Repeat:true Procs:1 Sandbox:namespace SandboxArg:-234 Fault:false FaultCall:-1 FaultNth:0 EnableTun:true UseTmpDir:true EnableCgroups:true HandleSegv:true WaitRepeat:true Debug:false Repro:false}": { + Threaded: false, + Repeat: true, + Procs: 1, + Slowdown: 1, + Sandbox: "namespace", + SandboxArg: -234, + NetInjection: true, + Cgroups: true, + BinfmtMisc: false, + CloseFDs: true, + UseTmpDir: true, + HandleSegv: true, + Repro: false, + LegacyOptions: LegacyOptions{ + Collide: true, + Fault: false, + FaultCall: -1, + FaultNth: 0, + }, + }, } for data, want := range canned { got, err := DeserializeOptions([]byte(data)) @@ -206,6 +254,11 @@ func enumerateField(OS string, opt Options, field int) []Options { fld.SetString(sandbox) opts = append(opts, opt) } + } else if fldName == "SandboxArg" { + for _, sandboxArg := range []int64{math.MinInt, math.MaxInt} { + fld.SetInt(sandboxArg) + opts = append(opts, opt) + } } else if fldName == "Procs" { for _, procs := range []int64{1, 4} { fld.SetInt(procs) diff --git a/pkg/instance/execprog.go b/pkg/instance/execprog.go index 1fa01b620..3c54342ac 100644 --- a/pkg/instance/execprog.go +++ b/pkg/instance/execprog.go @@ -169,7 +169,7 @@ func (inst *ExecProgInstance) RunSyzProgFile(progFile string, duration time.Dura faultCall = opts.FaultCall } command := ExecprogCmd(inst.execprogBin, inst.executorBin, target.OS, target.Arch, opts.Sandbox, - opts.Repeat, opts.Threaded, opts.Collide, opts.Procs, faultCall, opts.FaultNth, + opts.SandboxArg, opts.Repeat, opts.Threaded, opts.Collide, opts.Procs, faultCall, opts.FaultNth, !inst.OldFlagsCompatMode, inst.mgrCfg.Timeouts.Slowdown, vmProgFile) return inst.runCommand(command, duration) } diff --git a/pkg/instance/instance.go b/pkg/instance/instance.go index 9f3e46c82..d25eae4c5 100644 --- a/pkg/instance/instance.go +++ b/pkg/instance/instance.go @@ -349,7 +349,7 @@ func (inst *inst) testInstance() error { } cmd := OldFuzzerCmd(fuzzerBin, executorBin, targets.TestOS, inst.cfg.TargetOS, inst.cfg.TargetArch, fwdAddr, - inst.cfg.Sandbox, 0, inst.cfg.Cover, true, inst.optionalFlags, inst.cfg.Timeouts.Slowdown) + inst.cfg.Sandbox, inst.cfg.SandboxArg, 0, inst.cfg.Cover, true, inst.optionalFlags, inst.cfg.Timeouts.Slowdown) outc, errc, err := inst.vm.Run(10*time.Minute*inst.cfg.Timeouts.Scale, nil, cmd) if err != nil { return fmt.Errorf("failed to run binary in VM: %v", err) @@ -418,8 +418,9 @@ func (inst *inst) testRepro() ([]byte, error) { } type OptionalFuzzerArgs struct { - Slowdown int - RawCover bool + Slowdown int + RawCover bool + SandboxArg int } type FuzzerCmdArgs struct { @@ -460,6 +461,7 @@ func FuzzerCmd(args *FuzzerCmdArgs) string { flags := []tool.Flag{ {Name: "slowdown", Value: fmt.Sprint(args.Optional.Slowdown)}, {Name: "raw_cover", Value: fmt.Sprint(args.Optional.RawCover)}, + {Name: "sandbox_arg", Value: fmt.Sprint(args.Optional.SandboxArg)}, } optionalArg = " " + tool.OptionalFlags(flags) } @@ -469,19 +471,19 @@ func FuzzerCmd(args *FuzzerCmdArgs) string { args.Procs, args.Cover, args.Debug, args.Test, runtestArg, verbosityArg, optionalArg) } -func OldFuzzerCmd(fuzzer, executor, name, OS, arch, fwdAddr, sandbox string, procs int, +func OldFuzzerCmd(fuzzer, executor, name, OS, arch, fwdAddr, sandbox string, sandboxArg, procs int, cover, test, optionalFlags bool, slowdown int) string { var optional *OptionalFuzzerArgs if optionalFlags { - optional = &OptionalFuzzerArgs{Slowdown: slowdown} + optional = &OptionalFuzzerArgs{Slowdown: slowdown, SandboxArg: sandboxArg} } return FuzzerCmd(&FuzzerCmdArgs{Fuzzer: fuzzer, Executor: executor, Name: name, - OS: OS, Arch: arch, FwdAddr: fwdAddr, Sandbox: sandbox, Procs: procs, - Verbosity: 0, Cover: cover, Debug: false, Test: test, Runtest: false, + OS: OS, Arch: arch, FwdAddr: fwdAddr, Sandbox: sandbox, + Procs: procs, Verbosity: 0, Cover: cover, Debug: false, Test: test, Runtest: false, Optional: optional}) } -func ExecprogCmd(execprog, executor, OS, arch, sandbox string, repeat, threaded, collide bool, +func ExecprogCmd(execprog, executor, OS, arch, sandbox string, sandboxArg int, repeat, threaded, collide bool, procs, faultCall, faultNth int, optionalFlags bool, slowdown int, progFile string) string { repeatCount := 1 if repeat { @@ -501,6 +503,7 @@ func ExecprogCmd(execprog, executor, OS, arch, sandbox string, repeat, threaded, if optionalFlags { optionalArg += " " + tool.OptionalFlags([]tool.Flag{ {Name: "slowdown", Value: fmt.Sprint(slowdown)}, + {Name: "sandboxArg", Value: fmt.Sprint(sandboxArg)}, }) } diff --git a/pkg/instance/instance_test.go b/pkg/instance/instance_test.go index 2f5c08625..843334ebd 100644 --- a/pkg/instance/instance_test.go +++ b/pkg/instance/instance_test.go @@ -16,7 +16,7 @@ import ( func TestFuzzerCmd(t *testing.T) { // IMPORTANT: if this test fails, do not fix it by changing flags here! - // Test how an old version of syz-fuzzer parses flags genereated by the current FuzzerCmd. + // Test how an old version of syz-fuzzer parses flags generated by the current FuzzerCmd. // This actually happens in syz-ci when we test a patch for an old bug and use an old syz-fuzzer/execprog. flags := flag.NewFlagSet("", flag.ContinueOnError) flagName := flags.String("name", "", "unique name for manager") @@ -29,11 +29,11 @@ func TestFuzzerCmd(t *testing.T) { flagTest := flags.Bool("test", false, "enable image testing mode") // used by syz-ci flagExecutor := flags.String("executor", "./syz-executor", "path to executor binary") flagSignal := flags.Bool("cover", false, "collect feedback signals (coverage)") - flagSandbox := flags.String("sandbox", "none", "sandbox for fuzzing (none/setuid/namespace)") + flagSandbox := flags.String("sandbox", "none", "sandbox for fuzzing (none/setuid/namespace/android)") flagDebug := flags.Bool("debug", false, "debug output from executor") flagV := flags.Int("v", 0, "verbosity") cmdLine := OldFuzzerCmd(os.Args[0], "/myexecutor", "myname", targets.Linux, targets.I386, "localhost:1234", - "namespace", 3, true, true, false, 5) + "namespace", 23, 3, true, true, false, 5) args := strings.Split(cmdLine, " ")[1:] if err := flags.Parse(args); err != nil { t.Fatal(err) @@ -96,10 +96,10 @@ func TestExecprogCmd(t *testing.T) { // sets this flag and never relies on the default value. flagCollide := flags.Bool("collide", false, "collide syscalls to provoke data races") flagSignal := flags.Bool("cover", false, "collect feedback signals (coverage)") - flagSandbox := flags.String("sandbox", "none", "sandbox for fuzzing (none/setuid/namespace)") + flagSandbox := flags.String("sandbox", "none", "sandbox for fuzzing (none/setuid/namespace/android)") flagSlowdown := flags.Int("slowdown", 1, "") cmdLine := ExecprogCmd(os.Args[0], "/myexecutor", targets.FreeBSD, targets.I386, - "namespace", true, false, true, 7, 2, 3, true, 10, "myprog") + "namespace", 3, true, false, true, 7, 2, 3, true, 10, "myprog") args := strings.Split(cmdLine, " ")[1:] if err := tool.ParseFlags(flags, args); err != nil { t.Fatal(err) diff --git a/pkg/ipc/ipc.go b/pkg/ipc/ipc.go index 839fdcd4c..5537ce0f8 100644 --- a/pkg/ipc/ipc.go +++ b/pkg/ipc/ipc.go @@ -70,7 +70,8 @@ type Config struct { UseForkServer bool // use extended protocol with handshake // Flags are configuation flags, defined above. - Flags EnvFlags + Flags EnvFlags + SandboxArg int Timeouts targets.Timeouts } @@ -504,9 +505,10 @@ const ( ) type handshakeReq struct { - magic uint64 - flags uint64 // env flags - pid uint64 + magic uint64 + flags uint64 // env flags + pid uint64 + sandboxArg uint64 } type handshakeReply struct { @@ -686,9 +688,10 @@ func (c *command) close() { // handshake sends handshakeReq and waits for handshakeReply. func (c *command) handshake() error { req := &handshakeReq{ - magic: inMagic, - flags: uint64(c.config.Flags), - pid: uint64(c.pid), + magic: inMagic, + flags: uint64(c.config.Flags), + pid: uint64(c.pid), + sandboxArg: uint64(c.config.SandboxArg), } reqData := (*[unsafe.Sizeof(*req)]byte)(unsafe.Pointer(req))[:] if _, err := c.outwp.Write(reqData); err != nil { diff --git a/pkg/ipc/ipc_test.go b/pkg/ipc/ipc_test.go index fd89eae9b..3456852c5 100644 --- a/pkg/ipc/ipc_test.go +++ b/pkg/ipc/ipc_test.go @@ -103,6 +103,7 @@ func TestExecute(t *testing.T) { UseShmem: useShmem, UseForkServer: useForkServer, Timeouts: timeouts, + SandboxArg: 0, } env, err := MakeEnv(cfg, 0) if err != nil { @@ -144,6 +145,7 @@ func TestParallel(t *testing.T) { UseShmem: useShmem, UseForkServer: useForkServer, Timeouts: timeouts, + SandboxArg: 0, } const P = 10 errs := make(chan error, P) diff --git a/pkg/ipc/ipcconfig/ipcconfig.go b/pkg/ipc/ipcconfig/ipcconfig.go index 5be4d4b39..15e1dfde0 100644 --- a/pkg/ipc/ipcconfig/ipcconfig.go +++ b/pkg/ipc/ipcconfig/ipcconfig.go @@ -12,12 +12,13 @@ import ( ) var ( - flagExecutor = flag.String("executor", "./syz-executor", "path to executor binary") - flagThreaded = flag.Bool("threaded", true, "use threaded mode in executor") - flagSignal = flag.Bool("cover", false, "collect feedback signals (coverage)") - flagSandbox = flag.String("sandbox", "none", "sandbox for fuzzing (none/setuid/namespace/android)") - flagDebug = flag.Bool("debug", false, "debug output from executor") - flagSlowdown = flag.Int("slowdown", 1, "execution slowdown caused by emulation/instrumentation") + flagExecutor = flag.String("executor", "./syz-executor", "path to executor binary") + flagThreaded = flag.Bool("threaded", true, "use threaded mode in executor") + flagSignal = flag.Bool("cover", false, "collect feedback signals (coverage)") + flagSandbox = flag.String("sandbox", "none", "sandbox for fuzzing (none/setuid/namespace/android)") + flagSandboxArg = flag.Int("sandbox_arg", 0, "argument for sandbox runner to adjust it via config") + flagDebug = flag.Bool("debug", false, "debug output from executor") + flagSlowdown = flag.Int("slowdown", 1, "execution slowdown caused by emulation/instrumentation") ) func Default(target *prog.Target) (*ipc.Config, *ipc.ExecOpts, error) { @@ -36,6 +37,7 @@ func Default(target *prog.Target) (*ipc.Config, *ipc.ExecOpts, error) { if err != nil { return nil, nil, err } + c.SandboxArg = *flagSandboxArg c.Flags |= sandboxFlags c.UseShmem = sysTarget.ExecutorUsesShmem c.UseForkServer = sysTarget.ExecutorUsesForkServer diff --git a/pkg/mgrconfig/config.go b/pkg/mgrconfig/config.go index 2c3d19475..9073c482c 100644 --- a/pkg/mgrconfig/config.go +++ b/pkg/mgrconfig/config.go @@ -125,6 +125,11 @@ type Config struct { // "android": emulate permissions of an untrusted Android app (supported only on Linux) Sandbox string `json:"sandbox"` + // This value is passed as an argument to executor and allows to adjust sandbox behavior + // via manager config. For example you can switch between system and user accounts based + // on this value. + SandboxArg int `json:"sandbox_arg"` + // Use KCOV coverage (default: true). Cover bool `json:"cover"` // Use coverage filter. Supported types of filter: diff --git a/syz-manager/manager.go b/syz-manager/manager.go index 202168a31..8f2a44f79 100644 --- a/syz-manager/manager.go +++ b/syz-manager/manager.go @@ -774,8 +774,9 @@ func (mgr *Manager) runInstanceInner(index int, instanceName string) (*report.Re Test: false, Runtest: false, Optional: &instance.OptionalFuzzerArgs{ - Slowdown: mgr.cfg.Timeouts.Slowdown, - RawCover: mgr.cfg.RawCover, + Slowdown: mgr.cfg.Timeouts.Slowdown, + RawCover: mgr.cfg.RawCover, + SandboxArg: mgr.cfg.SandboxArg, }, } cmd := instance.FuzzerCmd(args) diff --git a/tools/syz-prog2c/prog2c.go b/tools/syz-prog2c/prog2c.go index 7cea629f8..e7b1ae9b1 100644 --- a/tools/syz-prog2c/prog2c.go +++ b/tools/syz-prog2c/prog2c.go @@ -24,7 +24,8 @@ var ( flagRepeat = flag.Int("repeat", 1, "repeat program that many times (<=0 - infinitely)") flagProcs = flag.Int("procs", 1, "number of parallel processes") flagSlowdown = flag.Int("slowdown", 1, "execution slowdown caused by emulation/instrumentation") - flagSandbox = flag.String("sandbox", "", "sandbox to use (none, setuid, namespace)") + flagSandbox = flag.String("sandbox", "", "sandbox to use (none, setuid, namespace, android)") + flagSandboxArg = flag.Int("sandbox_arg", 0, "argument for executor to customize its behavior") flagProg = flag.String("prog", "", "file with program to convert (required)") flagHandleSegv = flag.Bool("segv", false, "catch and ignore SIGSEGV") flagUseTmpDir = flag.Bool("tmpdir", false, "create a temporary dir and execute inside it") @@ -76,6 +77,7 @@ func main() { Procs: *flagProcs, Slowdown: *flagSlowdown, Sandbox: *flagSandbox, + SandboxArg: *flagSandboxArg, Leak: *flagLeak, NetInjection: features["tun"].Enabled, NetDevices: features["net_dev"].Enabled, diff --git a/tools/syz-runtest/runtest.go b/tools/syz-runtest/runtest.go index 621c3e958..3bf05c8b0 100644 --- a/tools/syz-runtest/runtest.go +++ b/tools/syz-runtest/runtest.go @@ -188,7 +188,8 @@ func (mgr *Manager) boot(name string, index int) (*report.Report, error) { Test: false, Runtest: true, Optional: &instance.OptionalFuzzerArgs{ - Slowdown: mgr.cfg.Timeouts.Slowdown, + Slowdown: mgr.cfg.Timeouts.Slowdown, + SandboxArg: mgr.cfg.SandboxArg, }, } cmd := instance.FuzzerCmd(args) diff --git a/tools/syz-testbuild/testbuild.go b/tools/syz-testbuild/testbuild.go index 961fb2e4c..9fe1dd3f1 100644 --- a/tools/syz-testbuild/testbuild.go +++ b/tools/syz-testbuild/testbuild.go @@ -49,6 +49,7 @@ var ( flagBisectBin = flag.String("bisect_bin", "", "path to bisection binaries") flagSyzkaller = flag.String("syzkaller", ".", "path to built syzkaller") flagSandbox = flag.String("sandbox", "namespace", "sandbox to use for testing") + flagSandboxArg = flag.Int("sandbox_arg", 0, "an argument for sandbox runner") ) const ( @@ -68,18 +69,19 @@ func main() { } defer os.RemoveAll(dir) cfg := &mgrconfig.Config{ - RawTarget: *flagOS + "/" + *flagArch, - HTTP: ":0", - Workdir: dir, - KernelSrc: *flagKernelSrc, - KernelObj: *flagKernelSrc, - Syzkaller: *flagSyzkaller, - Sandbox: *flagSandbox, - SSHUser: "root", - Procs: 1, - Cover: false, - Type: vmType, - VM: json.RawMessage([]byte(fmt.Sprintf(`{ "count": %v, "cpu": 2, "mem": 2048 }`, numTests))), + RawTarget: *flagOS + "/" + *flagArch, + HTTP: ":0", + Workdir: dir, + KernelSrc: *flagKernelSrc, + KernelObj: *flagKernelSrc, + Syzkaller: *flagSyzkaller, + Sandbox: *flagSandbox, + SandboxArg: *flagSandboxArg, + SSHUser: "root", + Procs: 1, + Cover: false, + Type: vmType, + VM: json.RawMessage([]byte(fmt.Sprintf(`{ "count": %v, "cpu": 2, "mem": 2048 }`, numTests))), Derived: mgrconfig.Derived{ TargetOS: *flagOS, TargetArch: *flagArch, -- cgit mrf-deployment