In the current design (mostly not implemented yet), a "supervisor" is a program that creates (but probably not enforce on itself) a Landlock ruleset which it specifically marks as operating in "supervise" mode. For such a layer (but not other layers below or above it), access not granted by the ruleset, which would normally result in a denial, instead triggers a supervise event, and the thread which caused the event is paused until either the supervisor responds to the event, the event is cancelled due to supervisor termination, or the requesting thread being killed. We define a refcounted structure that represents a supervisor, and will later be exposed to the user-space via a file descriptor. Each supervisor has an event queue and a separate list of events which have been read by the supervisor and is now awaiting response. This allows the future read codepath to not have to iterate over already notified events, but still allow the response codepath to find the event. The event struct is also refcounted, so that it is not tied to the lifetime of the supervisor (e.g. if it dies, the task doing the access that is currently stuck in kernel syscall still holds the event refcount, and can read its status safely). The details of the event structure will be populated in a future patch. The struct is called landlock_supervise_event_kernel so that the uapi header can use the shorter name. Signed-off-by: Tingmao Wang <m@xxxxxxxxxx> --- security/landlock/Makefile | 2 +- security/landlock/supervise.c | 72 +++++++++++++++++++++++++++++++++++ security/landlock/supervise.h | 63 ++++++++++++++++++++++++++++++ 3 files changed, 136 insertions(+), 1 deletion(-) create mode 100644 security/landlock/supervise.c create mode 100644 security/landlock/supervise.h diff --git a/security/landlock/Makefile b/security/landlock/Makefile index b4538b7cf7d2..c9bab22ab0f5 100644 --- a/security/landlock/Makefile +++ b/security/landlock/Makefile @@ -1,6 +1,6 @@ obj-$(CONFIG_SECURITY_LANDLOCK) := landlock.o landlock-y := setup.o syscalls.o object.o ruleset.o \ - cred.o task.o fs.o + cred.o task.o fs.o supervise.o landlock-$(CONFIG_INET) += net.o diff --git a/security/landlock/supervise.c b/security/landlock/supervise.c new file mode 100644 index 000000000000..a3bb6928f453 --- /dev/null +++ b/security/landlock/supervise.c @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Landlock LSM - Implementation specific to landlock-supervise + * + * Copyright © 2025 Tingmao Wang <m@xxxxxxxxxx> + */ + +#include <linux/path.h> +#include <linux/pid.h> +#include <linux/slab.h> +#include <linux/wait_bit.h> + +#include "supervise.h" + +struct landlock_supervisor *landlock_create_supervisor(void) +{ + struct landlock_supervisor *supervisor; + + supervisor = kzalloc(sizeof(*supervisor), GFP_KERNEL_ACCOUNT); + if (!supervisor) + return ERR_PTR(-ENOMEM); + refcount_set(&supervisor->usage, 1); + supervisor->next_event_id = 1; + spin_lock_init(&supervisor->lock); + INIT_LIST_HEAD(&supervisor->event_queue); + INIT_LIST_HEAD(&supervisor->notified_events); + init_waitqueue_head(&supervisor->poll_event_wq); + return supervisor; +} + +void landlock_get_supervisor(struct landlock_supervisor *const supervisor) +{ + refcount_inc(&supervisor->usage); +} + +static void +deny_and_put_event(struct landlock_supervise_event_kernel *const event) +{ + cmpxchg(&event->state, LANDLOCK_SUPERVISE_EVENT_NEW, + LANDLOCK_SUPERVISE_EVENT_DENIED); + cmpxchg(&event->state, LANDLOCK_SUPERVISE_EVENT_NOTIFIED, + LANDLOCK_SUPERVISE_EVENT_DENIED); + wake_up_var(event); + landlock_put_supervise_event(event); +} + +void landlock_put_supervisor(struct landlock_supervisor *const supervisor) +{ + if (refcount_dec_and_test(&supervisor->usage)) { + struct landlock_supervise_event_kernel *freeme, *next; + + might_sleep(); + /* we are the only reference, hence no locking */ + + /* deny all pending events */ + list_for_each_entry_safe(freeme, next, &supervisor->event_queue, + node) { + list_del(&freeme->node); + deny_and_put_event(freeme); + } + /* + * user reply no longer possible without any reference to + * supervisor, deny all notified events + */ + list_for_each_entry_safe(freeme, next, + &supervisor->notified_events, node) { + list_del(&freeme->node); + deny_and_put_event(freeme); + } + kfree(supervisor); + } +} diff --git a/security/landlock/supervise.h b/security/landlock/supervise.h new file mode 100644 index 000000000000..1fc3460335af --- /dev/null +++ b/security/landlock/supervise.h @@ -0,0 +1,63 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Landlock LSM - Implementation specific to landlock-supervise + * + * Copyright © 2025 Tingmao Wang <m@xxxxxxxxxx> + */ + +#ifndef _SECURITY_LANDLOCK_SUPERVISE_H +#define _SECURITY_LANDLOCK_SUPERVISE_H + +#include <linux/refcount.h> +#include <linux/wait.h> +#include <linux/path.h> +#include <linux/pid.h> + +#include "access.h" +#include "ruleset.h" + +struct landlock_supervisor { + refcount_t usage; + spinlock_t lock; + /* protected by @lock, contains landlock_supervise_event_kernel */ + struct list_head event_queue; + /* protected by @lock, contains landlock_supervise_event_kernel */ + struct list_head notified_events; + struct wait_queue_head poll_event_wq; + /* protected by @lock */ + u32 next_event_id; +}; + +enum landlock_supervise_event_state { + LANDLOCK_SUPERVISE_EVENT_NEW, + LANDLOCK_SUPERVISE_EVENT_NOTIFIED, + LANDLOCK_SUPERVISE_EVENT_ALLOWED, + LANDLOCK_SUPERVISE_EVENT_DENIED, +}; + +struct landlock_supervise_event_kernel { + struct list_head node; + refcount_t usage; + enum landlock_supervise_event_state state; + + /* more fields to come */ +}; + +struct landlock_supervisor *landlock_create_supervisor(void); +void landlock_get_supervisor(struct landlock_supervisor *const supervisor); +void landlock_put_supervisor(struct landlock_supervisor *const supervisor); + +static inline void landlock_get_supervise_event( + struct landlock_supervise_event_kernel *const event) +{ + refcount_inc(&event->usage); +} + +static inline void landlock_put_supervise_event( + struct landlock_supervise_event_kernel *const event) +{ + if (refcount_dec_and_test(&event->usage)) + kfree(event); +} + +#endif /* _SECURITY_LANDLOCK_SUPERVISE_H */ -- 2.39.5