Skip to content

Commit

Permalink
checkpolicy: add libfuzz based fuzzer
Browse files Browse the repository at this point in the history
Introduce a libfuzz[1] based fuzzer testing the parsing and policy
generation code used within checkpolicy(8) and checkmodule(8), similar
to the fuzzer for secilc(8).
The fuzzer will work on generated source policy input and try to parse,
link, expand, optimize, sort and output it.

Build the fuzzer in the oss-fuzz script.

[1]: https://llvm.org/docs/LibFuzzer.html

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
  • Loading branch information
cgzones committed Oct 1, 2021
1 parent 0b83397 commit a5472f6
Show file tree
Hide file tree
Showing 7 changed files with 452 additions and 2 deletions.
3 changes: 3 additions & 0 deletions checkpolicy/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ y.tab.c: policy_parse.y
lex.yy.c: policy_scan.l y.tab.c
$(LEX) policy_scan.l

# helper target for fuzzing
checkobjects: $(CHECKOBJS)

install: all
-mkdir -p $(DESTDIR)$(BINDIR)
-mkdir -p $(DESTDIR)$(MANDIR)/man8
Expand Down
195 changes: 195 additions & 0 deletions checkpolicy/fuzz/checkpolicy-fuzzer.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
#include <assert.h>
#include <unistd.h>
#include <sys/mman.h>

#include <sepol/debug.h>
#include <sepol/kernel_to_cil.h>
#include <sepol/kernel_to_conf.h>
#include <sepol/policydb/policydb.h>
#include <sepol/policydb/services.h>
#include <sepol/policydb/hierarchy.h>
#include <sepol/policydb/expand.h>
#include <sepol/policydb/link.h>

#include "queue.h"

extern int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);

extern int mlspol;
extern policydb_t *policydbp;
extern queue_t id_queue;
extern unsigned int policydb_errors;

extern int yynerrs;
extern FILE *yyin;
extern void init_parser(int);
extern int yyparse(void);
extern void yyrestart(FILE *);
extern void set_source_file(const char *name);

static ssize_t full_write(int fd, const void *buf, size_t count)
{
ssize_t written = 0;

while (count > 0) {
ssize_t ret = write(fd, buf, count);
if (ret < 0) {
if (errno == EINTR)
continue;

return ret;
}

if (ret == 0)
break;

written += ret;
buf = (const unsigned char *)buf + (size_t)ret;
count -= (size_t)ret;
}

return written;
}

static int read_source_policy(policydb_t *p, const uint8_t *data, size_t size)
{
int fd;
ssize_t wr;

fd = memfd_create("fuzz-input", MFD_CLOEXEC);
if (fd < 0)
return -1;

wr = full_write(fd, data, size);
if (wr < 0 || (size_t)wr != size) {
close(fd);
return -1;
}

fsync(fd);

yynerrs = 0;

yyin = fdopen(fd, "re");
if (!yyin) {
close(fd);
return -1;
}

rewind(yyin);

set_source_file("fuzz-input");

id_queue = queue_create();
if (id_queue == NULL) {
fclose(yyin);
return -1;
}

policydbp = p;
mlspol = p->mls;

init_parser(1);

if (yyparse() || policydb_errors) {
queue_destroy(id_queue);
fclose(yyin);
return -1;
}

rewind(yyin);
init_parser(2);
set_source_file("fuzz-input");
yyrestart(yyin);

if (yyparse() || policydb_errors) {
queue_destroy(id_queue);
fclose(yyin);
return -1;
}

queue_destroy(id_queue);
fclose(yyin);

return 0;
}

static int write_binary_policy(policydb_t *p, FILE *outfp)
{
struct policy_file pf;

policy_file_init(&pf);
pf.type = PF_USE_STDIO;
pf.fp = outfp;
return policydb_write(p, &pf);
}

int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
{
policydb_t parsepolicydb = {};
policydb_t kernpolicydb = {};
sidtab_t sidtab = {};
FILE *devnull = NULL;

sepol_debug(0);
sepol_set_policydb(&parsepolicydb);
sepol_set_sidtab(&sidtab);

if (policydb_init(&parsepolicydb))
goto exit;

parsepolicydb.policy_type = POLICY_BASE;
parsepolicydb.mls = 1;
parsepolicydb.handle_unknown = DENY_UNKNOWN;
policydb_set_target_platform(&parsepolicydb, SEPOL_TARGET_SELINUX);

if (read_source_policy(&parsepolicydb, data, size))
goto exit;

if (hierarchy_check_constraints(NULL, &parsepolicydb))
goto exit;

if (link_modules(NULL, &parsepolicydb, NULL, 0, 0))
goto exit;

if (policydb_init(&kernpolicydb))
goto exit;

if (expand_module(NULL, &parsepolicydb, &kernpolicydb, 0, 1))
goto exit;

assert(kernpolicydb.policyvers == POLICYDB_VERSION_MAX);
assert(kernpolicydb.policy_type == POLICY_KERN);
assert(kernpolicydb.handle_unknown == SEPOL_DENY_UNKNOWN);
assert(kernpolicydb.mls == 1);

if (policydb_load_isids(&kernpolicydb, &sidtab))
goto exit;

if (policydb_optimize(&kernpolicydb))
goto exit;

if (policydb_sort_ocontexts(&kernpolicydb))
goto exit;

devnull = fopen("/dev/null", "we");
if (devnull == NULL)
goto exit;

(void) write_binary_policy(&kernpolicydb, devnull);

(void) sepol_kernel_policydb_to_conf(devnull, &kernpolicydb);

(void) sepol_kernel_policydb_to_cil(devnull, &kernpolicydb);

exit:
if (devnull != NULL)
fclose(devnull);

sepol_sidtab_destroy(&sidtab);
policydb_destroy(&kernpolicydb);
policydb_destroy(&parsepolicydb);

/* Non-zero return values are reserved for future use. */
return 0;
}
101 changes: 101 additions & 0 deletions checkpolicy/fuzz/checkpolicy-fuzzer.dict
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# Keyword dictionary

"clone"
"common"
"class"
"constrain"
"validatetrans"
"inherits"
"sid"
"role"
"roles"
"roleattribute"
"attribute_role"
"types"
"typealias"
"typeattribute"
"typebounds"
"type"
"bool"
"tunable"
"if"
"else"
"alias"
"attribute"
"expandattribute"
"type_transition"
"type_member"
"type_change"
"role_transition"
"range_transition"
"sensitivity"
"dominance"
"category"
"level"
"range"
"mlsconstrain"
"mlsvalidatetrans"
"user"
"neverallow"
"allow"
"auditallow"
"auditdeny"
"dontaudit"
"allowxperm"
"auditallowxperm"
"dontauditxperm"
"neverallowxperm"
"source"
"target"
"sameuser"
"module"
"require"
"optional"
"or"
"and"
"not"
"xor"
"eq"
"true"
"false"
"dom"
"domby"
"incomp"
"fscon"
"ibpkeycon"
"ibendportcon"
"portcon"
"netifcon"
"nodecon"
"pirqcon"
"iomemcon"
"ioportcon"
"pcidevicecon"
"devicetreecon"
"fs_use_xattr"
"fs_use_task"
"fs_use_trans"
"genfscon"
"r1"
"r2"
"r3"
"u1"
"u2"
"u3"
"t1"
"t2"
"t3"
"l1"
"l2"
"h1"
"h2"
"policycap"
"permissive"
"default_user"
"default_role"
"default_type"
"default_range"
"low-high"
"high"
"low"
"glblub"
60 changes: 60 additions & 0 deletions checkpolicy/fuzz/min_pol.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
class process
class blk_file
class chr_file
class dir
class fifo_file
class file
class lnk_file
class sock_file
sid kernel
sid security
sid unlabeled
sid fs
sid file
sid file_labels
sid init
sid any_socket
sid port
sid netif
sid netmsg
sid node
sid igmp_packet
sid icmp_socket
sid tcp_socket
sid sysctl_modprobe
sid sysctl
sid sysctl_fs
sid sysctl_kernel
sid sysctl_net
sid sysctl_net_unix
sid sysctl_vm
sid sysctl_dev
sid kmod
sid policy
sid scmp_packet
sid devnull
class process { dyntransition transition }
default_role { blk_file } source;
default_role { chr_file } source;
default_role { dir } source;
default_role { fifo_file } source;
default_role { file } source;
default_role { lnk_file } source;
default_role { sock_file } source;
type sys_isid;
typealias sys_isid alias { dpkg_script_t rpm_script_t };
allow sys_isid self : process { dyntransition transition };
role sys_role;
role sys_role types { sys_isid };
user sys_user roles sys_role;
sid kernel sys_user:sys_role:sys_isid
sid security sys_user:sys_role:sys_isid
sid unlabeled sys_user:sys_role:sys_isid
sid file sys_user:sys_role:sys_isid
sid port sys_user:sys_role:sys_isid
sid netif sys_user:sys_role:sys_isid
sid netmsg sys_user:sys_role:sys_isid
sid node sys_user:sys_role:sys_isid
sid devnull sys_user:sys_role:sys_isid
fs_use_trans devpts sys_user:sys_role:sys_isid;
fs_use_trans devtmpfs sys_user:sys_role:sys_isid;
Loading

0 comments on commit a5472f6

Please sign in to comment.