Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions main.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@


#include <stdlib.h>

#include "seccomp.h"

int main(int argc, char **argv) {
Expand All @@ -13,7 +15,13 @@ int main(int argc, char **argv) {
const char *profile_path = argv[1];
FILE *file = sc_must_read_and_validate_header_from_file(profile_path, &hdr);
sc_must_read_filter_from_file(file, hdr.len_filter, &prog_allow);


fclose(file);
sc_apply_seccomp_filter(&prog_allow);

// Cleanup
free(prog_allow.filter);

fprintf(stderr, "filter loaded okay\n");
return 0;
}
29 changes: 29 additions & 0 deletions seccomp.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,31 @@ FILE* sc_must_read_and_validate_header_from_file(const char *profile_path, struc
if (num_read < sizeof(struct sc_seccomp_file_header)) {
die("short read on seccomp header: %zu", num_read);
}
if (hdr->header[0] != 'S' || hdr->header[1] != 'C') {
die("unexpected seccomp header: %x%x", hdr->header[0], hdr->header[1]);
}
if (hdr->version != SC_SECCOMP_VERSION) {
die("unsupported seccomp version: %u (expected %u)", hdr->version, SC_SECCOMP_VERSION);
}
if (hdr->unrestricted > 1) {
die("invalid unrestricted field: %u (must be 0 or 1)", hdr->unrestricted);
}
// Validate reserved2 is all zeros
for (int i = 0; i < sizeof(hdr->reserved2); i++) {
if (hdr->reserved2[i] != 0) {
die("reserved field must be zero at byte %d", i);
}
}
if (hdr->len_filter == 0) {
die("seccomp filter cannot be empty");
}
if (hdr->len_filter % sizeof(struct sock_filter) != 0) {
die("seccomp filter length must be multiple of %zu, got %u",
sizeof(struct sock_filter), hdr->len_filter);
}
if (hdr->len_filter > MAX_BPF_SIZE) {
die("seccomp filter too large: %u", hdr->len_filter);
}
return file;
}

Expand All @@ -53,9 +78,13 @@ void sc_must_read_filter_from_file(FILE *file, uint32_t len_bytes, struct sock_f
}
size_t num_read = fread(prog->filter, 1, len_bytes, file);
if (ferror(file)) {
free(prog->filter);
prog->filter = NULL;
die("cannot read filter");
}
if (num_read != len_bytes) {
free(prog->filter);
prog->filter = NULL;
die("short read for filter %zu != %i", num_read, len_bytes);
}
}
Expand Down
6 changes: 4 additions & 2 deletions seccomp.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@

#include <linux/filter.h>

#define SC_SECCOMP_VERSION 1

struct sc_seccomp_file_header {
char header[2];
uint8_t version;
uint8_t unrestricted;
uint8_t unrestricted; // Must be 0 or 1

uint32_t len_filter;
uint8_t reserved2[16];
uint8_t reserved2[16]; // Reserved for future use, must be zero
};

void sc_must_read_filter_from_file(FILE *file, uint32_t len_bytes, struct sock_fprog *prog);
Expand Down
103 changes: 103 additions & 0 deletions unit-tests/unit-tests.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ static void test_must_read_and_validate_header_from_file__happy(void)
.header[0] = 'S',
.header[1] = 'C',
.version = 1,
.len_filter = 8, // Valid filter length (1 sock_filter)
};
char *profile = NULL;
int fd = 0;
Expand All @@ -27,10 +28,112 @@ static void test_must_read_and_validate_header_from_file__happy(void)
g_assert_true(file != NULL);
}

static void test_must_read_and_validate_header_from_file__missing_header(void)
{
struct sc_seccomp_file_header hdr = {};

if (g_test_subprocess())
{
char *profile = NULL;
int fd = 0;
make_seccomp_profile(&hdr, &fd, &profile);
FILE *file =
sc_must_read_and_validate_header_from_file(profile, &hdr);
g_assert_not_reached();
g_assert_null(file);
}

g_test_trap_subprocess(NULL, 0, 0);
g_test_trap_assert_failed();
g_test_trap_assert_stderr("unexpected seccomp header: 00\n");
}

static void test_must_read_and_validate_header_from_file__invalid_version(void)
{
struct sc_seccomp_file_header hdr = {
.header[0] = 'S',
.header[1] = 'C',
.version = 99, // Invalid version
};

if (g_test_subprocess())
{
char *profile = NULL;
int fd = 0;
make_seccomp_profile(&hdr, &fd, &profile);
FILE *file =
sc_must_read_and_validate_header_from_file(profile, &hdr);
g_assert_not_reached();
g_assert_null(file);
}

g_test_trap_subprocess(NULL, 0, 0);
g_test_trap_assert_failed();
g_test_trap_assert_stderr("*unsupported seccomp version: 99*");
}

static void test_must_read_and_validate_header_from_file__filter_too_large(void)
{
struct sc_seccomp_file_header hdr = {
.header[0] = 'S',
.header[1] = 'C',
.version = 1,
.len_filter = 64 * 1024, // Too large
};

if (g_test_subprocess())
{
char *profile = NULL;
int fd = 0;
make_seccomp_profile(&hdr, &fd, &profile);
FILE *file =
sc_must_read_and_validate_header_from_file(profile, &hdr);
g_assert_not_reached();
g_assert_null(file);
}

g_test_trap_subprocess(NULL, 0, 0);
g_test_trap_assert_failed();
g_test_trap_assert_stderr("*seccomp filter too large*");
}

static void test_must_read_and_validate_header_from_file__filter_empty(void)
{
struct sc_seccomp_file_header hdr = {
.header[0] = 'S',
.header[1] = 'C',
.version = 1,
.len_filter = 0, // Empty
};

if (g_test_subprocess())
{
char *profile = NULL;
int fd = 0;
make_seccomp_profile(&hdr, &fd, &profile);
FILE *file =
sc_must_read_and_validate_header_from_file(profile, &hdr);
g_assert_not_reached();
g_assert_null(file);
}

g_test_trap_subprocess(NULL, 0, 0);
g_test_trap_assert_failed();
g_test_trap_assert_stderr("*seccomp filter cannot be empty*");
}

static void __attribute__((constructor)) init(void)
{
g_test_add_func("/seccomp/must_read_and_validate_header_from_file/happy",
test_must_read_and_validate_header_from_file__happy);
g_test_add_func("/seccomp/must_read_and_validate_header_from_file/missing_header",
test_must_read_and_validate_header_from_file__missing_header);
g_test_add_func("/seccomp/must_read_and_validate_header_from_file/invalid_version",
test_must_read_and_validate_header_from_file__invalid_version);
g_test_add_func("/seccomp/must_read_and_validate_header_from_file/filter_too_large",
test_must_read_and_validate_header_from_file__filter_too_large);
g_test_add_func("/seccomp/must_read_and_validate_header_from_file/filter_empty",
test_must_read_and_validate_header_from_file__filter_empty);
}

int main(int argc, char **argv)
Expand Down