Skip to content

Commit 982a6f0

Browse files
committed
[WIP] fuzzer: attach security contexts to programs
Added two new experimental flags that allow the fuzzer to attach simple security contexts to each program that will be executed on the target. The objective is to allow syzkaller to fuzz syscalls under specific security contexts that match the SELinux policy that is loaded on target. The `enforce_policy` flag may be used to enforce the policy before the executor forks and calls the `execute_one()` function. The default Debian security policy does not allow the dynamic transition to `user_u:user_r:user_t:s0` through `setcon()`. Signed-off-by: Rares Constantin <[email protected]>
1 parent 0ac7291 commit 982a6f0

File tree

14 files changed

+144
-55
lines changed

14 files changed

+144
-55
lines changed

executor/common.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ typedef signed int ssize_t;
3434
#else
3535
#include <endian.h> // for htobe*.
3636
#endif
37+
#include <fstream>
3738
#include <stdint.h>
3839
#include <stdio.h> // for fmt arguments
3940
#include <stdlib.h>
@@ -601,6 +602,15 @@ static void execute_one(void);
601602
#define WAIT_FLAGS 0
602603
#endif
603604

605+
void enforce_policy(bool enforce)
606+
{
607+
std::ofstream outFile("/sys/fs/selinux/enforce");
608+
if (outFile.is_open()) {
609+
outFile << (enforce ? "1" : "0");
610+
outFile.close();
611+
}
612+
}
613+
604614
#if SYZ_EXECUTOR_USES_FORK_SERVER
605615
#include <signal.h>
606616
#include <sys/types.h>
@@ -636,6 +646,7 @@ static void loop(void)
636646
if (!flag_snapshot)
637647
receive_execute();
638648
#endif
649+
enforce_policy(flag_enforce_policy);
639650
int pid = fork();
640651
if (pid < 0)
641652
fail("clone failed");
@@ -737,6 +748,7 @@ static void loop(void)
737748
errno = 0;
738749
fail("child failed");
739750
}
751+
enforce_policy(false);
740752
reply_execute(0);
741753
#endif
742754
#if SYZ_EXECUTOR || SYZ_USE_TMP_DIR

executor/common_linux.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4455,7 +4455,7 @@ static void getcon(char* context, size_t context_size)
44554455
// - Uses fail() instead of returning an error code
44564456
static void setcon(const char* context)
44574457
{
4458-
char new_context[512];
4458+
char new_context[512] = {0};
44594459

44604460
// Attempt to write the new context
44614461
int fd = open(SELINUX_CONTEXT_FILE, O_WRONLY);
@@ -4470,7 +4470,7 @@ static void setcon(const char* context)
44704470
close(fd);
44714471

44724472
if (bytes_written != (ssize_t)strlen(context))
4473-
failmsg("setcon: could not write entire context", "wrote=%zi, expected=%zu", bytes_written, strlen(context));
4473+
failmsg("setcon: could not write entire context", "context: %s, wrote=%zi, expected=%zu", context, bytes_written, strlen(context));
44744474

44754475
// Validate the transition by checking the context
44764476
getcon(new_context, sizeof(new_context));

executor/executor.cc

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,7 @@ static bool flag_nic_vf;
273273
static bool flag_vhci_injection;
274274
static bool flag_wifi;
275275
static bool flag_delay_kcov_mmap;
276+
static bool flag_enforce_policy;
276277

277278
static bool flag_collect_cover;
278279
static bool flag_collect_signal;
@@ -306,6 +307,7 @@ const uint64 instr_eof = -1;
306307
const uint64 instr_copyin = -2;
307308
const uint64 instr_copyout = -3;
308309
const uint64 instr_setprops = -4;
310+
const uint64 instr_seccontext = -5;
309311

310312
const uint64 arg_const = 0;
311313
const uint64 arg_addr32 = 1;
@@ -847,6 +849,7 @@ void parse_handshake(const handshake_req& req)
847849
flag_wifi = (bool)(req.flags & rpc::ExecEnv::EnableWifi);
848850
flag_delay_kcov_mmap = (bool)(req.flags & rpc::ExecEnv::DelayKcovMmap);
849851
flag_nic_vf = (bool)(req.flags & rpc::ExecEnv::EnableNicVF);
852+
flag_enforce_policy = (bool)(req.flags & rpc::ExecEnv::EnforcePolicy);
850853
}
851854

852855
void receive_execute()
@@ -970,10 +973,25 @@ void execute_one()
970973
memset(&call_props, 0, sizeof(call_props));
971974

972975
read_input(&input_pos); // total number of calls
973-
for (;;) {
976+
for (int index = 0;; index++) {
974977
uint64 call_num = read_input(&input_pos);
975978
if (call_num == instr_eof)
976979
break;
980+
#if GOOS_linux
981+
if (call_num == instr_seccontext) {
982+
if (index) {
983+
fail("seclabel instruction is not the first call\n");
984+
}
985+
uint64 size = read_input(&input_pos);
986+
char seclabel[64]{};
987+
memcpy(seclabel, input_pos, size);
988+
setcon(seclabel);
989+
input_pos += size;
990+
991+
debug_verbose("applied security label: %s\n", seclabel);
992+
continue;
993+
}
994+
#endif
977995
if (call_num == instr_copyin) {
978996
char* addr = (char*)(read_input(&input_pos) + SYZ_DATA_OFFSET);
979997
uint64 typ = read_input(&input_pos);

pkg/flatrpc/flatrpc.fbs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ enum ExecEnv : uint64 (bit_flags) {
142142
SandboxSetuid, // impersonate nobody user
143143
SandboxNamespace, // use namespaces for sandboxing
144144
SandboxAndroid, // use Android sandboxing for the untrusted_app domain
145+
EnforcePolicy, // enforce the loaded SELinux policy
145146
ExtraCover, // collect extra coverage
146147
EnableTun, // setup and use /dev/tun for packet injection
147148
EnableNetDev, // setup more network devices for testing

pkg/flatrpc/flatrpc.go

Lines changed: 14 additions & 11 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/flatrpc/flatrpc.h

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -566,23 +566,24 @@ enum class ExecEnv : uint64_t {
566566
SandboxSetuid = 32ULL,
567567
SandboxNamespace = 64ULL,
568568
SandboxAndroid = 128ULL,
569-
ExtraCover = 256ULL,
570-
EnableTun = 512ULL,
571-
EnableNetDev = 1024ULL,
572-
EnableNetReset = 2048ULL,
573-
EnableCgroups = 4096ULL,
574-
EnableCloseFds = 8192ULL,
575-
EnableDevlinkPCI = 16384ULL,
576-
EnableVhciInjection = 32768ULL,
577-
EnableWifi = 65536ULL,
578-
DelayKcovMmap = 131072ULL,
579-
EnableNicVF = 262144ULL,
569+
EnforcePolicy = 256ULL,
570+
ExtraCover = 512ULL,
571+
EnableTun = 1024ULL,
572+
EnableNetDev = 2048ULL,
573+
EnableNetReset = 4096ULL,
574+
EnableCgroups = 8192ULL,
575+
EnableCloseFds = 16384ULL,
576+
EnableDevlinkPCI = 32768ULL,
577+
EnableVhciInjection = 65536ULL,
578+
EnableWifi = 131072ULL,
579+
DelayKcovMmap = 262144ULL,
580+
EnableNicVF = 524288ULL,
580581
NONE = 0,
581-
ANY = 524287ULL
582+
ANY = 1048575ULL
582583
};
583584
FLATBUFFERS_DEFINE_BITMASK_OPERATORS(ExecEnv, uint64_t)
584585

585-
inline const ExecEnv (&EnumValuesExecEnv())[19] {
586+
inline const ExecEnv (&EnumValuesExecEnv())[20] {
586587
static const ExecEnv values[] = {
587588
ExecEnv::Debug,
588589
ExecEnv::Signal,
@@ -592,6 +593,7 @@ inline const ExecEnv (&EnumValuesExecEnv())[19] {
592593
ExecEnv::SandboxSetuid,
593594
ExecEnv::SandboxNamespace,
594595
ExecEnv::SandboxAndroid,
596+
ExecEnv::EnforcePolicy,
595597
ExecEnv::ExtraCover,
596598
ExecEnv::EnableTun,
597599
ExecEnv::EnableNetDev,
@@ -617,6 +619,7 @@ inline const char *EnumNameExecEnv(ExecEnv e) {
617619
case ExecEnv::SandboxSetuid: return "SandboxSetuid";
618620
case ExecEnv::SandboxNamespace: return "SandboxNamespace";
619621
case ExecEnv::SandboxAndroid: return "SandboxAndroid";
622+
case ExecEnv::EnforcePolicy: return "EnforcePolicy";
620623
case ExecEnv::ExtraCover: return "ExtraCover";
621624
case ExecEnv::EnableTun: return "EnableTun";
622625
case ExecEnv::EnableNetDev: return "EnableNetDev";

pkg/fuzzer/fuzzer.go

Lines changed: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -208,20 +208,23 @@ func (fuzzer *Fuzzer) processResult(req *queue.Request, res *queue.Result, flags
208208
}
209209

210210
type Config struct {
211-
Debug bool
212-
Corpus *corpus.Corpus
213-
Logf func(level int, msg string, args ...interface{})
214-
Snapshot bool
215-
Coverage bool
216-
FaultInjection bool
217-
Comparisons bool
218-
Collide bool
219-
EnabledCalls map[*prog.Syscall]bool
220-
NoMutateCalls map[int]bool
221-
FetchRawCover bool
222-
NewInputFilter func(call string) bool
223-
PatchTest bool
224-
ModeKFuzzTest bool
211+
Debug bool
212+
Corpus *corpus.Corpus
213+
Logf func(level int, msg string, args ...interface{})
214+
Snapshot bool
215+
Coverage bool
216+
FaultInjection bool
217+
Comparisons bool
218+
Collide bool
219+
EnabledCalls map[*prog.Syscall]bool
220+
NoMutateCalls map[int]bool
221+
FetchRawCover bool
222+
Sandbox string
223+
SandboxArg int64
224+
AttachSecContext bool
225+
NewInputFilter func(call string) bool
226+
PatchTest bool
227+
ModeKFuzzTest bool
225228
}
226229

227230
func (fuzzer *Fuzzer) triageProgCall(p *prog.Prog, info *flatrpc.CallInfo, call int, triage *map[int]*triageCall) {
@@ -371,6 +374,17 @@ func (fuzzer *Fuzzer) AddCandidates(candidates []Candidate) {
371374
Stat: fuzzer.statExecCandidate,
372375
Important: true,
373376
}
377+
req.Prog.SecContext = ""
378+
if fuzzer.Config.AttachSecContext {
379+
req.Prog.SecContext = "user_u:user_r:user_t:s0"
380+
if fuzzer.Config.Sandbox == "android" {
381+
if fuzzer.Config.SandboxArg == 0 {
382+
req.Prog.SecContext = "u:r:untrusted_app:s0:c512,c768"
383+
} else {
384+
req.Prog.SecContext = ""
385+
}
386+
}
387+
}
374388
fuzzer.enqueue(fuzzer.candidateQueue, req, candidate.Flags|progCandidate, 0)
375389
}
376390
}
@@ -472,6 +486,9 @@ func DefaultExecOpts(cfg *mgrconfig.Config, features flatrpc.Feature, debug bool
472486
if cfg.Cover {
473487
env |= flatrpc.ExecEnvSignal
474488
}
489+
if cfg.Experimental.EnforcePolicy {
490+
env |= flatrpc.ExecEnvEnforcePolicy
491+
}
475492
sandbox, err := flatrpc.SandboxToFlags(cfg.Sandbox)
476493
if err != nil {
477494
panic(fmt.Sprintf("failed to parse sandbox: %v", err))

pkg/fuzzer/job.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,17 @@ func genProgRequest(fuzzer *Fuzzer, rnd *rand.Rand) *queue.Request {
4545
p := fuzzer.target.Generate(rnd,
4646
fuzzer.RecommendedCalls(),
4747
fuzzer.ChoiceTable())
48+
p.SecContext = ""
49+
if fuzzer.Config.AttachSecContext {
50+
p.SecContext = "user_u:user_r:user_t:s0"
51+
if fuzzer.Config.Sandbox == "android" {
52+
if fuzzer.Config.SandboxArg == 0 {
53+
p.SecContext = "u:r:untrusted_app:s0:c512,c768"
54+
} else {
55+
p.SecContext = ""
56+
}
57+
}
58+
}
4859
return &queue.Request{
4960
Prog: p,
5061
ExecOpts: setFlags(flatrpc.ExecFlagCollectSignal),

pkg/mgrconfig/config.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,12 @@ type Experimental struct {
258258

259259
// Enable dynamic discovery and fuzzing of KFuzzTest targets.
260260
EnableKFuzzTest bool `json:"enable_kfuzztest"`
261+
262+
// Enables the policy enforcement for programs that request this before running syscalls.
263+
EnforcePolicy bool `json:"enforce_policy"`
264+
265+
// Allows the fuzzer to attach a security label to each program.
266+
AttachSecContexts bool `json:"attach_seccontexts"`
261267
}
262268

263269
type FocusArea struct {

pkg/rpcserver/runner.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,7 @@ func (runner *Runner) sendRequest(req *queue.Request) error {
329329
avoid |= uint64(1 << id.Proc)
330330
}
331331
}
332+
332333
msg := &flatrpc.HostMessage{
333334
Msg: &flatrpc.HostMessages{
334335
Type: flatrpc.HostMessagesRawExecRequest,

0 commit comments

Comments
 (0)