diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 819a3e207438..67ec198a90cb 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -214,6 +214,7 @@ enum bpf_arg_type {
ARG_PTR_TO_LONG, /* pointer to long */
ARG_PTR_TO_SOCKET, /* pointer to bpf_sock (fullsock) */
ARG_PTR_TO_BTF_ID, /* pointer to in-kernel struct */
+ ARG_PTR_TO_TASK, /* pointer to task_struct */
};
/* type of values returned from helper functions */@@ -1088,6 +1089,7 @@ extern const struct bpf_func_proto bpf_get_local_storage_proto;
extern const struct bpf_func_proto bpf_strtol_proto;
extern const struct bpf_func_proto bpf_strtoul_proto;
extern const struct bpf_func_proto bpf_tcp_sock_proto;
+extern const struct bpf_func_proto bpf_task_landlock_ptrace_ancestor_proto;
/* Shared helpers among cBPF and eBPF. */
void bpf_user_rnd_init_once(void);
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index 6e4147790f96..c88436b97163 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -2777,6 +2777,24 @@ union bpf_attr {
* restricted to raw_tracepoint bpf programs.
* Return
* 0 on success, or a negative error in case of failure.
+ *
+ * int bpf_task_landlock_ptrace_ancestor(struct task_struct *parent, struct task_struct *child)
+ * Description
+ * Check the relation of a potentially parent task with a child
+ * one, according to their Landlock ptrace hook programs.
+ * Return
+ * **-EINVAL** if the child's ptrace programs are not comparable
+ * to the parent ones, i.e. one of them is an empty set.
+ *
+ * **-ENOENT** if the parent's ptrace programs are either in a
+ * separate hierarchy of the child ones, or if the parent's ptrace
+ * programs are a superset of the child ones.
+ *
+ * 0 if the parent's ptrace programs are the same as the child
+ * ones.
+ *
+ * 1 if the parent's ptrace programs are indeed a subset of the
+ * child ones.
*/
#define __BPF_FUNC_MAPPER(FN) \
FN(unspec), \@@ -2890,7 +2908,8 @@ union bpf_attr {
FN(sk_storage_delete), \
FN(send_signal), \
FN(tcp_gen_syncookie), \
- FN(skb_output),
+ FN(skb_output), \
+ FN(task_landlock_ptrace_ancestor),
/* integer value in 'imm' field of BPF_CALL instruction selects which helper
* function eBPF program intends to calldiff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index ebf1991906b7..af8f1a777a2d 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -3492,6 +3492,10 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 regno,
type != PTR_TO_MAP_VALUE &&
type != expected_type)
goto err_type;
+ } else if (arg_type == ARG_PTR_TO_TASK) {
+ expected_type = PTR_TO_TASK;
+ if (type != expected_type)
+ goto err_type;
} else {
verbose(env, "unsupported arg_type %d\n", arg_type);
return -EFAULT;diff --git a/security/landlock/bpf_ptrace.c b/security/landlock/bpf_ptrace.c
index 2ec73078ad01..0e1362951463 100644
--- a/security/landlock/bpf_ptrace.c
+++ b/security/landlock/bpf_ptrace.c
@@ -7,9 +7,13 @@
*/
#include <linux/bpf.h>
+#include <linux/cred.h>
+#include <linux/kernel.h>
+#include <linux/rcupdate.h>
#include <uapi/linux/landlock.h>
#include "bpf_ptrace.h"
+#include "common.h"
bool landlock_is_valid_access_ptrace(int off, enum bpf_access_type type,
enum bpf_reg_type *reg_type, int *max_size)
@@ -28,3 +32,67 @@ bool landlock_is_valid_access_ptrace(int off, enum bpf_access_type type,
return false;
}
}
+
+/**
+ * domain_ptrace_ancestor - check domain ordering according to ptrace
+ *
+ * @parent: a parent domain
+ * @child: a potential child of @parent
+ *
+ * Check if the @parent domain is less or equal to (i.e. a subset of) the
+ * @child domain.
+ */
+static int domain_ptrace_ancestor(const struct landlock_domain *parent,
+ const struct landlock_domain *child)
+{
+ const struct landlock_prog_list *child_progs, *parent_progs;
+ const size_t hook = get_hook_index(LANDLOCK_HOOK_PTRACE);
+
+ if (!parent || !child)
+ /* @parent or @child has no ptrace restriction */
+ return -EINVAL;
+ parent_progs = parent->programs[hook];
+ child_progs = child->programs[hook];
+ if (!parent_progs || !child_progs)
+ /* @parent or @child has no ptrace restriction */
+ return -EINVAL;
+ if (child_progs == parent_progs)
+ /* @parent is at the same level as @child */
+ return 0;
+ for (child_progs = child_progs->prev; child_progs;
+ child_progs = child_progs->prev) {
+ if (child_progs == parent_progs)
+ /* @parent is one of the ancestors of @child */
+ return 1;
+ }
+ /*
+ * Either there is no relationship between @parent and @child, or
+ * @child is one of the ancestors of @parent.
+ */
+ return -ENOENT;
+}
+
+/*
+ * Cf. include/uapi/linux/bpf.h - bpf_task_landlock_ptrace_ancestor
+ */
+BPF_CALL_2(bpf_task_landlock_ptrace_ancestor, const struct task_struct *,
+ parent, const struct task_struct *, child)
+{
+ const struct landlock_domain *dom_parent, *dom_child;
+
+ WARN_ON_ONCE(!rcu_read_lock_held());
+ if (WARN_ON(!parent || !child))
+ return -EFAULT;
+ dom_parent = landlock_cred(__task_cred(parent))->domain;
+ dom_child = landlock_cred(__task_cred(child))->domain;
+ return domain_ptrace_ancestor(dom_parent, dom_child);
+}
+
+const struct bpf_func_proto bpf_task_landlock_ptrace_ancestor_proto = {
+ .func = bpf_task_landlock_ptrace_ancestor,
+ .gpl_only = false,
+ .pkt_access = false,
+ .ret_type = RET_INTEGER,
+ .arg1_type = ARG_PTR_TO_TASK,
+ .arg2_type = ARG_PTR_TO_TASK,
+};diff --git a/security/landlock/bpf_verify.c b/security/landlock/bpf_verify.c
index 6ed921588178..a1d2db75d51d 100644
--- a/security/landlock/bpf_verify.c
+++ b/security/landlock/bpf_verify.c
@@ -70,6 +70,10 @@ static const struct bpf_func_proto *bpf_landlock_func_proto(
return &bpf_map_update_elem_proto;
case BPF_FUNC_map_delete_elem:
return &bpf_map_delete_elem_proto;
+ case BPF_FUNC_task_landlock_ptrace_ancestor:
+ if (get_hook_type(prog) == LANDLOCK_HOOK_PTRACE)
+ return &bpf_task_landlock_ptrace_ancestor_proto;
+ return NULL;
default:
return NULL;
}
--
2.23.0