aboutsummaryrefslogtreecommitdiffstats
path: root/pkg/csource
diff options
context:
space:
mode:
authorAndrey Artemiev <artemiev@google.com>2022-08-06 05:17:33 -0700
committerGitHub <noreply@github.com>2022-08-06 14:17:33 +0200
commit88e3a1226bc591d81c1fb98e83cb63cd4f341c6e (patch)
tree323b7fa492a8d9698e432c1d3bd4514771fc3252 /pkg/csource
parente853abd9a2542fcccb8e1a23eb8ae475500ecaf9 (diff)
pkg/csource, pkg/instance, pkg/ipc, pkg/mgrconfig, tools/syz-prog2c, syz-manager: introduce a new setting 'sandbox_arg' (#3263)
Diffstat (limited to 'pkg/csource')
-rw-r--r--pkg/csource/csource.go22
-rw-r--r--pkg/csource/csource_test.go35
-rw-r--r--pkg/csource/generated.go11
-rw-r--r--pkg/csource/options.go89
-rw-r--r--pkg/csource/options_test.go53
5 files changed, 176 insertions, 34 deletions
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)