Message ID | 20200323164415.12943-8-kpsingh@chromium.org (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | MAC and Audit policy using eBPF (KRSI) | expand |
On 3/23/20 9:44 AM, KP Singh wrote: > From: KP Singh <kpsingh@google.com> > > * Load/attach a BPF program to the file_mprotect (int) and > bprm_committed_creds (void) LSM hooks. > * Perform an action that triggers the hook. > * Verify if the audit event was received using a shared global > result variable. > > Signed-off-by: KP Singh <kpsingh@google.com> > Reviewed-by: Brendan Jackman <jackmanb@google.com> > Reviewed-by: Florent Revest <revest@google.com> > Reviewed-by: Thomas Garnier <thgarnie@google.com> > --- > tools/testing/selftests/bpf/lsm_helpers.h | 19 +++ > .../selftests/bpf/prog_tests/lsm_test.c | 112 ++++++++++++++++++ > .../selftests/bpf/progs/lsm_int_hook.c | 54 +++++++++ > .../selftests/bpf/progs/lsm_void_hook.c | 41 +++++++ > 4 files changed, 226 insertions(+) > create mode 100644 tools/testing/selftests/bpf/lsm_helpers.h > create mode 100644 tools/testing/selftests/bpf/prog_tests/lsm_test.c > create mode 100644 tools/testing/selftests/bpf/progs/lsm_int_hook.c > create mode 100644 tools/testing/selftests/bpf/progs/lsm_void_hook.c > > diff --git a/tools/testing/selftests/bpf/lsm_helpers.h b/tools/testing/selftests/bpf/lsm_helpers.h > new file mode 100644 > index 000000000000..3de230df93db > --- /dev/null > +++ b/tools/testing/selftests/bpf/lsm_helpers.h > @@ -0,0 +1,19 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > + > +/* > + * Copyright (C) 2020 Google LLC. > + */ > +#ifndef _LSM_HELPERS_H > +#define _LSM_HELPERS_H > + > +struct lsm_prog_result { > + /* This ensures that the LSM Hook only monitors the PID requested > + * by the loader > + */ > + __u32 monitored_pid; > + /* The number of calls to the prog for the monitored PID. > + */ > + __u32 count; > +}; > + > +#endif /* _LSM_HELPERS_H */ > diff --git a/tools/testing/selftests/bpf/prog_tests/lsm_test.c b/tools/testing/selftests/bpf/prog_tests/lsm_test.c > new file mode 100644 > index 000000000000..5fd6b8f569f7 > --- /dev/null > +++ b/tools/testing/selftests/bpf/prog_tests/lsm_test.c > @@ -0,0 +1,112 @@ > +// SPDX-License-Identifier: GPL-2.0 > + > +/* > + * Copyright (C) 2020 Google LLC. > + */ > + > +#include <test_progs.h> > +#include <sys/mman.h> > +#include <sys/wait.h> > +#include <unistd.h> > +#include <malloc.h> > +#include <stdlib.h> > + > +#include "lsm_helpers.h" > +#include "lsm_void_hook.skel.h" > +#include "lsm_int_hook.skel.h" > + > +char *LS_ARGS[] = {"true", NULL}; > + > +int heap_mprotect(void) > +{ > + void *buf; > + long sz; > + > + sz = sysconf(_SC_PAGESIZE); > + if (sz < 0) > + return sz; > + > + buf = memalign(sz, 2 * sz); > + if (buf == NULL) > + return -ENOMEM; > + > + return mprotect(buf, sz, PROT_READ | PROT_EXEC); "buf" is leaking memory here. > +} > + > +int exec_ls(struct lsm_prog_result *result) > +{ > + int child_pid; > + > + child_pid = fork(); > + if (child_pid == 0) { > + result->monitored_pid = getpid(); > + execvp(LS_ARGS[0], LS_ARGS); > + return -EINVAL; > + } else if (child_pid > 0) > + return wait(NULL); > + > + return -EINVAL; > +} > + > +void test_lsm_void_hook(void) > +{ > + struct lsm_prog_result *result; > + struct lsm_void_hook *skel = NULL; > + int err, duration = 0; > + > + skel = lsm_void_hook__open_and_load(); > + if (CHECK(!skel, "skel_load", "lsm_void_hook skeleton failed\n")) > + goto close_prog; > + > + err = lsm_void_hook__attach(skel); > + if (CHECK(err, "attach", "lsm_void_hook attach failed: %d\n", err)) > + goto close_prog; > + > + result = &skel->bss->result; > + > + err = exec_ls(result); > + if (CHECK(err < 0, "exec_ls", "err %d errno %d\n", err, errno)) > + goto close_prog; > + > + if (CHECK(result->count != 1, "count", "count = %d", result->count)) > + goto close_prog; > + > + CHECK_FAIL(result->count != 1); I think the above if (CHECK(result->count != 1, "count", "count = %d", result->count)) goto close_prog; CHECK_FAIL(result->count != 1); can be replaced with CHECK(result->count != 1, "count", "count = %d", result->count); > + > +close_prog: > + lsm_void_hook__destroy(skel); > +} > + > +void test_lsm_int_hook(void) > +{ > + struct lsm_prog_result *result; > + struct lsm_int_hook *skel = NULL; > + int err, duration = 0; > + > + skel = lsm_int_hook__open_and_load(); > + if (CHECK(!skel, "skel_load", "lsm_int_hook skeleton failed\n")) > + goto close_prog; > + > + err = lsm_int_hook__attach(skel); > + if (CHECK(err, "attach", "lsm_int_hook attach failed: %d\n", err)) > + goto close_prog; > + > + result = &skel->bss->result; > + result->monitored_pid = getpid(); > + > + err = heap_mprotect(); > + if (CHECK(errno != EPERM, "heap_mprotect", "want errno=EPERM, got %d\n", > + errno)) > + goto close_prog; > + > + CHECK_FAIL(result->count != 1); > + > +close_prog: > + lsm_int_hook__destroy(skel); > +} > + > +void test_lsm_test(void) > +{ > + test_lsm_void_hook(); > + test_lsm_int_hook(); > +} > diff --git a/tools/testing/selftests/bpf/progs/lsm_int_hook.c b/tools/testing/selftests/bpf/progs/lsm_int_hook.c > new file mode 100644 > index 000000000000..1c5028ddca61 > --- /dev/null > +++ b/tools/testing/selftests/bpf/progs/lsm_int_hook.c > @@ -0,0 +1,54 @@ > +// SPDX-License-Identifier: GPL-2.0 > + > +/* > + * Copyright 2020 Google LLC. > + */ > + > +#include <linux/bpf.h> > +#include <stdbool.h> > +#include <bpf/bpf_helpers.h> > +#include <bpf/bpf_tracing.h> > +#include <errno.h> > +#include "lsm_helpers.h" > + > +char _license[] SEC("license") = "GPL"; > + > +struct lsm_prog_result result = { > + .monitored_pid = 0, > + .count = 0, > +}; > + > +/* > + * Define some of the structs used in the BPF program. > + * Only the field names and their sizes need to be the > + * same as the kernel type, the order is irrelevant. > + */ > +struct mm_struct { > + unsigned long start_brk, brk; > +} __attribute__((preserve_access_index)); > + > +struct vm_area_struct { > + unsigned long vm_start, vm_end; > + struct mm_struct *vm_mm; > +} __attribute__((preserve_access_index)); > + > +SEC("lsm/file_mprotect") > +int BPF_PROG(test_int_hook, struct vm_area_struct *vma, > + unsigned long reqprot, unsigned long prot, int ret) > +{ > + if (ret != 0) > + return ret; > + > + __u32 pid = bpf_get_current_pid_tgid(); In user space, we assign monitored_pid with getpid() which is the process pid. Here pid = bpf_get_current_pid_tgid() actually got tid in the kernel. Although it does not matter in this particular example, maybe still use bpf_get_current_pid_tgid() >> 32 to get process pid to be consistent. The same for lsm_void_hook.c. > + int is_heap = 0; > + > + is_heap = (vma->vm_start >= vma->vm_mm->start_brk && > + vma->vm_end <= vma->vm_mm->brk); > + > + if (is_heap && result.monitored_pid == pid) { > + result.count++; > + ret = -EPERM; > + } > + > + return ret; > +} > diff --git a/tools/testing/selftests/bpf/progs/lsm_void_hook.c b/tools/testing/selftests/bpf/progs/lsm_void_hook.c > new file mode 100644 > index 000000000000..4d01a8536413 > --- /dev/null > +++ b/tools/testing/selftests/bpf/progs/lsm_void_hook.c > @@ -0,0 +1,41 @@ > +// SPDX-License-Identifier: GPL-2.0 > + > +/* > + * Copyright (C) 2020 Google LLC. > + */ > + > +#include <linux/bpf.h> > +#include <stdbool.h> > +#include <bpf/bpf_helpers.h> > +#include <bpf/bpf_tracing.h> > +#include <errno.h> > +#include "lsm_helpers.h" > + > +char _license[] SEC("license") = "GPL"; > + > +struct lsm_prog_result result = { > + .monitored_pid = 0, > + .count = 0, > +}; > + > +/* > + * Define some of the structs used in the BPF program. > + * Only the field names and their sizes need to be the > + * same as the kernel type, the order is irrelevant. > + */ > +struct linux_binprm { > + const char *filename; > +} __attribute__((preserve_access_index)); > + > +SEC("lsm/bprm_committed_creds") > +int BPF_PROG(test_void_hook, struct linux_binprm *bprm) > +{ > + __u32 pid = bpf_get_current_pid_tgid(); > + char fmt[] = "lsm(bprm_committed_creds): process executed %s\n"; > + > + bpf_trace_printk(fmt, sizeof(fmt), bprm->filename); > + if (result.monitored_pid == pid) > + result.count++; > + > + return 0; > +} > Could you also upddate tools/testing/selftests/bpf/config file so people will know what config options are needed to run the self tests properly?
On 23-Mär 13:04, Yonghong Song wrote: > > > On 3/23/20 9:44 AM, KP Singh wrote: > > From: KP Singh <kpsingh@google.com> > > > > * Load/attach a BPF program to the file_mprotect (int) and > > bprm_committed_creds (void) LSM hooks. > > * Perform an action that triggers the hook. > > * Verify if the audit event was received using a shared global > > result variable. > > > > Signed-off-by: KP Singh <kpsingh@google.com> > > Reviewed-by: Brendan Jackman <jackmanb@google.com> > > Reviewed-by: Florent Revest <revest@google.com> > > Reviewed-by: Thomas Garnier <thgarnie@google.com> > > --- > > tools/testing/selftests/bpf/lsm_helpers.h | 19 +++ > > .../selftests/bpf/prog_tests/lsm_test.c | 112 ++++++++++++++++++ > > .../selftests/bpf/progs/lsm_int_hook.c | 54 +++++++++ > > .../selftests/bpf/progs/lsm_void_hook.c | 41 +++++++ > > 4 files changed, 226 insertions(+) > > create mode 100644 tools/testing/selftests/bpf/lsm_helpers.h > > create mode 100644 tools/testing/selftests/bpf/prog_tests/lsm_test.c > > create mode 100644 tools/testing/selftests/bpf/progs/lsm_int_hook.c > > create mode 100644 tools/testing/selftests/bpf/progs/lsm_void_hook.c > > > > diff --git a/tools/testing/selftests/bpf/lsm_helpers.h b/tools/testing/selftests/bpf/lsm_helpers.h > > new file mode 100644 > > index 000000000000..3de230df93db > > --- /dev/null > > +++ b/tools/testing/selftests/bpf/lsm_helpers.h > > @@ -0,0 +1,19 @@ > > +/* SPDX-License-Identifier: GPL-2.0 */ > > + > > +/* > > + * Copyright (C) 2020 Google LLC. > > + */ > > +#ifndef _LSM_HELPERS_H > > +#define _LSM_HELPERS_H > > + > > +struct lsm_prog_result { > > + /* This ensures that the LSM Hook only monitors the PID requested > > + * by the loader > > + */ > > + __u32 monitored_pid; > > + /* The number of calls to the prog for the monitored PID. > > + */ > > + __u32 count; > > +}; > > + > > +#endif /* _LSM_HELPERS_H */ > > diff --git a/tools/testing/selftests/bpf/prog_tests/lsm_test.c b/tools/testing/selftests/bpf/prog_tests/lsm_test.c > > new file mode 100644 > > index 000000000000..5fd6b8f569f7 > > --- /dev/null > > +++ b/tools/testing/selftests/bpf/prog_tests/lsm_test.c > > @@ -0,0 +1,112 @@ > > +// SPDX-License-Identifier: GPL-2.0 > > + > > +/* > > + * Copyright (C) 2020 Google LLC. > > + */ > > + > > +#include <test_progs.h> > > +#include <sys/mman.h> > > +#include <sys/wait.h> > > +#include <unistd.h> > > +#include <malloc.h> > > +#include <stdlib.h> > > + > > +#include "lsm_helpers.h" > > +#include "lsm_void_hook.skel.h" > > +#include "lsm_int_hook.skel.h" > > + > > +char *LS_ARGS[] = {"true", NULL}; > > + > > +int heap_mprotect(void) > > +{ > > + void *buf; > > + long sz; > > + > > + sz = sysconf(_SC_PAGESIZE); > > + if (sz < 0) > > + return sz; > > + > > + buf = memalign(sz, 2 * sz); > > + if (buf == NULL) > > + return -ENOMEM; > > + > > + return mprotect(buf, sz, PROT_READ | PROT_EXEC); > > "buf" is leaking memory here. > > > +} > > + > > +int exec_ls(struct lsm_prog_result *result) > > +{ > > + int child_pid; > > + > > + child_pid = fork(); > > + if (child_pid == 0) { > > + result->monitored_pid = getpid(); > > + execvp(LS_ARGS[0], LS_ARGS); > > + return -EINVAL; > > + } else if (child_pid > 0) > > + return wait(NULL); > > + > > + return -EINVAL; > > +} > > + > > +void test_lsm_void_hook(void) > > +{ > > + struct lsm_prog_result *result; > > + struct lsm_void_hook *skel = NULL; > > + int err, duration = 0; > > + > > + skel = lsm_void_hook__open_and_load(); > > + if (CHECK(!skel, "skel_load", "lsm_void_hook skeleton failed\n")) > > + goto close_prog; > > + > > + err = lsm_void_hook__attach(skel); > > + if (CHECK(err, "attach", "lsm_void_hook attach failed: %d\n", err)) > > + goto close_prog; > > + > > + result = &skel->bss->result; > > + > > + err = exec_ls(result); > > + if (CHECK(err < 0, "exec_ls", "err %d errno %d\n", err, errno)) > > + goto close_prog; > > + > > + if (CHECK(result->count != 1, "count", "count = %d", result->count)) > > + goto close_prog; > > + > > + CHECK_FAIL(result->count != 1); > > I think the above > if (CHECK(result->count != 1, "count", "count = %d", result->count)) > goto close_prog; > > CHECK_FAIL(result->count != 1); > can be replaced with > CHECK(result->count != 1, "count", "count = %d", result->count); Thanks, and updated for test_lsm_int_hook as well. > > > + > > +close_prog: > > + lsm_void_hook__destroy(skel); > > +} > > + > > +void test_lsm_int_hook(void) > > +{ > > + struct lsm_prog_result *result; > > + struct lsm_int_hook *skel = NULL; > > + int err, duration = 0; > > + > > + skel = lsm_int_hook__open_and_load(); > > + if (CHECK(!skel, "skel_load", "lsm_int_hook skeleton failed\n")) > > + goto close_prog; > > + > > + err = lsm_int_hook__attach(skel); > > + if (CHECK(err, "attach", "lsm_int_hook attach failed: %d\n", err)) > > + goto close_prog; > > + > > + result = &skel->bss->result; > > + result->monitored_pid = getpid(); > > + > > + err = heap_mprotect(); > > + if (CHECK(errno != EPERM, "heap_mprotect", "want errno=EPERM, got %d\n", > > + errno)) > > + goto close_prog; > > + > > + CHECK_FAIL(result->count != 1); > > + > > +close_prog: > > + lsm_int_hook__destroy(skel); > > +} > > + > > +void test_lsm_test(void) > > +{ > > + test_lsm_void_hook(); > > + test_lsm_int_hook(); > > +} > > diff --git a/tools/testing/selftests/bpf/progs/lsm_int_hook.c b/tools/testing/selftests/bpf/progs/lsm_int_hook.c > > new file mode 100644 > > index 000000000000..1c5028ddca61 > > --- /dev/null > > +++ b/tools/testing/selftests/bpf/progs/lsm_int_hook.c > > @@ -0,0 +1,54 @@ > > +// SPDX-License-Identifier: GPL-2.0 > > + > > +/* > > + * Copyright 2020 Google LLC. > > + */ > > + > > +#include <linux/bpf.h> > > +#include <stdbool.h> > > +#include <bpf/bpf_helpers.h> > > +#include <bpf/bpf_tracing.h> > > +#include <errno.h> > > +#include "lsm_helpers.h" > > + > > +char _license[] SEC("license") = "GPL"; > > + > > +struct lsm_prog_result result = { > > + .monitored_pid = 0, > > + .count = 0, > > +}; > > + > > +/* > > + * Define some of the structs used in the BPF program. > > + * Only the field names and their sizes need to be the > > + * same as the kernel type, the order is irrelevant. > > + */ > > +struct mm_struct { > > + unsigned long start_brk, brk; > > +} __attribute__((preserve_access_index)); > > + > > +struct vm_area_struct { > > + unsigned long vm_start, vm_end; > > + struct mm_struct *vm_mm; > > +} __attribute__((preserve_access_index)); > > + > > +SEC("lsm/file_mprotect") > > +int BPF_PROG(test_int_hook, struct vm_area_struct *vma, > > + unsigned long reqprot, unsigned long prot, int ret) > > +{ > > + if (ret != 0) > > + return ret; > > + > > + __u32 pid = bpf_get_current_pid_tgid(); > > In user space, we assign monitored_pid with getpid() > which is the process pid. Here > pid = bpf_get_current_pid_tgid() > actually got tid in the kernel. > > Although it does not matter in this particular example, > maybe still use > bpf_get_current_pid_tgid() >> 32 > to get process pid to be consistent. > > The same for lsm_void_hook.c. Done. Thanks! > > > + int is_heap = 0; > > + > > + is_heap = (vma->vm_start >= vma->vm_mm->start_brk && > > + vma->vm_end <= vma->vm_mm->brk); > > + > > + if (is_heap && result.monitored_pid == pid) { > > + result.count++; > > + ret = -EPERM; > > + } > > + > > + return ret; > > +} > > diff --git a/tools/testing/selftests/bpf/progs/lsm_void_hook.c b/tools/testing/selftests/bpf/progs/lsm_void_hook.c > > new file mode 100644 > > index 000000000000..4d01a8536413 > > --- /dev/null > > +++ b/tools/testing/selftests/bpf/progs/lsm_void_hook.c > > @@ -0,0 +1,41 @@ > > +// SPDX-License-Identifier: GPL-2.0 > > + > > +/* > > + * Copyright (C) 2020 Google LLC. > > + */ > > + > > +#include <linux/bpf.h> > > +#include <stdbool.h> > > +#include <bpf/bpf_helpers.h> > > +#include <bpf/bpf_tracing.h> > > +#include <errno.h> > > +#include "lsm_helpers.h" > > + > > +char _license[] SEC("license") = "GPL"; > > + > > +struct lsm_prog_result result = { > > + .monitored_pid = 0, > > + .count = 0, > > +}; > > + > > +/* > > + * Define some of the structs used in the BPF program. > > + * Only the field names and their sizes need to be the > > + * same as the kernel type, the order is irrelevant. > > + */ > > +struct linux_binprm { > > + const char *filename; > > +} __attribute__((preserve_access_index)); > > + > > +SEC("lsm/bprm_committed_creds") > > +int BPF_PROG(test_void_hook, struct linux_binprm *bprm) > > +{ > > + __u32 pid = bpf_get_current_pid_tgid(); > > + char fmt[] = "lsm(bprm_committed_creds): process executed %s\n"; > > + > > + bpf_trace_printk(fmt, sizeof(fmt), bprm->filename); > > + if (result.monitored_pid == pid) > > + result.count++; > > + > > + return 0; > > +} > > > > Could you also upddate tools/testing/selftests/bpf/config file > so people will know what config options are needed to run the > self tests properly? Added CONFIG_BPF_LSM and CONFIG_SECURITY to the list. - KP
On Mon, Mar 23, 2020 at 9:45 AM KP Singh <kpsingh@chromium.org> wrote: > > From: KP Singh <kpsingh@google.com> > > * Load/attach a BPF program to the file_mprotect (int) and > bprm_committed_creds (void) LSM hooks. > * Perform an action that triggers the hook. > * Verify if the audit event was received using a shared global > result variable. > > Signed-off-by: KP Singh <kpsingh@google.com> > Reviewed-by: Brendan Jackman <jackmanb@google.com> > Reviewed-by: Florent Revest <revest@google.com> > Reviewed-by: Thomas Garnier <thgarnie@google.com> > --- > tools/testing/selftests/bpf/lsm_helpers.h | 19 +++ > .../selftests/bpf/prog_tests/lsm_test.c | 112 ++++++++++++++++++ > .../selftests/bpf/progs/lsm_int_hook.c | 54 +++++++++ > .../selftests/bpf/progs/lsm_void_hook.c | 41 +++++++ > 4 files changed, 226 insertions(+) > create mode 100644 tools/testing/selftests/bpf/lsm_helpers.h > create mode 100644 tools/testing/selftests/bpf/prog_tests/lsm_test.c > create mode 100644 tools/testing/selftests/bpf/progs/lsm_int_hook.c > create mode 100644 tools/testing/selftests/bpf/progs/lsm_void_hook.c > > diff --git a/tools/testing/selftests/bpf/lsm_helpers.h b/tools/testing/selftests/bpf/lsm_helpers.h > new file mode 100644 > index 000000000000..3de230df93db > --- /dev/null > +++ b/tools/testing/selftests/bpf/lsm_helpers.h > @@ -0,0 +1,19 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > + > +/* > + * Copyright (C) 2020 Google LLC. > + */ > +#ifndef _LSM_HELPERS_H > +#define _LSM_HELPERS_H > + > +struct lsm_prog_result { > + /* This ensures that the LSM Hook only monitors the PID requested > + * by the loader > + */ > + __u32 monitored_pid; > + /* The number of calls to the prog for the monitored PID. > + */ > + __u32 count; > +}; > + Having this extra header just for this simple struct... On BPF side it's easier and nicer to just use global variables. Can you please drop helper and just pass two variables in prog_test part? > +#endif /* _LSM_HELPERS_H */ > diff --git a/tools/testing/selftests/bpf/prog_tests/lsm_test.c b/tools/testing/selftests/bpf/prog_tests/lsm_test.c > new file mode 100644 > index 000000000000..5fd6b8f569f7 > --- /dev/null > +++ b/tools/testing/selftests/bpf/prog_tests/lsm_test.c > @@ -0,0 +1,112 @@ > +// SPDX-License-Identifier: GPL-2.0 > + > +/* > + * Copyright (C) 2020 Google LLC. > + */ > + > +#include <test_progs.h> > +#include <sys/mman.h> > +#include <sys/wait.h> > +#include <unistd.h> > +#include <malloc.h> > +#include <stdlib.h> > + > +#include "lsm_helpers.h" > +#include "lsm_void_hook.skel.h" > +#include "lsm_int_hook.skel.h" > + > +char *LS_ARGS[] = {"true", NULL}; > + > +int heap_mprotect(void) > +{ > + void *buf; > + long sz; > + > + sz = sysconf(_SC_PAGESIZE); > + if (sz < 0) > + return sz; > + > + buf = memalign(sz, 2 * sz); > + if (buf == NULL) > + return -ENOMEM; > + > + return mprotect(buf, sz, PROT_READ | PROT_EXEC); > +} > + > +int exec_ls(struct lsm_prog_result *result) > +{ > + int child_pid; > + > + child_pid = fork(); > + if (child_pid == 0) { > + result->monitored_pid = getpid(); monitored_pid needed here only > + execvp(LS_ARGS[0], LS_ARGS); > + return -EINVAL; > + } else if (child_pid > 0) > + return wait(NULL); > + > + return -EINVAL; > +} > + > +void test_lsm_void_hook(void) > +{ > + struct lsm_prog_result *result; > + struct lsm_void_hook *skel = NULL; > + int err, duration = 0; > + > + skel = lsm_void_hook__open_and_load(); > + if (CHECK(!skel, "skel_load", "lsm_void_hook skeleton failed\n")) > + goto close_prog; > + > + err = lsm_void_hook__attach(skel); > + if (CHECK(err, "attach", "lsm_void_hook attach failed: %d\n", err)) > + goto close_prog; > + > + result = &skel->bss->result; if you define variables directly, you'll access them easily as skel->bss->monitored_pid and skel->bss->count, no problem, right? > + > + err = exec_ls(result); > + if (CHECK(err < 0, "exec_ls", "err %d errno %d\n", err, errno)) > + goto close_prog; > + > + if (CHECK(result->count != 1, "count", "count = %d", result->count)) > + goto close_prog; > + > + CHECK_FAIL(result->count != 1); > + > +close_prog: > + lsm_void_hook__destroy(skel); > +} > + > +void test_lsm_int_hook(void) > +{ > + struct lsm_prog_result *result; > + struct lsm_int_hook *skel = NULL; > + int err, duration = 0; > + > + skel = lsm_int_hook__open_and_load(); > + if (CHECK(!skel, "skel_load", "lsm_int_hook skeleton failed\n")) > + goto close_prog; > + > + err = lsm_int_hook__attach(skel); > + if (CHECK(err, "attach", "lsm_int_hook attach failed: %d\n", err)) > + goto close_prog; > + > + result = &skel->bss->result; > + result->monitored_pid = getpid(); > + > + err = heap_mprotect(); > + if (CHECK(errno != EPERM, "heap_mprotect", "want errno=EPERM, got %d\n", > + errno)) > + goto close_prog; > + > + CHECK_FAIL(result->count != 1); > + > +close_prog: > + lsm_int_hook__destroy(skel); > +} > + > +void test_lsm_test(void) > +{ > + test_lsm_void_hook(); > + test_lsm_int_hook(); These should be subtests (see test__start_subtest() usage). Also, I'm not sure why you need two separate BPF programs, why not create one and use it for two subtests? > +} > diff --git a/tools/testing/selftests/bpf/progs/lsm_int_hook.c b/tools/testing/selftests/bpf/progs/lsm_int_hook.c > new file mode 100644 > index 000000000000..1c5028ddca61 > --- /dev/null > +++ b/tools/testing/selftests/bpf/progs/lsm_int_hook.c consider it a nit because not every test follows this, but using progs/test_whatever.c for BPF side and prog_test/whatever.c makes my life a bit easier. > @@ -0,0 +1,54 @@ > +// SPDX-License-Identifier: GPL-2.0 > + > +/* > + * Copyright 2020 Google LLC. > + */ > + > +#include <linux/bpf.h> > +#include <stdbool.h> > +#include <bpf/bpf_helpers.h> > +#include <bpf/bpf_tracing.h> > +#include <errno.h> > +#include "lsm_helpers.h" > + > +char _license[] SEC("license") = "GPL"; > + > +struct lsm_prog_result result = { > + .monitored_pid = 0, > + .count = 0, > +}; > + > +/* > + * Define some of the structs used in the BPF program. > + * Only the field names and their sizes need to be the > + * same as the kernel type, the order is irrelevant. > + */ > +struct mm_struct { > + unsigned long start_brk, brk; > +} __attribute__((preserve_access_index)); > + > +struct vm_area_struct { > + unsigned long vm_start, vm_end; > + struct mm_struct *vm_mm; > +} __attribute__((preserve_access_index)); Why not just using vmlinux.h instead? > + > +SEC("lsm/file_mprotect") > +int BPF_PROG(test_int_hook, struct vm_area_struct *vma, > + unsigned long reqprot, unsigned long prot, int ret) > +{ > + if (ret != 0) > + return ret; > + > + __u32 pid = bpf_get_current_pid_tgid(); > + int is_heap = 0; > + > + is_heap = (vma->vm_start >= vma->vm_mm->start_brk && > + vma->vm_end <= vma->vm_mm->brk); > + > + if (is_heap && result.monitored_pid == pid) { > + result.count++; > + ret = -EPERM; > + } > + > + return ret; > +} > diff --git a/tools/testing/selftests/bpf/progs/lsm_void_hook.c b/tools/testing/selftests/bpf/progs/lsm_void_hook.c > new file mode 100644 > index 000000000000..4d01a8536413 > --- /dev/null > +++ b/tools/testing/selftests/bpf/progs/lsm_void_hook.c > @@ -0,0 +1,41 @@ > +// SPDX-License-Identifier: GPL-2.0 > + > +/* > + * Copyright (C) 2020 Google LLC. > + */ > + > +#include <linux/bpf.h> > +#include <stdbool.h> > +#include <bpf/bpf_helpers.h> > +#include <bpf/bpf_tracing.h> > +#include <errno.h> > +#include "lsm_helpers.h" > + > +char _license[] SEC("license") = "GPL"; > + > +struct lsm_prog_result result = { > + .monitored_pid = 0, > + .count = 0, > +}; > + > +/* > + * Define some of the structs used in the BPF program. > + * Only the field names and their sizes need to be the > + * same as the kernel type, the order is irrelevant. > + */ > +struct linux_binprm { > + const char *filename; > +} __attribute__((preserve_access_index)); > + > +SEC("lsm/bprm_committed_creds") > +int BPF_PROG(test_void_hook, struct linux_binprm *bprm) > +{ > + __u32 pid = bpf_get_current_pid_tgid(); > + char fmt[] = "lsm(bprm_committed_creds): process executed %s\n"; Try static char fmt[] = "..." instead and then compare BPF assembly before and after, you'll be amazed ;) > + > + bpf_trace_printk(fmt, sizeof(fmt), bprm->filename); is this part of test? > + if (result.monitored_pid == pid) > + result.count++; > + > + return 0; > +} > -- > 2.20.1 >
On 24-Mär 16:54, Andrii Nakryiko wrote: > On Mon, Mar 23, 2020 at 9:45 AM KP Singh <kpsingh@chromium.org> wrote: > > > > From: KP Singh <kpsingh@google.com> > > > > * Load/attach a BPF program to the file_mprotect (int) and > > bprm_committed_creds (void) LSM hooks. > > * Perform an action that triggers the hook. > > * Verify if the audit event was received using a shared global > > result variable. > > > > Signed-off-by: KP Singh <kpsingh@google.com> > > Reviewed-by: Brendan Jackman <jackmanb@google.com> > > Reviewed-by: Florent Revest <revest@google.com> > > Reviewed-by: Thomas Garnier <thgarnie@google.com> > > --- > > tools/testing/selftests/bpf/lsm_helpers.h | 19 +++ > > .../selftests/bpf/prog_tests/lsm_test.c | 112 ++++++++++++++++++ > > .../selftests/bpf/progs/lsm_int_hook.c | 54 +++++++++ > > .../selftests/bpf/progs/lsm_void_hook.c | 41 +++++++ > > 4 files changed, 226 insertions(+) > > create mode 100644 tools/testing/selftests/bpf/lsm_helpers.h > > create mode 100644 tools/testing/selftests/bpf/prog_tests/lsm_test.c > > create mode 100644 tools/testing/selftests/bpf/progs/lsm_int_hook.c > > create mode 100644 tools/testing/selftests/bpf/progs/lsm_void_hook.c > > > > diff --git a/tools/testing/selftests/bpf/lsm_helpers.h b/tools/testing/selftests/bpf/lsm_helpers.h > > new file mode 100644 > > index 000000000000..3de230df93db > > --- /dev/null > > +++ b/tools/testing/selftests/bpf/lsm_helpers.h > > @@ -0,0 +1,19 @@ > > +/* SPDX-License-Identifier: GPL-2.0 */ > > + > > +/* > > + * Copyright (C) 2020 Google LLC. > > + */ > > +#ifndef _LSM_HELPERS_H > > +#define _LSM_HELPERS_H > > + > > +struct lsm_prog_result { > > + /* This ensures that the LSM Hook only monitors the PID requested > > + * by the loader > > + */ > > + __u32 monitored_pid; > > + /* The number of calls to the prog for the monitored PID. > > + */ > > + __u32 count; > > +}; > > + > > Having this extra header just for this simple struct... On BPF side > it's easier and nicer to just use global variables. Can you please > drop helper and just pass two variables in prog_test part? Removed the header and moved to global variables. One less file to worry about :) > > > +#endif /* _LSM_HELPERS_H */ > > diff --git a/tools/testing/selftests/bpf/prog_tests/lsm_test.c b/tools/testing/selftests/bpf/prog_tests/lsm_test.c > > new file mode 100644 > > index 000000000000..5fd6b8f569f7 > > --- /dev/null > > +++ b/tools/testing/selftests/bpf/prog_tests/lsm_test.c > > @@ -0,0 +1,112 @@ > > +// SPDX-License-Identifier: GPL-2.0 > > + > > +/* > > + * Copyright (C) 2020 Google LLC. > > + */ > > + > > +#include <test_progs.h> > > +#include <sys/mman.h> > > +#include <sys/wait.h> > > +#include <unistd.h> > > +#include <malloc.h> > > +#include <stdlib.h> > > + > > +#include "lsm_helpers.h" > > +#include "lsm_void_hook.skel.h" > > +#include "lsm_int_hook.skel.h" > > + > > +char *LS_ARGS[] = {"true", NULL}; > > + > > +int heap_mprotect(void) > > +{ > > + void *buf; > > + long sz; > > + > > + sz = sysconf(_SC_PAGESIZE); > > + if (sz < 0) > > + return sz; > > + > > + buf = memalign(sz, 2 * sz); > > + if (buf == NULL) > > + return -ENOMEM; > > + > > + return mprotect(buf, sz, PROT_READ | PROT_EXEC); > > +} > > + > > +int exec_ls(struct lsm_prog_result *result) > > +{ > > + int child_pid; > > + > > + child_pid = fork(); > > + if (child_pid == 0) { > > + result->monitored_pid = getpid(); > > monitored_pid needed here only > > > + execvp(LS_ARGS[0], LS_ARGS); > > + return -EINVAL; > > + } else if (child_pid > 0) > > + return wait(NULL); > > + > > + return -EINVAL; > > +} > > + > > +void test_lsm_void_hook(void) > > +{ > > + struct lsm_prog_result *result; > > + struct lsm_void_hook *skel = NULL; > > + int err, duration = 0; > > + > > + skel = lsm_void_hook__open_and_load(); > > + if (CHECK(!skel, "skel_load", "lsm_void_hook skeleton failed\n")) > > + goto close_prog; > > + > > + err = lsm_void_hook__attach(skel); > > + if (CHECK(err, "attach", "lsm_void_hook attach failed: %d\n", err)) > > + goto close_prog; > > + > > + result = &skel->bss->result; > > if you define variables directly, you'll access them easily as > skel->bss->monitored_pid and skel->bss->count, no problem, right? Yes. Updated. > > > + > > + err = exec_ls(result); > > + if (CHECK(err < 0, "exec_ls", "err %d errno %d\n", err, errno)) > > + goto close_prog; > > + > > + if (CHECK(result->count != 1, "count", "count = %d", result->count)) > > + goto close_prog; > > + > > + CHECK_FAIL(result->count != 1); > > + > > +close_prog: > > + lsm_void_hook__destroy(skel); > > +} > > + > > +void test_lsm_int_hook(void) > > +{ > > + struct lsm_prog_result *result; > > + struct lsm_int_hook *skel = NULL; > > + int err, duration = 0; > > + > > + skel = lsm_int_hook__open_and_load(); > > + if (CHECK(!skel, "skel_load", "lsm_int_hook skeleton failed\n")) > > + goto close_prog; > > + > > + err = lsm_int_hook__attach(skel); > > + if (CHECK(err, "attach", "lsm_int_hook attach failed: %d\n", err)) > > + goto close_prog; > > + > > + result = &skel->bss->result; > > + result->monitored_pid = getpid(); > > + > > + err = heap_mprotect(); > > + if (CHECK(errno != EPERM, "heap_mprotect", "want errno=EPERM, got %d\n", > > + errno)) > > + goto close_prog; > > + > > + CHECK_FAIL(result->count != 1); > > + > > +close_prog: > > + lsm_int_hook__destroy(skel); > > +} > > + > > +void test_lsm_test(void) > > +{ > > + test_lsm_void_hook(); > > + test_lsm_int_hook(); > > These should be subtests (see test__start_subtest() usage). Also, I'm > not sure why you need two separate BPF programs, why not create one > and use it for two subtests? Thanks! I simplified it much more based on your feedback. Now we just have two files: * prog_tests/test_lsm.c which defines "test_lsm_test which does an exec and an mprotect and verifies the global variables and the return of the mprotect. These are fairly simple, so we can drop the need for subtests. * progs/lsm.c which has both bprm_committed_creds and file_mprotect. > > > > +} > > diff --git a/tools/testing/selftests/bpf/progs/lsm_int_hook.c b/tools/testing/selftests/bpf/progs/lsm_int_hook.c > > new file mode 100644 > > index 000000000000..1c5028ddca61 > > --- /dev/null > > +++ b/tools/testing/selftests/bpf/progs/lsm_int_hook.c > > consider it a nit because not every test follows this, but using > progs/test_whatever.c for BPF side and prog_test/whatever.c makes my > life a bit easier. I am all for uniformity :) Updated. > > > > @@ -0,0 +1,54 @@ > > +// SPDX-License-Identifier: GPL-2.0 > > + > > +/* > > + * Copyright 2020 Google LLC. > > + */ > > + > > +#include <linux/bpf.h> > > +#include <stdbool.h> > > +#include <bpf/bpf_helpers.h> > > +#include <bpf/bpf_tracing.h> > > +#include <errno.h> > > +#include "lsm_helpers.h" > > + > > +char _license[] SEC("license") = "GPL"; > > + > > +struct lsm_prog_result result = { > > + .monitored_pid = 0, > > + .count = 0, > > +}; > > + > > +/* > > + * Define some of the structs used in the BPF program. > > + * Only the field names and their sizes need to be the > > + * same as the kernel type, the order is irrelevant. > > + */ > > +struct mm_struct { > > + unsigned long start_brk, brk; > > +} __attribute__((preserve_access_index)); > > + > > +struct vm_area_struct { > > + unsigned long vm_start, vm_end; > > + struct mm_struct *vm_mm; > > +} __attribute__((preserve_access_index)); > > Why not just using vmlinux.h instead? Thanks, updated. > > > + > > +SEC("lsm/file_mprotect") > > +int BPF_PROG(test_int_hook, struct vm_area_struct *vma, > > + unsigned long reqprot, unsigned long prot, int ret) > > +{ > > + if (ret != 0) > > + return ret; > > + > > + __u32 pid = bpf_get_current_pid_tgid(); > > + int is_heap = 0; > > + > > + is_heap = (vma->vm_start >= vma->vm_mm->start_brk && > > + vma->vm_end <= vma->vm_mm->brk); > > + > > + if (is_heap && result.monitored_pid == pid) { > > + result.count++; > > + ret = -EPERM; > > + } > > + > > + return ret; > > +} > > diff --git a/tools/testing/selftests/bpf/progs/lsm_void_hook.c b/tools/testing/selftests/bpf/progs/lsm_void_hook.c > > new file mode 100644 > > index 000000000000..4d01a8536413 > > --- /dev/null > > +++ b/tools/testing/selftests/bpf/progs/lsm_void_hook.c > > @@ -0,0 +1,41 @@ > > +// SPDX-License-Identifier: GPL-2.0 > > + > > +/* > > + * Copyright (C) 2020 Google LLC. > > + */ > > + > > +#include <linux/bpf.h> > > +#include <stdbool.h> > > +#include <bpf/bpf_helpers.h> > > +#include <bpf/bpf_tracing.h> > > +#include <errno.h> > > +#include "lsm_helpers.h" > > + > > +char _license[] SEC("license") = "GPL"; > > + > > +struct lsm_prog_result result = { > > + .monitored_pid = 0, > > + .count = 0, > > +}; > > + > > +/* > > + * Define some of the structs used in the BPF program. > > + * Only the field names and their sizes need to be the > > + * same as the kernel type, the order is irrelevant. > > + */ > > +struct linux_binprm { > > + const char *filename; > > +} __attribute__((preserve_access_index)); > > + > > +SEC("lsm/bprm_committed_creds") > > +int BPF_PROG(test_void_hook, struct linux_binprm *bprm) > > +{ > > + __u32 pid = bpf_get_current_pid_tgid(); > > + char fmt[] = "lsm(bprm_committed_creds): process executed %s\n"; > > Try static char fmt[] = "..." instead and then compare BPF assembly > before and after, you'll be amazed ;) > > > + > > + bpf_trace_printk(fmt, sizeof(fmt), bprm->filename); > > is this part of test? Not really, removed. - KP > > > + if (result.monitored_pid == pid) > > + result.count++; > > + > > + return 0; > > +} > > -- > > 2.20.1 > >
diff --git a/tools/testing/selftests/bpf/lsm_helpers.h b/tools/testing/selftests/bpf/lsm_helpers.h new file mode 100644 index 000000000000..3de230df93db --- /dev/null +++ b/tools/testing/selftests/bpf/lsm_helpers.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* + * Copyright (C) 2020 Google LLC. + */ +#ifndef _LSM_HELPERS_H +#define _LSM_HELPERS_H + +struct lsm_prog_result { + /* This ensures that the LSM Hook only monitors the PID requested + * by the loader + */ + __u32 monitored_pid; + /* The number of calls to the prog for the monitored PID. + */ + __u32 count; +}; + +#endif /* _LSM_HELPERS_H */ diff --git a/tools/testing/selftests/bpf/prog_tests/lsm_test.c b/tools/testing/selftests/bpf/prog_tests/lsm_test.c new file mode 100644 index 000000000000..5fd6b8f569f7 --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/lsm_test.c @@ -0,0 +1,112 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Copyright (C) 2020 Google LLC. + */ + +#include <test_progs.h> +#include <sys/mman.h> +#include <sys/wait.h> +#include <unistd.h> +#include <malloc.h> +#include <stdlib.h> + +#include "lsm_helpers.h" +#include "lsm_void_hook.skel.h" +#include "lsm_int_hook.skel.h" + +char *LS_ARGS[] = {"true", NULL}; + +int heap_mprotect(void) +{ + void *buf; + long sz; + + sz = sysconf(_SC_PAGESIZE); + if (sz < 0) + return sz; + + buf = memalign(sz, 2 * sz); + if (buf == NULL) + return -ENOMEM; + + return mprotect(buf, sz, PROT_READ | PROT_EXEC); +} + +int exec_ls(struct lsm_prog_result *result) +{ + int child_pid; + + child_pid = fork(); + if (child_pid == 0) { + result->monitored_pid = getpid(); + execvp(LS_ARGS[0], LS_ARGS); + return -EINVAL; + } else if (child_pid > 0) + return wait(NULL); + + return -EINVAL; +} + +void test_lsm_void_hook(void) +{ + struct lsm_prog_result *result; + struct lsm_void_hook *skel = NULL; + int err, duration = 0; + + skel = lsm_void_hook__open_and_load(); + if (CHECK(!skel, "skel_load", "lsm_void_hook skeleton failed\n")) + goto close_prog; + + err = lsm_void_hook__attach(skel); + if (CHECK(err, "attach", "lsm_void_hook attach failed: %d\n", err)) + goto close_prog; + + result = &skel->bss->result; + + err = exec_ls(result); + if (CHECK(err < 0, "exec_ls", "err %d errno %d\n", err, errno)) + goto close_prog; + + if (CHECK(result->count != 1, "count", "count = %d", result->count)) + goto close_prog; + + CHECK_FAIL(result->count != 1); + +close_prog: + lsm_void_hook__destroy(skel); +} + +void test_lsm_int_hook(void) +{ + struct lsm_prog_result *result; + struct lsm_int_hook *skel = NULL; + int err, duration = 0; + + skel = lsm_int_hook__open_and_load(); + if (CHECK(!skel, "skel_load", "lsm_int_hook skeleton failed\n")) + goto close_prog; + + err = lsm_int_hook__attach(skel); + if (CHECK(err, "attach", "lsm_int_hook attach failed: %d\n", err)) + goto close_prog; + + result = &skel->bss->result; + result->monitored_pid = getpid(); + + err = heap_mprotect(); + if (CHECK(errno != EPERM, "heap_mprotect", "want errno=EPERM, got %d\n", + errno)) + goto close_prog; + + CHECK_FAIL(result->count != 1); + +close_prog: + lsm_int_hook__destroy(skel); +} + +void test_lsm_test(void) +{ + test_lsm_void_hook(); + test_lsm_int_hook(); +} diff --git a/tools/testing/selftests/bpf/progs/lsm_int_hook.c b/tools/testing/selftests/bpf/progs/lsm_int_hook.c new file mode 100644 index 000000000000..1c5028ddca61 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/lsm_int_hook.c @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Copyright 2020 Google LLC. + */ + +#include <linux/bpf.h> +#include <stdbool.h> +#include <bpf/bpf_helpers.h> +#include <bpf/bpf_tracing.h> +#include <errno.h> +#include "lsm_helpers.h" + +char _license[] SEC("license") = "GPL"; + +struct lsm_prog_result result = { + .monitored_pid = 0, + .count = 0, +}; + +/* + * Define some of the structs used in the BPF program. + * Only the field names and their sizes need to be the + * same as the kernel type, the order is irrelevant. + */ +struct mm_struct { + unsigned long start_brk, brk; +} __attribute__((preserve_access_index)); + +struct vm_area_struct { + unsigned long vm_start, vm_end; + struct mm_struct *vm_mm; +} __attribute__((preserve_access_index)); + +SEC("lsm/file_mprotect") +int BPF_PROG(test_int_hook, struct vm_area_struct *vma, + unsigned long reqprot, unsigned long prot, int ret) +{ + if (ret != 0) + return ret; + + __u32 pid = bpf_get_current_pid_tgid(); + int is_heap = 0; + + is_heap = (vma->vm_start >= vma->vm_mm->start_brk && + vma->vm_end <= vma->vm_mm->brk); + + if (is_heap && result.monitored_pid == pid) { + result.count++; + ret = -EPERM; + } + + return ret; +} diff --git a/tools/testing/selftests/bpf/progs/lsm_void_hook.c b/tools/testing/selftests/bpf/progs/lsm_void_hook.c new file mode 100644 index 000000000000..4d01a8536413 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/lsm_void_hook.c @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Copyright (C) 2020 Google LLC. + */ + +#include <linux/bpf.h> +#include <stdbool.h> +#include <bpf/bpf_helpers.h> +#include <bpf/bpf_tracing.h> +#include <errno.h> +#include "lsm_helpers.h" + +char _license[] SEC("license") = "GPL"; + +struct lsm_prog_result result = { + .monitored_pid = 0, + .count = 0, +}; + +/* + * Define some of the structs used in the BPF program. + * Only the field names and their sizes need to be the + * same as the kernel type, the order is irrelevant. + */ +struct linux_binprm { + const char *filename; +} __attribute__((preserve_access_index)); + +SEC("lsm/bprm_committed_creds") +int BPF_PROG(test_void_hook, struct linux_binprm *bprm) +{ + __u32 pid = bpf_get_current_pid_tgid(); + char fmt[] = "lsm(bprm_committed_creds): process executed %s\n"; + + bpf_trace_printk(fmt, sizeof(fmt), bprm->filename); + if (result.monitored_pid == pid) + result.count++; + + return 0; +}