From nobody Fri Oct 2 14:03:02 2026 Received: from mail-yx1-f48.google.com (mail-yx1-f48.google.com [74.125.224.48]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 07D0830B502 for ; Fri, 31 Jul 2026 02:21:13 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.224.48 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785464476; cv=none; b=F8ex/j9sFtb+yMxa/vd7SuqeJ7+RvRIHmD4D+G2y3/QqBHed6ZeFhhIALHXgE7LkUiHtoTfGaN6DBwW2+pTSHEn4lpp/1yxhVcLkLg38N/h+xmzNPPT976iYIhuIpGfe4xUUmBbioXhdFzu6PRb7fjgFUc6PykiudEQrbuHpexA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785464476; c=relaxed/simple; bh=JVIUapt3mXax8Zzvl5moEskJgWAHSXq0LtxR/fnDvOc=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Xt63ShDly1Yk5d+H21UuZUr+VSNiemQuucvN588qs0RzBtpRdDLQbRyXQs63VXh+OPYaI8l5lr9d00cfwIzlSYMgL2SnzVurgCIBeSRKfy9J+UxpNpq+PRTv6kmU4P6Fzt3lrZGpemeliDsQNHphImrabIgkO4mW5zr9P3VXFjY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=iVnaw4OA; arc=none smtp.client-ip=74.125.224.48 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="iVnaw4OA" Received: by mail-yx1-f48.google.com with SMTP id 956f58d0204a3-6681e7911b0so550996d50.0 for ; Thu, 30 Jul 2026 19:21:13 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1785464473; x=1786069273; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=kln/oJiLAaRdTDOdng8Bb4F+q8mG/4pvdPDoCAUkB+k=; b=iVnaw4OARcyiwp17xlfVt4+M0Qln/nWfZh3V6PdoyeJDARWkLDsbBYfRGIXcR6/SDL d3x6lGmVgS42X8j5BvBW1rdFFjjSaDuHDEqczFPyQtlwB6PdXabQALbplYiEZWb0syAH wi0/qJ3m+QJOEWDEEIOvQ/lp9YSwK4ngN6bJqJ2swcD/8oaAWoPKRgE0yOgoaH1RVvPn HpfeG4z1tzlUc+447NhYmr3pAyEpXy96ft19d7JIe9XXUvmICg6AS2BRY/upe4WsjoUI tJz4C9CXOUMd63rLrGC7llWWpTW+ZO0XPx/rQhZ0C0+JEqXn5LTFkK1jzvvG2S170w56 Zu6Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1785464473; x=1786069273; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=kln/oJiLAaRdTDOdng8Bb4F+q8mG/4pvdPDoCAUkB+k=; b=oti7vW5q7rkMTVD0oLcJsU4+7IVgcBg+PZvDdfSBmfjYKtBRR2ltvQUKn4MFgDvhW7 JdIELQTSVl1GmUxRiKuK16tD/7ZNsj1sOs2kuU12FOkkDF6+1KfQ98y6iPOEiIL/DRLB VBgimAy1rjDyLW7n4WAr3pyswW/Mc8lTZkyy4XNXXySQGFLEB9yjIHNx12djtMiMbUeQ elqv0J3+LvEejyObEe35/kkqjjOK7yKyn08O6M71X9aVaSxYl+Vwi0ia7n4KrOa8mCN7 wdY4Ak2pe2rjbcD86AMYs8hOKw2okhhzUYgC/8ZWIXRd1PIEqGhVSk+AfTWW7PtTte0E 71QA== X-Forwarded-Encrypted: i=1; AHgh+RpKtPwxcWwJrM8q8s3c/yJQPDLmgdxedewJ3jGrWGRAfNWLUqqkuyZTgRAu3GpGrrNoyBN3Ym8FUMvsT3A=@vger.kernel.org X-Gm-Message-State: AOJu0YyiaAc8O6dgEqVrzQHLTJqnUX9vzwg2U+zBT3Bb8UP78oM20iwu zHScFNKoflQPF3sGgWVXEXvIUwG2upQ7fp7V9kaey/yxVnnoQqNK8ox9 X-Gm-Gg: AR+sD115X3rEtrNQ74A1Vrr8qgxTAMMGUXBCS8MOdhr7GQ+HmoWqB8ZMk5wo8DU09xV Ygy86dE1sXtqvM6MgboI1df5t9OWWYXaIwQc9Bg/ucm2TovcZD1XJl1Cluv+8FuefzkwFd78JMH A27VwCyXGnDYoMzOv8sXYNHwiIKL4WzFj6hs/DlyGmucM8uCpL9MQilBU9H/b4zOI1WZNxdB1n6 ZqCi0h+ScjoXycgfqnps814rMteO4kR4RUVrxfEQf1St0PS/s99HPgdY+V/3EbfTkx9AZz/8uUQ KfgVFgXX2ERnMAAuwc49AvL3EkN4D+mthL9jrF7i6Rb+qYJfzKJn7OHe4pd8KQMCMPvK7d3MZDF tlFrXihea850G8KOCIE9Xu8NWgvppz0AEkSGIg5S54QfaVMu801HOMPT2RS4DqGgQXXbfc0w84U QUrZU80I4Hqhqmmd2BdUlz2Q6huBbKjCy4C8wWI/1eK4rTWx6kf9Zxv6US6yVsnXkb88wnLvbnK MjIKc4o9pJ2ivXsT60wHQ== X-Received: by 2002:a05:690c:9981:b0:81e:8a24:d5fe with SMTP id 00721157ae682-81fcb9727a2mr778437b3.2.1785464472839; Thu, 30 Jul 2026 19:21:12 -0700 (PDT) Received: from zenbox ([2600:1700:18fb:6011:e94:8a83:feea:6720]) by smtp.gmail.com with ESMTPSA id 00721157ae682-81fb8b26e8bsm20519047b3.47.2026.07.30.19.21.12 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 30 Jul 2026 19:21:12 -0700 (PDT) From: Justin Suess To: ast@kernel.org, daniel@iogearbox.net, andrii@kernel.org, kpsingh@kernel.org, paul@paul-moore.com, mic@digikod.net, viro@zeniv.linux.org.uk, brauner@kernel.org, kees@kernel.org Cc: gnoack@google.com, jack@suse.cz, song@kernel.org, yonghong.song@linux.dev, martin.lau@linux.dev, m@maowtm.org, bpf@vger.kernel.org, linux-security-module@vger.kernel.org, linux-kernel@vger.kernel.org, Justin Suess , Casey Schaufler Subject: [PATCH bpf-next 01/13] lsm: Add LSM hook security_policy_kptr_from_fd Date: Thu, 30 Jul 2026 22:20:34 -0400 Message-ID: <20260731022047.189137-2-utilityemal77@gmail.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260731022047.189137-1-utilityemal77@gmail.com> References: <20260731022047.189137-1-utilityemal77@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add a generic LSM hook handing out a reference to an LSM policy object on behalf of a kernel-internal caller: security_policy_kptr_from_fd(lsmid, fd, &policy) together with union lsm_policy_kptr, the tagged payload carrying such a reference across the LSM boundary. The union holds one per-LSM member; which member is valid is determined by the lsmid the caller passes. The pointers the members hold are the BTF-visible handles whose provenance the BPF verifier guarantees, i.e. referenced kptrs, hence the hook naming: the reference stays strongly typed from the BPF program through the hook to the owning LSM, which resolves the handle to its internal policy representation. This hook backs BPF kfuncs: it lets an LSM hand out a reference to one of its policy objects (identified by a file descriptor created through the LSM's own userspace API) without exporting any symbol or defining any BPF interface itself. The reference is released through security_policy_kptr_put(), added by the next patch. The hook uses targeted dispatch: the shim walks the hook list and only calls the implementation registered by the LSM matching @lsmid, following the security_getprocattr()/security_setprocattr() patterns for generic hooks carrying LSM-specific payloads. When no active LSM matches, the shim returns -EOPNOTSUPP: a kfunc call for an LSM that is compiled out or not enabled fails at runtime rather than being hidden from the BPF program at verification time. For the same reason the union members are not guarded by the LSMs' CONFIG options: the callers are built independently of any individual LSM. The hook is excluded from the "bpf" LSM's attachment points. The targeted dispatch would only reach a program attached there for calls with LSM_ID_BPF, which no caller passes, so the attachment point would be dead. Cc: Paul Moore Cc: Casey Schaufler Signed-off-by: Justin Suess --- Notes: The hook naming choice of policy_kptr_from_fd and the other hooks is up in the air for me. I decided to include the kptr part in the name because it's relevant to the task being performed: we are simply getting a pointer to a kernel policy object from a file descriptor. =20 I'm open to better ideas for the name... include/linux/lsm_hook_defs.h | 2 ++ include/linux/security.h | 25 +++++++++++++++++++++++ kernel/bpf/bpf_lsm.c | 1 + security/security.c | 38 +++++++++++++++++++++++++++++++++++ 4 files changed, 66 insertions(+) diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h index 65c9609ec207..afd5b3f932a9 100644 --- a/include/linux/lsm_hook_defs.h +++ b/include/linux/lsm_hook_defs.h @@ -452,6 +452,8 @@ LSM_HOOK(int, 0, bpf_token_create, struct bpf_token *to= ken, union bpf_attr *attr LSM_HOOK(void, LSM_RET_VOID, bpf_token_free, struct bpf_token *token) LSM_HOOK(int, 0, bpf_token_cmd, const struct bpf_token *token, enum bpf_cm= d cmd) LSM_HOOK(int, 0, bpf_token_capable, const struct bpf_token *token, int cap) +LSM_HOOK(int, -EOPNOTSUPP, policy_kptr_from_fd, int fd, + union lsm_policy_kptr *policy) #endif /* CONFIG_BPF_SYSCALL */ =20 LSM_HOOK(int, 0, locked_down, enum lockdown_reason what) diff --git a/include/linux/security.h b/include/linux/security.h index 153e9043058f..db807e61d310 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -168,6 +168,23 @@ struct lsm_prop { struct lsm_prop_bpf bpf; }; =20 +struct bpf_landlock_ruleset; + +struct lsm_policy_landlock { + struct bpf_landlock_ruleset *ruleset; +}; + +/* + * A reference to an LSM policy object, tagged by the LSM_ID_* value + * passed alongside: only the matching LSM's member is valid. The + * members are not guarded by the LSMs' CONFIG options: the callers + * are built independently of any individual LSM and a call for a + * missing LSM must fail at runtime, not at build time. + */ +union lsm_policy_kptr { + struct lsm_policy_landlock landlock; +}; + extern const char *const lockdown_reasons[LOCKDOWN_CONFIDENTIALITY_MAX+1]; =20 /* These functions are in security/commoncap.c */ @@ -2312,6 +2329,8 @@ extern int security_bpf_token_create(struct bpf_token= *token, union bpf_attr *at extern void security_bpf_token_free(struct bpf_token *token); extern int security_bpf_token_cmd(const struct bpf_token *token, enum bpf_= cmd cmd); extern int security_bpf_token_capable(const struct bpf_token *token, int c= ap); +extern int security_policy_kptr_from_fd(u64 lsmid, int fd, + union lsm_policy_kptr *policy); #else static inline int security_bpf(int cmd, union bpf_attr *attr, unsigned int size, bool kernel) @@ -2365,6 +2384,12 @@ static inline int security_bpf_token_capable(const s= truct bpf_token *token, int { return 0; } + +static inline int security_policy_kptr_from_fd(u64 lsmid, int fd, + union lsm_policy_kptr *policy) +{ + return -EOPNOTSUPP; +} #endif /* CONFIG_SECURITY */ #endif /* CONFIG_BPF_SYSCALL */ =20 diff --git a/kernel/bpf/bpf_lsm.c b/kernel/bpf/bpf_lsm.c index 3983b4ce73c8..9fa514204fb5 100644 --- a/kernel/bpf/bpf_lsm.c +++ b/kernel/bpf/bpf_lsm.c @@ -56,6 +56,7 @@ BTF_ID(func, bpf_lsm_xfrm_decode_session) #endif BTF_ID(func, bpf_lsm_ismaclabel) BTF_ID(func, bpf_lsm_file_alloc_security) +BTF_ID(func, bpf_lsm_policy_kptr_from_fd) BTF_SET_END(bpf_lsm_disabled_hooks) =20 /* List of LSM hooks that should operate on 'current' cgroup regardless diff --git a/security/security.c b/security/security.c index 71aea8fdf014..14fd8b878cd0 100644 --- a/security/security.c +++ b/security/security.c @@ -5441,6 +5441,44 @@ int security_bpf_token_capable(const struct bpf_toke= n *token, int cap) return call_int_hook(bpf_token_capable, token, cap); } =20 +/** + * security_policy_kptr_from_fd() - Get an LSM policy object from a fd + * @lsmid: LSM_ID_* value of the LSM asked to interpret @fd + * @fd: file descriptor referring to a policy object, resolved in the + * calling task's file descriptor table + * @policy: receives the referenced policy object in the member of the + * LSM identified by @lsmid + * + * Ask the LSM identified by @lsmid to translate @fd into a reference + * counted policy object. The caller must not dereference the + * returned handle, must only hand it back to the same LSM, e.g. + * through security_bprm_enforce_policy_kptr(), and must release it + * with security_policy_kptr_put(). Only the hook implementation of + * the LSM identified by @lsmid is called. The hook is only called + * from a context that may sleep. + * + * An implementation must fill its own member of @policy with a + * reference that remains valid until it is released through the + * policy_kptr_put hook, and must not assume anything about @fd beyond + * what its own userspace API created it with. + * + * Return: Returns 0 if @policy holds a reference counted policy + * object, -EOPNOTSUPP if the LSM does not implement the hook, negative + * values on other failures. + */ +int security_policy_kptr_from_fd(u64 lsmid, int fd, + union lsm_policy_kptr *policy) +{ + struct lsm_static_call *scall; + + lsm_for_each_hook(scall, policy_kptr_from_fd) { + if (scall->hl->lsmid->id !=3D lsmid) + continue; + return scall->hl->hook.policy_kptr_from_fd(fd, policy); + } + return LSM_RET_DEFAULT(policy_kptr_from_fd); +} + /** * security_bpf_map_free() - Free a bpf map's LSM blob * @map: bpf map --=20 2.54.0 From nobody Fri Oct 2 14:03:02 2026 Received: from mail-yw1-f174.google.com (mail-yw1-f174.google.com [209.85.128.174]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 5588A318EF6 for ; Fri, 31 Jul 2026 02:21:16 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.174 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785464478; cv=none; b=ZEa65E0VAvkU2kV4YR+Jo2VlTdMf2PgzaO53yOGlT4v9y37SYBynAwz4kMNElDCtCqgcUk6GURYWoMv2BENGOQ7GKsIXCHcGM4Rp8WBtCUK1p0UfLxN0xowsV14xaxmq06Oh2a/9LF6flN+T9/8KlPSoeqdfNls4V6z94sEe+1w= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785464478; c=relaxed/simple; bh=Eb0i9E1Gj/ePAHcgoR0E+HEHOHcL5jShkypGqqir1ck=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=tVqAsjd4og8r34bqZQDV/p+zHnq0ai/0tt9UK080WIv7/Ks4k0G9gp/Qhkv16kJclfu2C8YbM3u+YpbrHxDtbkUp3AZVVJG0GD+aRcHwAnAc8n2zmdsYtlW13UXDoPQPs6AdlvLNTWhfGuLe0RVZWeGCDYK29pkQmFQz97a5cbQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=BJxwxulJ; arc=none smtp.client-ip=209.85.128.174 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="BJxwxulJ" Received: by mail-yw1-f174.google.com with SMTP id 00721157ae682-81ec29f1d07so7209987b3.1 for ; Thu, 30 Jul 2026 19:21:16 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1785464475; x=1786069275; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=x5Hdd2d+rIeKOx6QkydL222RNNm3Nsb20BqP+/GgTXI=; b=BJxwxulJGiu8pnn2NjZ51oNybQpxsSLHfraJNiYIN5Pog1iJ41uon7qfTFXLmmhhwM DPGrlM6rOK68sPT0KTry+I+0He3WbcyZAwyrqKR54PYqpo6Ltb5PIBfRIa5Ly1QtXPwj QHZvdf7Knk4cI4u7g6MHun4qc/mt8bPnwfUAjApf0iwtQwf3k2h9hv5tGQAnf+e5OztB XAf8HmtkoJAhNoF8PZhzPG6ZlPjmbwkR62ywTzBHdZW88ZD6JtIJ+UsMlTpfZGpKdpQo +Khw4xap3TR2Sv4A6PYBQxJdU/p/Nhov8Dh2Xqz7Q1adTVoh3yDX+PTEn9iocwNEY4Sw 5iIg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1785464475; x=1786069275; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=x5Hdd2d+rIeKOx6QkydL222RNNm3Nsb20BqP+/GgTXI=; b=cdZYMCz6NZVHgy7bos1RTpeTJLjxNMfUKNI4z2RYNX5QcDvbwct91t/56lARg3mbN/ 5TEPbxgn0MZyhaf0I8AiVln3kYG7mEDyNzbNm10whpxpf1OA7hOR64sfjLh4tUWuG6cj k5aBSnjlrm6QmAxbNEESc0sXNT0B7+wCMZX4GEGCy5SAuzDStsvMjtRTUWHhMQ/+3TIv HJ/DfE/bIkjVcOvUYgAlCZFW3aSpKo3t4mdrWgd99rKA/jhcSIjGRLBF91jPH5+H+eHz wsGMItZpP8quadGtGAEPFHSLaxZWap9C0s6UTmkrnHLoA66f/Vf4nouUTKYrddgDwAfQ A/wQ== X-Forwarded-Encrypted: i=1; AHgh+RqaVjejh6KN+kcXgFTQJnbqIyi9Oqm19x64QQIwwhL0QKroVErmh18iEsbZi2G87VtYVCe1lP66iaxR/dA=@vger.kernel.org X-Gm-Message-State: AOJu0Ywa86WFYEMkNzbtoH5R/A9Ke0bYnsYCars46gZTRAzSbXzS89AX YdvTOjgr8LsT7XwXh1GsIcReZVJwuc0kLJBbYMReQyT+4MEbjn5g8iTP X-Gm-Gg: AR+sD10sCVmG1K7jFaA/GFGQhaCqOpQ0SFa6DDeGZcTIAkebInB0Cp5uiIV5YXr/Xxj 6b2Hz6vyKFkIOJOeYxwfjDDoE1ypSXONfepCPHuiPrvEXSQwsi4V9kHIqbgHUloQgLPuVD9GXQN GPlImFoZPe+4qr1h2zhJVBUZW9bTZ95oTCc3v7006A1yrKa8aaBwizR3Wj8clnS3AzKaT498KIW 76mF2MbOH/xi9uzCl3g8cS6paRlimcwrDwsV1RIVtgrtkgNif5lKDdqhTIDI5Q+DTw2mhxQ+MkP qY1CnGcWG3z38FDrBiQcSdZ9eVI7NKq42F2/XvceOenhnuRiG/eGx48SO1JsRe7m8hfK5tPAfko Nl2qBHGWwGtWUJflAnuF0E07/a5GNMZaSMIbZx6o10UPkaBDSMb34bYRP2O0Y/UzX/zr66NBT2T LhQpmumzccRzChHFq+07mtjXiV+aCbm/mE5euBU/H1YO0RAjF/psXELOnOBWhX0urx03AsYOSJK C5g4gGIjRElvWG23MLkLA== X-Received: by 2002:a05:690c:4d09:b0:81f:3b6d:a0c2 with SMTP id 00721157ae682-81fcbbb0bd4mr506707b3.42.1785464474838; Thu, 30 Jul 2026 19:21:14 -0700 (PDT) Received: from zenbox ([2600:1700:18fb:6011:e94:8a83:feea:6720]) by smtp.gmail.com with ESMTPSA id 00721157ae682-81fb8b26e8bsm20519047b3.47.2026.07.30.19.21.14 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 30 Jul 2026 19:21:14 -0700 (PDT) From: Justin Suess To: ast@kernel.org, daniel@iogearbox.net, andrii@kernel.org, kpsingh@kernel.org, paul@paul-moore.com, mic@digikod.net, viro@zeniv.linux.org.uk, brauner@kernel.org, kees@kernel.org Cc: gnoack@google.com, jack@suse.cz, song@kernel.org, yonghong.song@linux.dev, martin.lau@linux.dev, m@maowtm.org, bpf@vger.kernel.org, linux-security-module@vger.kernel.org, linux-kernel@vger.kernel.org, Justin Suess , Casey Schaufler Subject: [PATCH bpf-next 02/13] lsm: Add LSM hook security_policy_kptr_put Date: Thu, 30 Jul 2026 22:20:35 -0400 Message-ID: <20260731022047.189137-3-utilityemal77@gmail.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260731022047.189137-1-utilityemal77@gmail.com> References: <20260731022047.189137-1-utilityemal77@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add the generic LSM hook releasing a reference obtained through security_policy_kptr_from_fd(): security_policy_kptr_put(lsmid, &policy) The shim uses the same targeted dispatch by @lsmid as the get hook: only the implementation registered by the matching LSM is called, and it only ever reads its own member of union lsm_policy_kptr, so a policy object only ever travels back to the LSM that produced it. The hook is void: releasing a reference cannot fail. A reference can only come from the matching LSM's policy_kptr_from_fd hook, so a dispatch miss means a caller passed the wrong lsmid or an LSM implemented the get hook without the put hook; the shim warns instead of silently leaking the reference. An implementation must support being called from a context that cannot sleep: the release of BPF managed references may be driven from object destructors. Like the get hook, this hook is excluded from the "bpf" LSM's attachment points, as the targeted dispatch makes an attachment there unreachable. Cc: Paul Moore Cc: Casey Schaufler Signed-off-by: Justin Suess --- include/linux/lsm_hook_defs.h | 1 + include/linux/security.h | 6 ++++++ kernel/bpf/bpf_lsm.c | 1 + security/security.c | 27 +++++++++++++++++++++++++++ 4 files changed, 35 insertions(+) diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h index afd5b3f932a9..0800622e317f 100644 --- a/include/linux/lsm_hook_defs.h +++ b/include/linux/lsm_hook_defs.h @@ -454,6 +454,7 @@ LSM_HOOK(int, 0, bpf_token_cmd, const struct bpf_token = *token, enum bpf_cmd cmd) LSM_HOOK(int, 0, bpf_token_capable, const struct bpf_token *token, int cap) LSM_HOOK(int, -EOPNOTSUPP, policy_kptr_from_fd, int fd, union lsm_policy_kptr *policy) +LSM_HOOK(void, LSM_RET_VOID, policy_kptr_put, union lsm_policy_kptr *polic= y) #endif /* CONFIG_BPF_SYSCALL */ =20 LSM_HOOK(int, 0, locked_down, enum lockdown_reason what) diff --git a/include/linux/security.h b/include/linux/security.h index db807e61d310..5017a335918c 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -2331,6 +2331,7 @@ extern int security_bpf_token_cmd(const struct bpf_to= ken *token, enum bpf_cmd cm extern int security_bpf_token_capable(const struct bpf_token *token, int c= ap); extern int security_policy_kptr_from_fd(u64 lsmid, int fd, union lsm_policy_kptr *policy); +extern void security_policy_kptr_put(u64 lsmid, union lsm_policy_kptr *pol= icy); #else static inline int security_bpf(int cmd, union bpf_attr *attr, unsigned int size, bool kernel) @@ -2390,6 +2391,11 @@ static inline int security_policy_kptr_from_fd(u64 l= smid, int fd, { return -EOPNOTSUPP; } + +static inline void security_policy_kptr_put(u64 lsmid, + union lsm_policy_kptr *policy) +{ +} #endif /* CONFIG_SECURITY */ #endif /* CONFIG_BPF_SYSCALL */ =20 diff --git a/kernel/bpf/bpf_lsm.c b/kernel/bpf/bpf_lsm.c index 9fa514204fb5..e9059d43e92c 100644 --- a/kernel/bpf/bpf_lsm.c +++ b/kernel/bpf/bpf_lsm.c @@ -57,6 +57,7 @@ BTF_ID(func, bpf_lsm_xfrm_decode_session) BTF_ID(func, bpf_lsm_ismaclabel) BTF_ID(func, bpf_lsm_file_alloc_security) BTF_ID(func, bpf_lsm_policy_kptr_from_fd) +BTF_ID(func, bpf_lsm_policy_kptr_put) BTF_SET_END(bpf_lsm_disabled_hooks) =20 /* List of LSM hooks that should operate on 'current' cgroup regardless diff --git a/security/security.c b/security/security.c index 14fd8b878cd0..fd535bd00c24 100644 --- a/security/security.c +++ b/security/security.c @@ -5479,6 +5479,33 @@ int security_policy_kptr_from_fd(u64 lsmid, int fd, return LSM_RET_DEFAULT(policy_kptr_from_fd); } =20 +/** + * security_policy_kptr_put() - Put a reference on an LSM policy object + * @lsmid: LSM_ID_* value of the LSM owning @policy + * @policy: the policy object, in the member of the LSM identified by + * @lsmid + * + * Release a reference previously obtained with + * security_policy_kptr_from_fd(). Only the hook implementation + * of the LSM identified by @lsmid is called, and @policy must have + * been obtained from that same LSM. An implementation must support + * being called from a context that cannot sleep: the release of BPF + * managed references may be driven from object destructors. + */ +void security_policy_kptr_put(u64 lsmid, union lsm_policy_kptr *policy) +{ + struct lsm_static_call *scall; + + lsm_for_each_hook(scall, policy_kptr_put) { + if (scall->hl->lsmid->id !=3D lsmid) + continue; + scall->hl->hook.policy_kptr_put(policy); + return; + } + /* A held reference implies the matching LSM implements the hook. */ + WARN_ON_ONCE(1); +} + /** * security_bpf_map_free() - Free a bpf map's LSM blob * @map: bpf map --=20 2.54.0 From nobody Fri Oct 2 14:03:02 2026 Received: from mail-yw1-f180.google.com (mail-yw1-f180.google.com [209.85.128.180]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A6D3931715A for ; Fri, 31 Jul 2026 02:21:17 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.180 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785464480; cv=none; b=XnQmjljVFlxjKzDH5Dj1E/e3tuAJ2SfEa6Uw8IH7pBZwlZyHHJrGPePMMGWM9Rw+XDpZwxVy4Oweg4kGiEKxPZUfq+vctBytuo/8hMI93PnLBDs3rGzE2jJvYbSxxYGIkVhvG/qmTCYTqIB1kPIoVb/aY9TAnGbo+NeEZJsiH/o= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785464480; c=relaxed/simple; bh=zF0tqApE3olWAH+8tKR7/tJlMlF9wC9LvCeUj33ogrE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=MzBK97WaXGr9vRCu2ILPdU/V0YfltpS7+VN8lJG3rC7KHxFptwYr5/rxF1IKvxio/hE4Q7ASoY4tSkHYysevP1lNKMo3UFsvQNbE4AGxJWrJQuXxZdU6lAojHwRah2IG+JK5bVRSRnmwvq4NyLAvtNevvVvPqeVzRiL4FWOPDrQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=P20tIPxW; arc=none smtp.client-ip=209.85.128.180 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="P20tIPxW" Received: by mail-yw1-f180.google.com with SMTP id 00721157ae682-80cebd41372so6849107b3.3 for ; Thu, 30 Jul 2026 19:21:17 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1785464476; x=1786069276; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=dtKbTJ5THz2A+MXeqySnkUBiio4mcHLmT5n5t78F/X0=; b=P20tIPxW9iNwVUVyuwRVUOttXxFxiun5NslGeoiklCtpix/JbiGVJUmtBLvFWCWQ4T b/LPj+33jY5vbTp8pjUgKxuuTCniwXtSkxYtFiQLXw+Ap0GbTHG3M7oaeUsidsBqH4OW ZBCGyTHq47pZ4ySL8xJ7wuuaFFJY2FxlCwi+EvIQRbLSQk7CNnt6vD7076PNFJaoj/uJ 6kkSD+hwjNkzGc6YOTcs/0Z8vRgDokCO0vgz+0FydaS2S+KahUrIxnW1hyP5tcKGuTl0 a+SZPJuoHtbZ00+7sX5+YuJPYKesVCp+vFPs2PmfyH4vPh9X0nkPFd/T4vlEmI6MRmZu 3O+g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1785464476; x=1786069276; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=dtKbTJ5THz2A+MXeqySnkUBiio4mcHLmT5n5t78F/X0=; b=Nov656Bxkv2G/QrJ6Hx6+C8r4QqW6qYujEzpBFascapSiFWuwWRCeFBkqSpPZpCo/S ZkDzy0Sp/bVd8EgVZj9TG+EXNbJphc1tRVkDuHbRKroCElSCBBa+SZBUrcWuI1mrnapG ZGEp6x6zlaiMJfD7r8TDNbzgjpoxYwWLZA6zZAnzwROHxvT/+Rt8AYaUbH+X6/oE2z0G E+yebhKBb9a8PLhvo/LbDWVUApxxLetX7pVtgftRfqJa+H5QAchtVaqS1aNpDtUycddq /oWyA94nb9kK/7TdYFqsiUAMjvNb3TKNOl9HtQn3ItUZOsGInP7vy/KkPoS0M1e54m0r jDAw== X-Forwarded-Encrypted: i=1; AHgh+Rqx9SMuM6mZkK51i6BUuSJ683zAchp6BqsXgndA47pQ+u1dSAWwb1rDT85LEdsjgLcYua314Otag8OFkdc=@vger.kernel.org X-Gm-Message-State: AOJu0Yz1/hp8TbJxh2S2PgyrT69I6A7IrmdVkAKoByFE9FvfxPAdsQOE qqNZymiFK6Z0Y96bOw7QVCx/ycPLWy6obtlL+qTLHzUQ4VaUJpAMFN2d X-Gm-Gg: AR+sD11ofIPpaV5VYWfQGwQ7tuEMYGssisU2ACZaRZN7QbhKmOB4PbIpxDxguMPt8lY H5Kxe4UPuv2QNA4hHf8lvSvb+RhimGk4YOS7KqTQaZ7FtOEzOZOLBIDYmNRSRwGXM3CemQqIGDL VvXtdOAI9BvQ10gmb8OhF1jSLRF9vtJ+9Hsg6n9YGs/xQhgnnkOmwjfTGPFQlFNkUSQHJc5xWwI KoctOkssqZ/3w57iQiz8SC5yzeoa1teTCt//1gQg0UMZp6MZl52aHlULxwFME9Yuj32kzrL5hf/ 0QWyks+HBGWZoCvLneH3YcT+Rz6H3RbFcPRy8pUfwFsN5wJBmEn44BHHRk76OVgdycWxdpMrMFl RzF3JGgFCHZ1esSOceidzKCcPOF34C2KqV0wnIDAkTiwGJb86x2K+jZjOXXJNOKMA5IvCsQcXYQ zJtgc2y6nY3M6OpwQdp/MeHq0bsNrgBq74vyKEhOqM+tB50Zf9s6AmzRXdOWTtuD/8uZ3euXxPg agBI8ymMIph+w3siY0Y7+YXl4DyGP2S X-Received: by 2002:a05:690c:38a:b0:81f:69e:1c70 with SMTP id 00721157ae682-81fcbb3fcb6mr634587b3.38.1785464476435; Thu, 30 Jul 2026 19:21:16 -0700 (PDT) Received: from zenbox ([2600:1700:18fb:6011:e94:8a83:feea:6720]) by smtp.gmail.com with ESMTPSA id 00721157ae682-81fb8b26e8bsm20519047b3.47.2026.07.30.19.21.15 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 30 Jul 2026 19:21:16 -0700 (PDT) From: Justin Suess To: ast@kernel.org, daniel@iogearbox.net, andrii@kernel.org, kpsingh@kernel.org, paul@paul-moore.com, mic@digikod.net, viro@zeniv.linux.org.uk, brauner@kernel.org, kees@kernel.org Cc: gnoack@google.com, jack@suse.cz, song@kernel.org, yonghong.song@linux.dev, martin.lau@linux.dev, m@maowtm.org, bpf@vger.kernel.org, linux-security-module@vger.kernel.org, linux-kernel@vger.kernel.org, Justin Suess , Casey Schaufler Subject: [PATCH bpf-next 03/13] lsm: Add LSM hook security_bprm_enforce_policy_kptr Date: Thu, 30 Jul 2026 22:20:36 -0400 Message-ID: <20260731022047.189137-4-utilityemal77@gmail.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260731022047.189137-1-utilityemal77@gmail.com> References: <20260731022047.189137-1-utilityemal77@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add a generic LSM hook enforcing an LSM policy object on the credentials prepared for an execution: security_bprm_enforce_policy_kptr(lsmid, bprm, &policy, flags) The policy object is obtained from the owning LSM through security_policy_kptr_from_fd(), travels in that LSM's member of union lsm_policy_kptr, and is handed back only to that same LSM: the shim uses the same targeted dispatch by @lsmid as the policy kptr lifetime hooks, and returns -EOPNOTSUPP when no active LSM matches. This is the first policy operation backed by the BPF-owned LSM kfuncs: it lets a sleepable LSM BPF program attached to bprm_creds_for_exec() or bprm_creds_from_file() arrange for the executed task to start confined by a policy created through the LSM's own userspace API, e.g. a Landlock ruleset applied to a binprm. The BPF-facing kfunc keeps the policy pointer strongly BTF-typed all the way to the union member the implementing LSM reads back. The hook contract is LSM agnostic: any LSM with a notion of a per-task policy object can implement it, with its own semantics for how the policy composes with restrictions the credentials already carry and for the meaning of @flags, unsupported values of which it must reject with -EINVAL. Implementations can rely on being called only between the preparation and the commitment of the bprm's credentials. Like the policy kptr lifetime hooks, this hook is excluded from the "bpf" LSM's attachment points, as the targeted dispatch makes an attachment there unreachable. Cc: Paul Moore Cc: Casey Schaufler Signed-off-by: Justin Suess --- include/linux/lsm_hook_defs.h | 2 ++ include/linux/security.h | 12 ++++++++++ kernel/bpf/bpf_lsm.c | 1 + security/security.c | 41 +++++++++++++++++++++++++++++++++++ 4 files changed, 56 insertions(+) diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h index 0800622e317f..a70edbd7b761 100644 --- a/include/linux/lsm_hook_defs.h +++ b/include/linux/lsm_hook_defs.h @@ -455,6 +455,8 @@ LSM_HOOK(int, 0, bpf_token_capable, const struct bpf_to= ken *token, int cap) LSM_HOOK(int, -EOPNOTSUPP, policy_kptr_from_fd, int fd, union lsm_policy_kptr *policy) LSM_HOOK(void, LSM_RET_VOID, policy_kptr_put, union lsm_policy_kptr *polic= y) +LSM_HOOK(int, -EOPNOTSUPP, bprm_enforce_policy_kptr, struct linux_binprm *= bprm, + union lsm_policy_kptr *policy, u32 flags) #endif /* CONFIG_BPF_SYSCALL */ =20 LSM_HOOK(int, 0, locked_down, enum lockdown_reason what) diff --git a/include/linux/security.h b/include/linux/security.h index 5017a335918c..40dfa96b6a71 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -2332,6 +2332,10 @@ extern int security_bpf_token_capable(const struct b= pf_token *token, int cap); extern int security_policy_kptr_from_fd(u64 lsmid, int fd, union lsm_policy_kptr *policy); extern void security_policy_kptr_put(u64 lsmid, union lsm_policy_kptr *pol= icy); +extern int security_bprm_enforce_policy_kptr(u64 lsmid, + struct linux_binprm *bprm, + union lsm_policy_kptr *policy, + u32 flags); #else static inline int security_bpf(int cmd, union bpf_attr *attr, unsigned int size, bool kernel) @@ -2396,6 +2400,14 @@ static inline void security_policy_kptr_put(u64 lsmi= d, union lsm_policy_kptr *policy) { } + +static inline int security_bprm_enforce_policy_kptr(u64 lsmid, + struct linux_binprm *bprm, + union lsm_policy_kptr *policy, + u32 flags) +{ + return -EOPNOTSUPP; +} #endif /* CONFIG_SECURITY */ #endif /* CONFIG_BPF_SYSCALL */ =20 diff --git a/kernel/bpf/bpf_lsm.c b/kernel/bpf/bpf_lsm.c index e9059d43e92c..d847a180489f 100644 --- a/kernel/bpf/bpf_lsm.c +++ b/kernel/bpf/bpf_lsm.c @@ -58,6 +58,7 @@ BTF_ID(func, bpf_lsm_ismaclabel) BTF_ID(func, bpf_lsm_file_alloc_security) BTF_ID(func, bpf_lsm_policy_kptr_from_fd) BTF_ID(func, bpf_lsm_policy_kptr_put) +BTF_ID(func, bpf_lsm_bprm_enforce_policy_kptr) BTF_SET_END(bpf_lsm_disabled_hooks) =20 /* List of LSM hooks that should operate on 'current' cgroup regardless diff --git a/security/security.c b/security/security.c index fd535bd00c24..e9d8c9492bdb 100644 --- a/security/security.c +++ b/security/security.c @@ -5506,6 +5506,47 @@ void security_policy_kptr_put(u64 lsmid, union lsm_p= olicy_kptr *policy) WARN_ON_ONCE(1); } =20 +/** + * security_bprm_enforce_policy_kptr() - Enforce a policy on exec credenti= als + * @lsmid: LSM_ID_* value of the LSM owning @policy + * @bprm: execution context providing the prepared credentials to restrict + * @policy: the policy object to enforce, in the member of the LSM + * identified by @lsmid + * @flags: LSM-specific enforcement flags + * + * Ask the LSM identified by @lsmid to restrict the credentials + * prepared in @bprm with @policy, so that the executed task starts + * confined by it. @policy must have been obtained from the same LSM + * with security_policy_kptr_from_fd(); the hook borrows the + * reference and the caller remains responsible for releasing it. + * Only the hook implementation of the LSM identified by @lsmid is + * called: an LSM never receives a policy object meant for another LSM. + * + * This hook may only be called from an exec security context where + * @bprm's credentials are prepared but not yet committed, i.e. from a + * bprm_creds_for_exec() or bprm_creds_from_file() hook. + * + * How @policy composes with restrictions the credentials already + * carry is defined by the implementing LSM, as is the meaning of + * @flags, unsupported values of which it must reject with -EINVAL. + * + * Return: Returns 0 on success, -EOPNOTSUPP if the LSM does not + * implement the hook, negative values on other failures. + */ +int security_bprm_enforce_policy_kptr(u64 lsmid, struct linux_binprm *bprm, + union lsm_policy_kptr *policy, u32 flags) +{ + struct lsm_static_call *scall; + + lsm_for_each_hook(scall, bprm_enforce_policy_kptr) { + if (scall->hl->lsmid->id !=3D lsmid) + continue; + return scall->hl->hook.bprm_enforce_policy_kptr(bprm, policy, + flags); + } + return LSM_RET_DEFAULT(bprm_enforce_policy_kptr); +} + /** * security_bpf_map_free() - Free a bpf map's LSM blob * @map: bpf map --=20 2.54.0 From nobody Fri Oct 2 14:03:02 2026 Received: from mail-yw1-f172.google.com (mail-yw1-f172.google.com [209.85.128.172]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 0F91031F990 for ; Fri, 31 Jul 2026 02:21:19 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785464484; cv=none; b=m/EvYd606p1jM6Kp+F81ZFpqZ/t7mGkUYnPeKwvjCdXW2aVxizQIwjtFCDiKhXefz315K4XGSzaSR1DcUdZTnh1ctKkcMg0fRN/jlLQPiwviZlLruvM7qoWhyetzIYX+ckfIYhkAmKX7yhWunK6CbJrm6WxTDdurW4aVtrGCPXc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785464484; c=relaxed/simple; bh=My+S/AoHnM0sWQ2xfzHcL0mjHYwtuqRDL/YoeC794wY=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=O20bkx2ZmqH8ltVBSneuMRvjA7cLNAGhgeOnjGENxWO1BeA87w6qO1dlaQWLd8H1UzKBtVGHxfe/QYlrRQD7ADcadNFV6iWNzpKOFZ3yfAD8a4Sj2Emg4q4+vDOlqOjCeU5pbKAG/gfWs7UntCz1r18dux0lVbCYLievAA23aQA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=a1DwJtRu; arc=none smtp.client-ip=209.85.128.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="a1DwJtRu" Received: by mail-yw1-f172.google.com with SMTP id 00721157ae682-8114a4542b2so7917507b3.1 for ; Thu, 30 Jul 2026 19:21:19 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1785464478; x=1786069278; darn=vger.kernel.org; h=content-transfer-encoding:content-type:mime-version:references :in-reply-to:message-id:date:subject:cc:to:from:from:to:cc:subject :date:message-id:reply-to:content-type; bh=XvJusACK6JVx1ShSYZD+EOjxsALcCJEGMTDUzMzDD3c=; b=a1DwJtRuTrfBqJIpT7wp4bSk7RCtJIXYYrQVMamrOzMYTTbcdNX4Pz/iB/L0505v/I xf2+zjVBY1gVRicrjaVJNGTolZiHTS3gSpp3ASRd77GUSYpAVcvmTKDrtBR2ND8pEAAj dNiJhesd1Wi5oMiYNsYCDJDzy8mhvswtiyKr0cqrFG1zBtkfMPSxq5r4FsymOZQEVArJ +WqStX3G74jsfik01Uv8IfbeT0Mv7diKahbvxNmHwjK89ztXWL8gq3536RaS9A4iVfm8 Ln4g0m1U8yWAQRhl401SCnyuRSzMNOWYJiR7gBlWRB4cTzzOBkOLtWVcJKP/q4RB67LU Q9jQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1785464478; x=1786069278; h=content-transfer-encoding:content-type:mime-version:references :in-reply-to:message-id:date:subject:cc:to:from:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=XvJusACK6JVx1ShSYZD+EOjxsALcCJEGMTDUzMzDD3c=; b=cH/gFCsMBWXg/vPhwzugEV6ruw7ETrGpG7DBiJNLZb0S3ziYI1ikXJAGuuFLyFdj9i yd2X1a9KRaPOi/RmYa7+yAyVkKi2Q0wMoIHAwUurm+KmfglPmbu9wbwo0lFZ+1xMAtY3 VRdx3OfcWoL+Fi3P4/DVQU+s1KdNhWKhS71oL8lWXSFAsUkl0OLF6k5SLrQwZzQwqiFY v0NQeAutOBNA59hAbaP5slW0i3CnylogmTCzteJA6oabxHat4fF6jxIEAZNsatddkuaz BvQg2pGsSwb+qTLRtXCeb1W3pNmx3uYIQ957VJ+RkcbyDCsckgr0Eh5lVMGbPqyUPNr4 YBmA== X-Forwarded-Encrypted: i=1; AHgh+RoUEYsE39ybpDClHJYv6Hg2J80ODkPqZGKXa1PER5GpFNZ3rywFiBuXEOfA8k3E8RMeZoQqf5I6V9v7Th4=@vger.kernel.org X-Gm-Message-State: AOJu0YxFOM0porr53LF8C1sGCo6nj+o+KbI4PVpElmInIyQFNovPb4xO TMvS8l2SmBHHN+g66TaUxdaGlPU2rT2/Gh2dtRSd4mVnbnJuj47jhfxt X-Gm-Gg: AR+sD10S1luH8uwhbFt0NumfwLirKrbIlFvRTB+26pi/Y+akdEkupffBznl4BePmMyX gdoFL8KEEt/zqav5IoGip8vgaczdQpw8d89Wq1rwwSanMSTenWtEjW4RiTLm4effYbAhePhL9fB M4P/UH8oAbbhUMF2HYTTnGuxwVyahYep9wXsoH+M/YdlWh4azefwsfFhY+4iu4+9YtvWQrJXoQB ASRG4UAkR6TsBaj5tvba7lDZKmGz1KyDm4T0LYvIk/K4r4HALbreMv4GqgaLncqRPZBBbR54Cpu X9ZA3NeTJ1zygtF26oEst6iGSnohmXp52mQyQomS0TtLuqD6MJqIdmZvBNhaUKQ6fF3LDD8gqpL kT6t+UZ9YaX4Tki/cFwQRrDyychP4vtC2aIIsZkOvNdogytBUFPHV+pL9edPQGocR8ueprtkWCZ Xtx09ausRxpTONVNXTny1rdQp5CaauhiAffb6FHdLi6xchsSEQoNBhVIbObdnHePwA9hi8EAaGf Qn5ZTF6P2US3pQtv2lg1Q== X-Received: by 2002:a05:690c:e3ce:b0:81e:6c2e:f113 with SMTP id 00721157ae682-81fcbb0187dmr690287b3.36.1785464477862; Thu, 30 Jul 2026 19:21:17 -0700 (PDT) Received: from zenbox ([2600:1700:18fb:6011:e94:8a83:feea:6720]) by smtp.gmail.com with ESMTPSA id 00721157ae682-81fb8b26e8bsm20519047b3.47.2026.07.30.19.21.17 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 30 Jul 2026 19:21:17 -0700 (PDT) From: Justin Suess To: ast@kernel.org, daniel@iogearbox.net, andrii@kernel.org, kpsingh@kernel.org, paul@paul-moore.com, mic@digikod.net, viro@zeniv.linux.org.uk, brauner@kernel.org, kees@kernel.org Cc: gnoack@google.com, jack@suse.cz, song@kernel.org, yonghong.song@linux.dev, martin.lau@linux.dev, m@maowtm.org, bpf@vger.kernel.org, linux-security-module@vger.kernel.org, linux-kernel@vger.kernel.org, Justin Suess Subject: [PATCH bpf-next 04/13] landlock: Expose the ruleset fd lookup to the rest of Landlock Date: Thu, 30 Jul 2026 22:20:37 -0400 Message-ID: <20260731022047.189137-5-utilityemal77@gmail.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260731022047.189137-1-utilityemal77@gmail.com> References: <20260731022047.189137-1-utilityemal77@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Rename get_ruleset_from_fd() to landlock_get_ruleset_from_fd() and give it external linkage within Landlock, declared in ruleset.h next to the other ruleset lifetime helpers. A following commit implements the LSM kfunc policy hooks, which need to translate a ruleset fd into a landlock_ruleset reference from outside syscalls.c. No behavioral change. Cc: Micka=C3=ABl Sala=C3=BCn Signed-off-by: Justin Suess --- security/landlock/ruleset.h | 3 +++ security/landlock/syscalls.c | 9 +++++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/security/landlock/ruleset.h b/security/landlock/ruleset.h index 0437adf17428..b65d1c07e192 100644 --- a/security/landlock/ruleset.h +++ b/security/landlock/ruleset.h @@ -225,6 +225,9 @@ struct landlock_ruleset * landlock_merge_ruleset(struct landlock_ruleset *const parent, struct landlock_ruleset *const ruleset); =20 +struct landlock_ruleset *landlock_get_ruleset_from_fd(const int fd, + const fmode_t mode); + const struct landlock_rule * landlock_find_rule(const struct landlock_ruleset *const ruleset, const struct landlock_id id); diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c index 36b02892c62f..9af2407274b2 100644 --- a/security/landlock/syscalls.c +++ b/security/landlock/syscalls.c @@ -293,8 +293,8 @@ SYSCALL_DEFINE3(landlock_create_ruleset, * Returns an owned ruleset from a FD. It is thus needed to call * landlock_put_ruleset() on the return value. */ -static struct landlock_ruleset *get_ruleset_from_fd(const int fd, - const fmode_t mode) +struct landlock_ruleset *landlock_get_ruleset_from_fd(const int fd, + const fmode_t mode) { CLASS(fd, ruleset_f)(fd); struct landlock_ruleset *ruleset; @@ -476,7 +476,7 @@ SYSCALL_DEFINE4(landlock_add_rule, const int, ruleset_f= d, return -EINVAL; =20 /* Gets and checks the ruleset. */ - ruleset =3D get_ruleset_from_fd(ruleset_fd, FMODE_CAN_WRITE); + ruleset =3D landlock_get_ruleset_from_fd(ruleset_fd, FMODE_CAN_WRITE); if (IS_ERR(ruleset)) return PTR_ERR(ruleset); =20 @@ -564,7 +564,8 @@ SYSCALL_DEFINE2(landlock_restrict_self, const int, rule= set_fd, const __u32, (flags & ~LANDLOCK_RESTRICT_SELF_TSYNC) =3D=3D LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF)) { /* Gets and checks the ruleset. */ - ruleset =3D get_ruleset_from_fd(ruleset_fd, FMODE_CAN_READ); + ruleset =3D landlock_get_ruleset_from_fd(ruleset_fd, + FMODE_CAN_READ); if (IS_ERR(ruleset)) return PTR_ERR(ruleset); } --=20 2.54.0 From nobody Fri Oct 2 14:03:02 2026 Received: from mail-yw1-f170.google.com (mail-yw1-f170.google.com [209.85.128.170]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id ECA7A31B80D for ; Fri, 31 Jul 2026 02:21:21 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.170 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785464485; cv=none; b=bizZjEfp8fDKaoOhDMgq31W8yTuYIHgrf9iHQ9pT61GnaCRkTnY74X79i4huVub6RJUJMzwkFYaWONblcXnIzIAYPVjWPqn2kZVC16RbBBDiLwMa/PDFPLfcDGaFcZeXWF8zGQIW+b51OE1wOk+sv8gCorMxscJiQNwjaNrUd4E= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785464485; c=relaxed/simple; bh=hSZLKL0ZVoziVn/VT3sa2EnQmfS/hzy/t7GIiq3s3qE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=OhlDJ/sbve3jmSdIwzJEO90JOOqK8MO9imiiguLSsMUPsLF3F36KnfS0oV2WOspifcc2a56hcyGEKNk/Za3vcaS80AI1UhYfao209rshji2opUm30uemxYhJrIUqs26h1tYRH4lLvKZ45DWRHOAMyW/qVrFMxWtATyCK3Ve114U= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=ldghJKV2; arc=none smtp.client-ip=209.85.128.170 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="ldghJKV2" Received: by mail-yw1-f170.google.com with SMTP id 00721157ae682-81f3b227a4aso7492667b3.1 for ; Thu, 30 Jul 2026 19:21:21 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1785464480; x=1786069280; darn=vger.kernel.org; h=content-transfer-encoding:content-type:mime-version:references :in-reply-to:message-id:date:subject:cc:to:from:from:to:cc:subject :date:message-id:reply-to:content-type; bh=E2YkARLK1UgWLJ9tWM7nw633P6+MEg0nLyubK9WrMGQ=; b=ldghJKV26wZ0KqFjxbLBm+5PmwfryPlwH/MfBCzS/rCpo24VQ75qn4TC3CZeAZ8O61 TeXUlgdCHwLDUyvhmH7en7lHbCRlwthc/Z6GY8PS0hbJFX6HT2sx+grL2hbfruAw58pu aE+bA5fiH6+PZV7o39kXO0fdHMkAi2p7ejks6pP6wBFZqvJuTPNvdOmow4ZkUpd6Tm9M Ifkzqpxyu84uSHKhUHmeMJbOS+YXFSIqfrl4saeif3T1cCbolaCbhpDX6k7HZWMoEk30 dtxHqiSdsDy9Z1U80sa1/rEEVqifbrU+/n3ryS/NygRYn43sxAi63Sh23qhu1I+vEOut sr1Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1785464480; x=1786069280; h=content-transfer-encoding:content-type:mime-version:references :in-reply-to:message-id:date:subject:cc:to:from:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=E2YkARLK1UgWLJ9tWM7nw633P6+MEg0nLyubK9WrMGQ=; b=CHH2bU+IsZZt3bNs8e5uLS+8Sb9XDmRE5zXH/YWPrSHSHZ/iauL2w62LrcBaBhAFx8 F9JRpFvXsf5NVbPrEdudcgIxYbMnm8uOPFQ70GJ/EWN6TXXzjKy7L/Mp/XAZkiOJHgbX tBqhBstU66i0tH0ch/yiqizd3BJ+RTMSfw2IYx5Y65xFbvK5ZAQVvX4txJe2DZYB25Kw 0Zv1aiT8NIN8y9BBn1YBKf+Jj6g5qHTlIs4lLizSsqyLIpMrHFTeQR68NJC9IectqVie sO3M9UsM5TjoSQ0chRX3ESRjPpcAwqe+N7oM9vWVQU1d4S2EXv6EtzxQu2NuKrcBTsKm X7vQ== X-Forwarded-Encrypted: i=1; AHgh+RoDoMWH5cQu5kznCthACXgFqKz3l9t85UlG4DOdOvel9bC+2bFh4jfouQMg3D8woFRmsQglK+yFQ4fsG9g=@vger.kernel.org X-Gm-Message-State: AOJu0Yzx86YIlXdNV93caISV6o93A1J0cYJ3MvYh9wnnQhCJ7XDa1qbB FiopQOaVc9haWBlm2WvbJi4paJ9xphcKVDjDKDdPl9JHNCRWixbZtq/P X-Gm-Gg: AR+sD137pobJgVYC+gyyOHDrJExQb1zF0CBNeAhYflcGymMYQqCDw75lNKgcsNALL3V sy0RJs+Axib9tYFka9/MVAVSeAvEGBwNKZtX/MrRCUttWnFGyQ3Vi2BmEXqbEOAHM989cp2SfFz fulncrX15KFeO/o4wL+c0ECKtklSWDfqa1oh2NlFZvQ7xW/E8+Hkp2ypqX8vppe+JP1EKN+R1Ut JOGhMGphG+0W8MyPY1rBPk/Cw1sngT0TuOMHrMGI0pMmn6YThBTUj5kLn0RBmIGRLWRSE8rACTg uITqK2ZJ97Hn/JqkSbCRy+JJkClq7brMBBxqxYRbzUO3PKgOZPDt/rRZw55BVBd9HsHyJnyD6nD P5XAB4MuysSgHGdM+eSf5MwVr0SC2mZpKRghrUWwRqCh+EzTm2WUYBRVeN1mRX5vPpeCVQVSU3B bQVTC/+2UhUhVPYd/hAMB6H4hK80VZ5pUoiv8er1sVxCMItm/NgZDm+aoYhXdpYWCUkgsjKpIG0 +06V6mdooDT+8o54/DnVA== X-Received: by 2002:a05:690c:660e:b0:80d:78bd:7a37 with SMTP id 00721157ae682-81fcbaff9ebmr585657b3.49.1785464479517; Thu, 30 Jul 2026 19:21:19 -0700 (PDT) Received: from zenbox ([2600:1700:18fb:6011:e94:8a83:feea:6720]) by smtp.gmail.com with ESMTPSA id 00721157ae682-81fb8b26e8bsm20519047b3.47.2026.07.30.19.21.18 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 30 Jul 2026 19:21:19 -0700 (PDT) From: Justin Suess To: ast@kernel.org, daniel@iogearbox.net, andrii@kernel.org, kpsingh@kernel.org, paul@paul-moore.com, mic@digikod.net, viro@zeniv.linux.org.uk, brauner@kernel.org, kees@kernel.org Cc: gnoack@google.com, jack@suse.cz, song@kernel.org, yonghong.song@linux.dev, martin.lau@linux.dev, m@maowtm.org, bpf@vger.kernel.org, linux-security-module@vger.kernel.org, linux-kernel@vger.kernel.org, Justin Suess Subject: [PATCH bpf-next 05/13] landlock: Factor the credential restriction out of landlock_restrict_self() Date: Thu, 30 Jul 2026 22:20:38 -0400 Message-ID: <20260731022047.189137-6-utilityemal77@gmail.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260731022047.189137-1-utilityemal77@gmail.com> References: <20260731022047.189137-1-utilityemal77@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Split the core of landlock_restrict_self() into two credential helpers: landlock_prepare_restriction() - translate the landlock_restrict_self(2) flags, merge the ruleset with the credentials' domain, and configure the new domain's audit log state, producing a struct landlock_restriction: the complete new state that the enforcement gives to a credential. landlock_apply_restriction() - enforce a computed restriction on credentials exclusively owned by the caller. This step cannot fail, so a caller may run it past its last point of failure. The syscall behaves exactly as before: prepare and apply run back to back on the prepared credentials. The no_new_privs/CAP_SYS_ADMIN precheck, the flag mask check, and the TSYNC handling are syscall policy and stay in place. The point of the split is that application is decoupled from computation: a following commit restricts an execution from a BPF kfunc by staging a prepared restriction in the binprm credentials and applying it at the exec point of no return. With the flag translation, domain merge, and audit log configuration in one shared place. Cc: Micka=C3=ABl Sala=C3=BCn Signed-off-by: Justin Suess --- security/landlock/cred.c | 100 +++++++++++++++++++++++++++++++++++ security/landlock/cred.h | 33 ++++++++++++ security/landlock/syscalls.c | 61 +++++---------------- 3 files changed, 147 insertions(+), 47 deletions(-) diff --git a/security/landlock/cred.c b/security/landlock/cred.c index cc419de75cd6..13b3952c31c5 100644 --- a/security/landlock/cred.c +++ b/security/landlock/cred.c @@ -8,14 +8,114 @@ */ =20 #include +#include #include +#include +#include #include +#include =20 #include "common.h" #include "cred.h" +#include "domain.h" #include "ruleset.h" #include "setup.h" =20 +/** + * landlock_prepare_restriction - Compute a credential restriction + * + * @llcred: Landlock credentials to restrict: provides the parent domain a= nd + * the previous log configuration. Not modified. + * @ruleset: Ruleset to enforce, or NULL for a log-configuration-only chan= ge. + * @flags: landlock_restrict_self(2) flags. The caller is responsible for + * validating them against the set of flags it supports. + * @restriction: Computed restriction. On success, holds a reference on + * @restriction->domain (if any), which + * landlock_apply_restriction() transfers to the restricted + * credentials. + * + * The restriction builds on @llcred's current state: the caller must apply + * it to (or stage it for) these same credentials. + * + * Return: 0 on success, -errno on failure. + */ +int landlock_prepare_restriction( + const struct landlock_cred_security *const llcred, + struct landlock_ruleset *const ruleset, const u32 flags, + struct landlock_restriction *const restriction) +{ +#ifdef CONFIG_AUDIT + /* Translates "off" and "on" flags to booleans. */ + const bool log_same_exec =3D + !(flags & LANDLOCK_RESTRICT_SELF_LOG_SAME_EXEC_OFF); + const bool log_new_exec =3D + !!(flags & LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON); + const bool log_subdomains =3D + !(flags & LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF); + const bool prev_log_subdomains =3D !llcred->log_subdomains_off; +#endif /* CONFIG_AUDIT */ + + *restriction =3D (struct landlock_restriction){}; + +#ifdef CONFIG_AUDIT + restriction->log_subdomains_off =3D !prev_log_subdomains || + !log_subdomains; +#endif /* CONFIG_AUDIT */ + + if (!ruleset) + return 0; + + restriction->domain =3D landlock_merge_ruleset(llcred->domain, ruleset); + if (IS_ERR(restriction->domain)) { + const int err =3D PTR_ERR(restriction->domain); + + restriction->domain =3D NULL; + return err; + } + +#ifdef CONFIG_AUDIT + restriction->domain->hierarchy->log_same_exec =3D log_same_exec; + restriction->domain->hierarchy->log_new_exec =3D log_new_exec; + if ((!log_same_exec && !log_new_exec) || !prev_log_subdomains) + restriction->domain->hierarchy->log_status =3D + LANDLOCK_LOG_DISABLED; +#endif /* CONFIG_AUDIT */ + + return 0; +} + +/** + * landlock_apply_restriction - Enforce a computed restriction on credenti= als + * + * @llcred: Landlock credentials to restrict, exclusively owned by the cal= ler + * (prepared and not yet committed). + * @restriction: Restriction computed by landlock_prepare_restriction() + * against the same credential state; its domain reference is + * transferred to @llcred. + * + * Cannot fail, so that a caller may apply a restriction past its last poi= nt + * of failure, e.g. an exec point of no return. + */ +void landlock_apply_restriction(struct landlock_cred_security *const llcre= d, + struct landlock_restriction *const restriction) +{ +#ifdef CONFIG_AUDIT + llcred->log_subdomains_off =3D restriction->log_subdomains_off; +#endif /* CONFIG_AUDIT */ + + if (!restriction->domain) + return; + + /* Replaces the old domain. */ + landlock_put_ruleset(llcred->domain); + llcred->domain =3D restriction->domain; + restriction->domain =3D NULL; + +#ifdef CONFIG_AUDIT + llcred->domain_exec |=3D BIT(llcred->domain->num_layers - 1); +#endif /* CONFIG_AUDIT */ +} + static void hook_cred_transfer(struct cred *const new, const struct cred *const old) { diff --git a/security/landlock/cred.h b/security/landlock/cred.h index f287c56b5fd4..1d5039b46ce7 100644 --- a/security/landlock/cred.h +++ b/security/landlock/cred.h @@ -20,6 +20,31 @@ #include "ruleset.h" #include "setup.h" =20 +/** + * struct landlock_restriction - Computed credential restriction + * + * The result of landlock_prepare_restriction(): the new state that + * enforcing a ruleset with a set of landlock_restrict_self(2) flags + * gives to a credential, decoupled from its application. It is + * enforced with landlock_apply_restriction(), either right away + * (landlock_restrict_self(2)) or after a staging period (restriction + * of an execution). + */ +struct landlock_restriction { + /** + * @domain: New domain to enforce, owning a reference. NULL if the + * restriction only carries a log configuration change. + */ + struct landlock_ruleset *domain; +#ifdef CONFIG_AUDIT + /** + * @log_subdomains_off: New value of the credentials' + * @landlock_cred_security.log_subdomains_off. + */ + u8 log_subdomains_off : 1; +#endif /* CONFIG_AUDIT */ +}; + /** * struct landlock_cred_security - Credential security blob * @@ -153,6 +178,14 @@ landlock_get_applicable_subject(const struct cred *con= st cred, return NULL; } =20 +int landlock_prepare_restriction( + const struct landlock_cred_security *const llcred, + struct landlock_ruleset *const ruleset, const u32 flags, + struct landlock_restriction *const restriction); + +void landlock_apply_restriction(struct landlock_cred_security *const llcre= d, + struct landlock_restriction *const restriction); + __init void landlock_add_cred_hooks(void); =20 #endif /* _SECURITY_LANDLOCK_CRED_H */ diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c index 9af2407274b2..899601af7c4e 100644 --- a/security/landlock/syscalls.c +++ b/security/landlock/syscalls.c @@ -528,9 +528,8 @@ SYSCALL_DEFINE2(landlock_restrict_self, const int, rule= set_fd, const __u32, { struct landlock_ruleset *ruleset __free(landlock_put_ruleset) =3D NULL; struct cred *new_cred; - struct landlock_cred_security *new_llcred; - bool __maybe_unused log_same_exec, log_new_exec, log_subdomains, - prev_log_subdomains; + struct landlock_restriction restriction; + int err; =20 if (!is_initialized()) return -EOPNOTSUPP; @@ -547,13 +546,6 @@ SYSCALL_DEFINE2(landlock_restrict_self, const int, rul= eset_fd, const __u32, LANDLOCK_MASK_RESTRICT_SELF) return -EINVAL; =20 - /* Translates "off" flag to boolean. */ - log_same_exec =3D !(flags & LANDLOCK_RESTRICT_SELF_LOG_SAME_EXEC_OFF); - /* Translates "on" flag to boolean. */ - log_new_exec =3D !!(flags & LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON); - /* Translates "off" flag to boolean. */ - log_subdomains =3D !(flags & LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF); - /* * It is allowed to set LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF with * -1 as ruleset_fd, optionally combined with @@ -575,53 +567,28 @@ SYSCALL_DEFINE2(landlock_restrict_self, const int, ru= leset_fd, const __u32, if (!new_cred) return -ENOMEM; =20 - new_llcred =3D landlock_cred(new_cred); - -#ifdef CONFIG_AUDIT - prev_log_subdomains =3D !new_llcred->log_subdomains_off; - new_llcred->log_subdomains_off =3D !prev_log_subdomains || - !log_subdomains; -#endif /* CONFIG_AUDIT */ - /* * The only case when a ruleset may not be set is if * LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF is set (optionally with * LANDLOCK_RESTRICT_SELF_TSYNC) and ruleset_fd is -1. We could * optimize this case by not calling commit_creds() if this flag was * already set, but it is not worth the complexity. + * + * There is no possible race condition while copying and manipulating + * the current credentials because they are dedicated per thread. */ - if (ruleset) { - /* - * There is no possible race condition while copying and - * manipulating the current credentials because they are - * dedicated per thread. - */ - struct landlock_ruleset *const new_dom =3D - landlock_merge_ruleset(new_llcred->domain, ruleset); - if (IS_ERR(new_dom)) { - abort_creds(new_cred); - return PTR_ERR(new_dom); - } - -#ifdef CONFIG_AUDIT - new_dom->hierarchy->log_same_exec =3D log_same_exec; - new_dom->hierarchy->log_new_exec =3D log_new_exec; - if ((!log_same_exec && !log_new_exec) || !prev_log_subdomains) - new_dom->hierarchy->log_status =3D LANDLOCK_LOG_DISABLED; -#endif /* CONFIG_AUDIT */ - - /* Replaces the old (prepared) domain. */ - landlock_put_ruleset(new_llcred->domain); - new_llcred->domain =3D new_dom; - -#ifdef CONFIG_AUDIT - new_llcred->domain_exec |=3D BIT(new_dom->num_layers - 1); -#endif /* CONFIG_AUDIT */ + err =3D landlock_prepare_restriction(landlock_cred(new_cred), ruleset, + flags, &restriction); + if (err) { + abort_creds(new_cred); + return err; } =20 + landlock_apply_restriction(landlock_cred(new_cred), &restriction); + if (flags & LANDLOCK_RESTRICT_SELF_TSYNC) { - const int err =3D landlock_restrict_sibling_threads( - current_cred(), new_cred); + err =3D landlock_restrict_sibling_threads(current_cred(), + new_cred); if (err) { abort_creds(new_cred); return err; --=20 2.54.0 From nobody Fri Oct 2 14:03:02 2026 Received: from mail-yx1-f51.google.com (mail-yx1-f51.google.com [74.125.224.51]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id CB5883290C2 for ; Fri, 31 Jul 2026 02:21:22 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.224.51 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785464485; cv=none; b=NcMN08GWkwAVtLmkCUd6zhbaeSMB6TrCZ32SQjJyN0z+8SzANBX8mAqDu2Ct77UkoDXx3HYmUGv1IGU6uQjU8pweZjDqsFyHeL/11O6aDaa+xrVR4jDIfGMKmeuR/U/hW4z1czHJRkRFITyPBwRG1KjRxULBpIu9bAC6+kKAt20= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785464485; c=relaxed/simple; bh=bMDEDdCfxgq2K0qqDR8vs2QrKB8d6z+6EloqlgwtX/c=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=kE3kkmo2lIe78vs1SzxG/8rf96XsbE24P3/yI9oC0lUijYFAdgAA+kts4q8w5xFxiPaFE0Oym4V542xKqwuoTXZW4yCmOXOn0AKFUq5q1b8w6OTLXkORxsFgfm7KyMe6LgrrzluMypx1f15JckNHkiigD98cGyrRE3ZU5ljYOVA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=CAxCK+Ko; arc=none smtp.client-ip=74.125.224.51 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="CAxCK+Ko" Received: by mail-yx1-f51.google.com with SMTP id 956f58d0204a3-6681e7911b0so551091d50.0 for ; Thu, 30 Jul 2026 19:21:22 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1785464481; x=1786069281; darn=vger.kernel.org; h=content-transfer-encoding:content-type:mime-version:references :in-reply-to:message-id:date:subject:cc:to:from:from:to:cc:subject :date:message-id:reply-to:content-type; bh=oFgeHGuQz0CJY+tXoH6+LNvpN+Gw0GAfvG1RIFPOUvo=; b=CAxCK+KopSlWXCjBdr7rSOpm54K7iN627+A5tCF+tgeClhmUe/Xlm46OS2IcyRanSB /oh5QmDu1IiYS9lJKqsK9zFjL+FP5mq+wnVZQP+Zk1H8qoVGO6/1bgnmi6WQu2UUyV0q 3RZ5fJo4skSURN//vhdg7cpn+Hi2zmt7utGHYDywfsQvTXiLMgfmafZPNN+QBM7Db5M4 65Lt5tUNLAC/xED/hHjt/+9T9j9JhEilApA7cLutOc8Vo5hycd1o2sB9dY9baAVTrJp1 i/lgXzW4d+Tj7AdM/DfnwqGgp+DeDEY3zY+nmnzXxr98b26skDf9Wrqdw43VxRtrG7Xs IuJA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1785464481; x=1786069281; h=content-transfer-encoding:content-type:mime-version:references :in-reply-to:message-id:date:subject:cc:to:from:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=oFgeHGuQz0CJY+tXoH6+LNvpN+Gw0GAfvG1RIFPOUvo=; b=j6pDQ98g5gyZmMPT5fDwNDu6CzVIZq0YuR65n0HUqY4AAot/bCaKxhZFGvFTZY5olw NXgP0v5F2htrL9HH5tDwzCK5YSKUQAFzoSsuB3QX+EgUfJfMrjl/yH/eV17qW0x9Qz3x 64gI+NeVj7lO0RN+/b/bu9BXm8FVSO1JyaeQQwFN1zpD0i7fHGC8anFA6mbt2fEFmK+o L9cR+OYZ2fvOYV+ZDoFp2hWAv+pl4Gmt1mwBzOSWAwZS/7I25KEodBPsGF2BzMTN9STi fxcBcR5pPuMujJltymE5uYrWB7sLzgbyftIL9mylGTsY1Aq+0Lv089Yqyw1vWmdqu7KJ U6sA== X-Forwarded-Encrypted: i=1; AHgh+Rpa2YovI1p3qbkIS3Z+PCyT+Ty3AaELCMojA0pUqamYvAyluLDKUmzh5SXYibQkzb0kr8/48x9PnnKZ/uU=@vger.kernel.org X-Gm-Message-State: AOJu0YzJO2O9YcZAl63er20/y/sereo31ZK2/AwdVeVUZ0UlfT0Omko6 +6cv98w6SWa0k3KUKj7yMicwuVlQVDpXMFeACjPTOCoXOyqufMOsSxwpLP46RL6+ X-Gm-Gg: AR+sD13dQoCYGnOLCguAUErFAstwqMfAGyTWKYW0KPcmYMvCGgMtaNmJ18jH46pNJWe Al5N49ic/J0MnYmuhAuMsHSttLM8vOLH9JvUO9SvudzrkEOH/TzdEqpBKz6kipzyzZjNkR8WbEJ 8Ykq/h2O7C6aW1GyhkXY0M/y8bLxlymMewlHJX2qQKLbb8UtQz6jwkvRZx/965s8gFplYvCfiIt O05G4Aw8rG+zjF/PmrIi888Sf/bFFSn5WaEEtVMl/WXne/Dgdpwr11VJqnDQMoZzs4MHFbZWENF dBsNS31a6oDjNkxYw0RSXeYZAim7hblqFBbrIhLE0ZDrY+u5oyuB4ZUUJQpenKvsFgwTdmB5U1r fGJS1wRI18Hw6qPNzjDHLpCvd/6Sd8g1Vx10ojEHQC9yfak2ARdD1Eey8wws5F/NigDM6PdBDV/ UcjBgqh/zCWL9MErbvT8bV2oDvp3s1QEf1AnlnL/IhC5sXmox+oT7ivGPbWxpFZsQRgOuDkoVt9 DGSeM53CBUVstXTlbN5KwY= X-Received: by 2002:a05:690c:d90:b0:81e:8b74:b35c with SMTP id 00721157ae682-81fcbae24c5mr799387b3.36.1785464481061; Thu, 30 Jul 2026 19:21:21 -0700 (PDT) Received: from zenbox ([2600:1700:18fb:6011:e94:8a83:feea:6720]) by smtp.gmail.com with ESMTPSA id 00721157ae682-81fb8b26e8bsm20519047b3.47.2026.07.30.19.21.20 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 30 Jul 2026 19:21:20 -0700 (PDT) From: Justin Suess To: ast@kernel.org, daniel@iogearbox.net, andrii@kernel.org, kpsingh@kernel.org, paul@paul-moore.com, mic@digikod.net, viro@zeniv.linux.org.uk, brauner@kernel.org, kees@kernel.org Cc: gnoack@google.com, jack@suse.cz, song@kernel.org, yonghong.song@linux.dev, martin.lau@linux.dev, m@maowtm.org, bpf@vger.kernel.org, linux-security-module@vger.kernel.org, linux-kernel@vger.kernel.org, Justin Suess Subject: [PATCH bpf-next 06/13] landlock: Implement the LSM policy kptr hooks Date: Thu, 30 Jul 2026 22:20:39 -0400 Message-ID: <20260731022047.189137-7-utilityemal77@gmail.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260731022047.189137-1-utilityemal77@gmail.com> References: <20260731022047.189137-1-utilityemal77@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Implement the generic LSM hooks backing the BPF-owned Landlock kfuncs. The new code is gated on CONFIG_BPF_LSM, the only configuration where the kfuncs calling the hooks exist. The policy objects travel in the Landlock member of union lsm_policy_kptr as struct bpf_landlock_ruleset handles, the BTF-visible type the verifier tracks; bpf.c is the only place converting between the handle and struct landlock_ruleset. - policy_kptr_from_fd() translates a ruleset fd, created with landlock_create_ruleset(2) and populated with landlock_add_rule(2), into an owned landlock_ruleset reference. The fd is validated the same way as for the Landlock syscalls (ruleset file type, FMODE_CAN_READ). - policy_kptr_put() releases such a reference. The free is always deferred as the hook may be reached from BPF object destructors that cannot sleep. - bprm_enforce_policy_kptr() shares the landlock_restrict_self(2) path: it calls landlock_prepare_restriction() on the credentials prepared in the binprm and stages the computed restriction in their Landlock blob. Nothing is applied at this point, and there is no flag logic of its own: a new landlock_restrict_self(2) feature implemented in the shared helpers works here as well. The only divergence is the flag mask: LANDLOCK_RESTRICT_SELF_TSYNC is rejected with -EINVAL because the restriction targets the execution, not the calling threads. The restriction is computed in a root memcg charging scope: the domain confines the execution on behalf of the BPF program, so its GFP_KERNEL_ACCOUNT allocations are not charged to the mediated task. The staged restriction is enforced by a bprm_committing_creds() hook with the same landlock_apply_restriction() call as the syscall, past the exec point of no return: a failed execution can no longer return to the calling program at that point and the application cannot fail, so an execution either starts confined by the domain or leaves the calling task untouched. The applied layer is accounted in domain_exec: for audit, the confined execution is the one that enforced the domain, so the LOG_SAME_EXEC and LOG_NEW_EXEC flags follow the executed program. There is no no_new_privs/CAP_SYS_ADMIN precondition here: gating who may load a policy-applying BPF program is the BPF attachment's privilege model. The staged restriction's lifetime is fully covered: a second bprm_enforce_policy_kptr() call on the same execution releases and replaces the previously staged restriction. An execution failing before the point of no return releases it through hook_cred_free() when the prepared credentials are aborted, and the application clears the staging field so committed task credentials never carry a staged restriction. The credential copy helpers (hook_cred_transfer(), landlock_cred_copy()) uphold that invariant by never copying a staged restriction. Cc: Micka=C3=ABl Sala=C3=BCn Signed-off-by: Justin Suess --- security/landlock/Makefile | 2 + security/landlock/bpf.c | 130 ++++++++++++++++++++++++++++++++++++ security/landlock/bpf.h | 21 ++++++ security/landlock/cred.c | 16 ++++- security/landlock/cred.h | 18 +++++ security/landlock/limits.h | 4 ++ security/landlock/ruleset.c | 2 +- security/landlock/setup.c | 2 + 8 files changed, 191 insertions(+), 4 deletions(-) create mode 100644 security/landlock/bpf.c create mode 100644 security/landlock/bpf.h diff --git a/security/landlock/Makefile b/security/landlock/Makefile index ffa7646d99f3..9ba28c06b5ac 100644 --- a/security/landlock/Makefile +++ b/security/landlock/Makefile @@ -16,3 +16,5 @@ landlock-$(CONFIG_AUDIT) +=3D \ id.o \ audit.o \ domain.o + +landlock-$(CONFIG_BPF_LSM) +=3D bpf.o diff --git a/security/landlock/bpf.c b/security/landlock/bpf.c new file mode 100644 index 000000000000..cf89062d35a7 --- /dev/null +++ b/security/landlock/bpf.c @@ -0,0 +1,130 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Landlock - LSM policy kptr hooks + * + * Implementation of the LSM hooks backing the Landlock kfuncs + * + * Copyright =C2=A9 2026 Justin Suess + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bpf.h" +#include "cred.h" +#include "limits.h" +#include "ruleset.h" +#include "setup.h" + +static int hook_policy_kptr_from_fd(int fd, union lsm_policy_kptr *policy) +{ + struct landlock_ruleset *ruleset; + + ruleset =3D landlock_get_ruleset_from_fd(fd, FMODE_CAN_READ); + if (IS_ERR(ruleset)) + return PTR_ERR(ruleset); + + policy->landlock.ruleset =3D (struct bpf_landlock_ruleset *)ruleset; + return 0; +} + +static void hook_policy_kptr_put(union lsm_policy_kptr *policy) +{ + struct landlock_ruleset *ruleset =3D + (struct landlock_ruleset *)policy->landlock.ruleset; + + /* + * May be called from a BPF object destructor that cannot sleep, + * whereas dropping the last ruleset reference frees it and may + * sleep: always defer the free. + */ + landlock_put_ruleset_deferred(ruleset); +} + +/* Charging scope for the BPF-driven domain allocations: root, i.e. nobody= . */ +static struct mem_cgroup *get_bpf_memcg(void) +{ +#ifdef CONFIG_MEMCG + return root_mem_cgroup; +#else + return NULL; +#endif /* CONFIG_MEMCG */ +} + +static int hook_bprm_enforce_policy_kptr(struct linux_binprm *bprm, + union lsm_policy_kptr *policy, + u32 flags) +{ + struct landlock_cred_security *bprm_llcred =3D landlock_cred(bprm->cred); + struct landlock_ruleset *ruleset =3D + (struct landlock_ruleset *)policy->landlock.ruleset; + struct landlock_restriction restriction; + struct mem_cgroup *old_memcg; + int err; + + /* + * Same flags as landlock_restrict_self(2), except + * LANDLOCK_RESTRICT_SELF_TSYNC: the restriction targets the + * execution, not the calling threads. + */ + if ((flags | LANDLOCK_MASK_RESTRICT_BINPRM) !=3D + LANDLOCK_MASK_RESTRICT_BINPRM) + return -EINVAL; + + /* + * The domain confines the execution on behalf of the BPF + * program, not of the mediated task: do not charge the task's + * memcg for it. + */ + old_memcg =3D set_active_memcg(get_bpf_memcg()); + err =3D landlock_prepare_restriction(bprm_llcred, ruleset, flags, + &restriction); + set_active_memcg(old_memcg); + if (err) + return err; + + /* + * Stages the restriction until the point of no return of the + * execution, replacing (and releasing) any previously staged + * one. Nothing is enforced yet: an execution that fails before + * committing its credentials drops the staged restriction in + * hook_cred_free() with no effect on the calling task. + */ + landlock_put_ruleset(bprm_llcred->staged.domain); + bprm_llcred->staged =3D restriction; + return 0; +} + +static void hook_bprm_committing_creds(const struct linux_binprm *bprm) +{ + struct landlock_cred_security *bprm_llcred =3D landlock_cred(bprm->cred); + struct landlock_restriction restriction; + + if (!bprm_llcred->staged.domain) + return; + + restriction =3D bprm_llcred->staged; + bprm_llcred->staged =3D (struct landlock_restriction){}; + + landlock_apply_restriction(bprm_llcred, &restriction); +} + +static struct security_hook_list landlock_hooks[] __ro_after_init =3D { + LSM_HOOK_INIT(policy_kptr_from_fd, hook_policy_kptr_from_fd), + LSM_HOOK_INIT(policy_kptr_put, hook_policy_kptr_put), + LSM_HOOK_INIT(bprm_enforce_policy_kptr, hook_bprm_enforce_policy_kptr), + LSM_HOOK_INIT(bprm_committing_creds, hook_bprm_committing_creds), +}; + +__init void landlock_add_bpf_hooks(void) +{ + security_add_hooks(landlock_hooks, ARRAY_SIZE(landlock_hooks), + &landlock_lsmid); +} diff --git a/security/landlock/bpf.h b/security/landlock/bpf.h new file mode 100644 index 000000000000..e57729e6a564 --- /dev/null +++ b/security/landlock/bpf.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Landlock - LSM policy kptr hooks + * + * Copyright =C2=A9 2026 Justin Suess + */ + +#ifndef _SECURITY_LANDLOCK_BPF_H +#define _SECURITY_LANDLOCK_BPF_H + +#include + +#ifdef CONFIG_BPF_LSM +__init void landlock_add_bpf_hooks(void); +#else /* CONFIG_BPF_LSM */ +static inline void landlock_add_bpf_hooks(void) +{ +} +#endif /* CONFIG_BPF_LSM */ + +#endif /* _SECURITY_LANDLOCK_BPF_H */ diff --git a/security/landlock/cred.c b/security/landlock/cred.c index 13b3952c31c5..efbfd0c20475 100644 --- a/security/landlock/cred.c +++ b/security/landlock/cred.c @@ -124,6 +124,12 @@ static void hook_cred_transfer(struct cred *const new, =20 landlock_get_ruleset(old_llcred->domain); *landlock_cred(new) =3D *old_llcred; + +#ifdef CONFIG_BPF_LSM + /* Only bprm credentials own a staged restriction: never copied. */ + WARN_ON_ONCE(landlock_cred(new)->staged.domain); + landlock_cred(new)->staged =3D (struct landlock_restriction){}; +#endif /* CONFIG_BPF_LSM */ } =20 static int hook_cred_prepare(struct cred *const new, @@ -135,10 +141,14 @@ static int hook_cred_prepare(struct cred *const new, =20 static void hook_cred_free(struct cred *const cred) { - struct landlock_ruleset *const dom =3D landlock_cred(cred)->domain; + struct landlock_cred_security *const llcred =3D landlock_cred(cred); + + landlock_put_ruleset_deferred(llcred->domain); =20 - if (dom) - landlock_put_ruleset_deferred(dom); +#ifdef CONFIG_BPF_LSM + /* Releases a restriction staged for an aborted execution. */ + landlock_put_ruleset_deferred(llcred->staged.domain); +#endif /* CONFIG_BPF_LSM */ } =20 #ifdef CONFIG_AUDIT diff --git a/security/landlock/cred.h b/security/landlock/cred.h index 1d5039b46ce7..74f8c9808dc4 100644 --- a/security/landlock/cred.h +++ b/security/landlock/cred.h @@ -60,6 +60,18 @@ struct landlock_cred_security { */ struct landlock_ruleset *domain; =20 +#ifdef CONFIG_BPF_LSM + /** + * @staged: Restriction staged by the bprm_enforce_policy_kptr() hook, + * owning its domain reference and applied at the point of no return of + * the execution (bprm_committing_creds). Only ever set on the + * credentials prepared for an execution, between the staging and + * either the application or the release of the aborted credentials; + * committed task credentials never carry a staged restriction. + */ + struct landlock_restriction staged; +#endif /* CONFIG_BPF_LSM */ + #ifdef CONFIG_AUDIT /** * @domain_exec: Bitmask identifying the domain layers that were enforced= by @@ -100,6 +112,12 @@ static inline void landlock_cred_copy(struct landlock_= cred_security *dst, *dst =3D *src; =20 landlock_get_ruleset(src->domain); + +#ifdef CONFIG_BPF_LSM + /* Only bprm credentials own a staged restriction: never copied. */ + WARN_ON_ONCE(src->staged.domain); + dst->staged =3D (struct landlock_restriction){}; +#endif /* CONFIG_BPF_LSM */ } =20 static inline struct landlock_ruleset *landlock_get_current_domain(void) diff --git a/security/landlock/limits.h b/security/landlock/limits.h index 08d5f2f6d321..0bedfe650ea0 100644 --- a/security/landlock/limits.h +++ b/security/landlock/limits.h @@ -37,6 +37,10 @@ #define LANDLOCK_LAST_RESTRICT_SELF LANDLOCK_RESTRICT_SELF_TSYNC #define LANDLOCK_MASK_RESTRICT_SELF ((LANDLOCK_LAST_RESTRICT_SELF << 1) - = 1) =20 +/* Subset of the restrict-self flags applicable to an execution. */ +#define LANDLOCK_MASK_RESTRICT_BINPRM \ + (LANDLOCK_MASK_RESTRICT_SELF & ~LANDLOCK_RESTRICT_SELF_TSYNC) + /* clang-format on */ =20 #endif /* _SECURITY_LANDLOCK_LIMITS_H */ diff --git a/security/landlock/ruleset.c b/security/landlock/ruleset.c index 4dd09ea22c84..176c626f9f10 100644 --- a/security/landlock/ruleset.c +++ b/security/landlock/ruleset.c @@ -520,7 +520,7 @@ static void free_ruleset_work(struct work_struct *const= work) free_ruleset(ruleset); } =20 -/* Only called by hook_cred_free(). */ +/* For contexts that cannot sleep, e.g. hook_cred_free(). */ void landlock_put_ruleset_deferred(struct landlock_ruleset *const ruleset) { if (ruleset && refcount_dec_and_test(&ruleset->usage)) { diff --git a/security/landlock/setup.c b/security/landlock/setup.c index 47dac1736f10..3b7e18edadfb 100644 --- a/security/landlock/setup.c +++ b/security/landlock/setup.c @@ -11,6 +11,7 @@ #include #include =20 +#include "bpf.h" #include "common.h" #include "cred.h" #include "errata.h" @@ -68,6 +69,7 @@ static int __init landlock_init(void) landlock_add_task_hooks(); landlock_add_fs_hooks(); landlock_add_net_hooks(); + landlock_add_bpf_hooks(); landlock_init_id(); landlock_initialized =3D true; pr_info("Up and running.\n"); --=20 2.54.0 From nobody Fri Oct 2 14:03:02 2026 Received: from mail-yw1-f173.google.com (mail-yw1-f173.google.com [209.85.128.173]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 733C432E128 for ; Fri, 31 Jul 2026 02:21:24 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.173 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785464486; cv=none; b=pqtCNOZ0mKZL9i6Mz0VOR811gokVIVZJh+tzQfeUAUCHwoZxXm09KGhlPDDL2niSQAASzJhZkS6CCK250SJUBn4MV7bRdkMhWLN6MORV68C/nyvLHCm5aWt9prKx+wQQ/XLAj7B+K2+9XB1UzZkT/WULT4l0VTzoIBPSFGMNmuA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785464486; c=relaxed/simple; bh=Wl2UCxtNmzLVXv7d2dxNGx2+o2cZF0FTl7baGMMo3wE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=if8NIbfGAQCI+AqZVOmkOldqnjgmsPqALiyCJKdM2ivttm5m8/r9RiLPS2622cW0+fI3BJK+7I9yxpLucJ1g2CniRcYDbMgPAf67H97Vwir9bgkbxWNxf+7jGe1EE5qJHbTDvfl6xJbug6X0SEg8EUsdQXqkfosP4xPa8l9bpjo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=FXAGKQJQ; arc=none smtp.client-ip=209.85.128.173 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="FXAGKQJQ" Received: by mail-yw1-f173.google.com with SMTP id 00721157ae682-81f3b227a4aso7493297b3.1 for ; Thu, 30 Jul 2026 19:21:24 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1785464483; x=1786069283; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=LFpwdBGwLOjN+6goU5qnqwdSehafEaJ80d3ncvWusC4=; b=FXAGKQJQOec3WtYjgKHjZBbm7/p5t+f7eLzZyKh+0LOPDJDbUXO+pHp4VVPs31Yk1i Py76tUQvd+h6kOe4Lo33ooIJ4emHwrokYRXbHoNmzy/Q96UlJPak8CftJiHPJlJJfYdk +BnbH0A7rFMhFT92Zo037zIfHUeGOX8Gpv1WXg6lJkMZnez0Nh36JGyxWhesM8dGZTh9 2N7JKmSqdVa7qWvgm7a2Km03RjWkVHdlfpZUC8GWJ5cBs/FeuAWZCozfjGyxKY6/Poxq xUC78orml7jaDczQ/LXvsvWrD4LwqrHfjLQqqigvk0jmO5gbzfWDyeIPPgERcs4u5gQ+ sYOw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1785464483; x=1786069283; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=LFpwdBGwLOjN+6goU5qnqwdSehafEaJ80d3ncvWusC4=; b=KXPJ86Tx9uorKBqbCG/EA3hICYxodBU67fea6Timb5jDJnipnEj7IqctBF6zklKL8P PjSVHXjQLSW7J/6UIoSPvBQJGjO0wqJCZ/8LSxye4XXpPlz+6EzwNBZk71rKwSAV0eXa zMPbp5R1X0yJGZ3KivwbjqXgEitqNrIumHnMfuPToO44l6iR5ybAlr4Lp8oRJ1bZhr3Y ZYXQaT6dfWCWcsu3wiZf4LvPj3DBfrnCtkpquIu4hjXFgu0Zovnh5HV4qZVuCwtzFH5x Gcj8rzn+Gvb3A/Ffo9JhzirWhNDGfaTanN5Ec4kU3OttABC5OBT65rIx+pV8ybbBb0qf nbXQ== X-Forwarded-Encrypted: i=1; AHgh+Rp83uk/iPVjpz2RtGwyzAe4xvMWLTr7iLoh9POpfpiw+Q0d1qgPTfR/y1SHdXJTWvCAs2WEBSqvHumdQ3I=@vger.kernel.org X-Gm-Message-State: AOJu0YzCIga9i1Fcylz0wxhXe4chPZGGTXhbQWXZT0D5LbRmEB/pTjlF Y0OvpuqCg0GGqG9VRdgwWV7ny/5lZFSTSbr68puUNNwgtjUCczLghw/0 X-Gm-Gg: AR+sD10AJl9g+0LXxuWKRGjDZb0OTnZWjF7YhhmnzNtwgpBcHL+hE9ugbqpQDoUWA+c R/oXjhsJKb5ByYnCc0iYKHg0iFlNtcTCfx0bOKlVnjizV5XaJGghsLJVKh5AVVH8Qx5FpYRHOwd DMC1LJTtQbXpnQIIX3r90PTE0uexjko0lSluXWiiKMBFh3D+V2x6IgJDW3q7y3gdzby4UhfQmc5 ht0RIeDjJR9jUI53pO9GACFUFbAPJvpS4IckWnJMsKnYiwKchz/GP46LsukVlyvuxJAd8Y0ecjw 07Op4MdcH4ltUQ3fyWR0llrXd5X2lg5D8nxhlhJDDUoanv2py/YoOrK1sVpUPZmW6W2UCMhOCd4 WVAJpqbfdvc8FhjGZJF0V3s19knHI1BE8g8LV3/Vnbj1eqYe/xkLo3acZnN2Fq50HL37VvcX2XE jwSvQsUXAjZ4EFzRQRTFMkuTHGX7Gr4yp1Y3LQyGOsaGX2X0+3JYOVjwiJV6wkCNnQiOuqcNyE4 ryQ+1yKbopBEslFfLi+SQ== X-Received: by 2002:a05:690c:9688:b0:80c:85c6:897f with SMTP id 00721157ae682-81fcbb332a6mr451497b3.62.1785464482738; Thu, 30 Jul 2026 19:21:22 -0700 (PDT) Received: from zenbox ([2600:1700:18fb:6011:e94:8a83:feea:6720]) by smtp.gmail.com with ESMTPSA id 00721157ae682-81fb8b26e8bsm20519047b3.47.2026.07.30.19.21.22 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 30 Jul 2026 19:21:22 -0700 (PDT) From: Justin Suess To: ast@kernel.org, daniel@iogearbox.net, andrii@kernel.org, kpsingh@kernel.org, paul@paul-moore.com, mic@digikod.net, viro@zeniv.linux.org.uk, brauner@kernel.org, kees@kernel.org Cc: gnoack@google.com, jack@suse.cz, song@kernel.org, yonghong.song@linux.dev, martin.lau@linux.dev, m@maowtm.org, bpf@vger.kernel.org, linux-security-module@vger.kernel.org, linux-kernel@vger.kernel.org, Justin Suess Subject: [PATCH bpf-next 07/13] bpf: Add the LSM policy kfunc infrastructure Date: Thu, 30 Jul 2026 22:20:40 -0400 Message-ID: <20260731022047.189137-8-utilityemal77@gmail.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260731022047.189137-1-utilityemal77@gmail.com> References: <20260731022047.189137-1-utilityemal77@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Prepare kernel/bpf/bpf_lsm.c to host kfuncs that let BPF programs apply a userspace-created Landlock ruleset to an execution. The kfuncs will be thin front ends to the generic LSM policy kptr hooks (security_policy_kptr_from_fd(), security_policy_kptr_put(), security_bprm_enforce_policy_kptr()), invoked with LSM_ID_LANDLOCK so that the LSM framework's targeted dispatch only ever reaches Landlock's hook implementations. Because of the hook indirection, kernel/bpf/ has no build-time dependency on Landlock: the kfuncs are registered whenever CONFIG_BPF_LSM is enabled, and calling them while Landlock is compiled out or not enabled in the LSM order fails at runtime with -EOPNOTSUPP through the dispatch miss, keeping BPF program loading independent of the boot-time LSM configuration. Add the section hosting the kfuncs: struct bpf_landlock_ruleset, the opaque BTF-typed handle for a Landlock ruleset that only Landlock resolves; the kfunc id set, registered for both BPF_PROG_TYPE_LSM and BPF_PROG_TYPE_SYSCALL; and the kfunc filter. The two program types share their kfunc lookup buckets with other program types, so restricting the kfuncs to them requires a filter. The set starts empty and the filter has no per-kfunc rules yet; the following patches add the kfuncs together with their filter rules. Signed-off-by: Justin Suess --- Notes: I decided to put the kfunc implementations in kernel/bpf to better delineate the separation between the BPF facing interface and the LSM framework. Since this file contains things like the BPF contexts the kfuncs are allowed to be called from, it's important for BPF to control that aspect. =20 I'm open to moving it if there is a better preferred location for these under kernel/bpf/ other than kernel/bpf/bpf_lsm.c. kernel/bpf/bpf_lsm.c | 50 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/kernel/bpf/bpf_lsm.c b/kernel/bpf/bpf_lsm.c index d847a180489f..dd58c5bd0119 100644 --- a/kernel/bpf/bpf_lsm.c +++ b/kernel/bpf/bpf_lsm.c @@ -473,3 +473,53 @@ int bpf_lsm_get_retval_range(const struct bpf_prog *pr= og, } return 0; } + +/* LSM policy kfuncs */ + +/* + * Opaque handle for a Landlock ruleset. Only Landlock resolves it. + */ +struct bpf_landlock_ruleset {}; + +BTF_KFUNCS_START(bpf_landlock_kfunc_ids) +BTF_KFUNCS_END(bpf_landlock_kfunc_ids) + +/* + * BPF_PROG_TYPE_LSM and BPF_PROG_TYPE_SYSCALL share their kfunc + * lookup buckets with other program types, so restricting the LSM + * policy kfuncs requires a filter. + */ +static int bpf_landlock_kfunc_filter(const struct bpf_prog *prog, u32 kfun= c_id) +{ + if (!btf_id_set8_contains(&bpf_landlock_kfunc_ids, kfunc_id)) + return 0; + + switch (prog->type) { + case BPF_PROG_TYPE_SYSCALL: + return 0; + case BPF_PROG_TYPE_LSM: + return 0; + default: + return -EACCES; + } +} + +static const struct btf_kfunc_id_set bpf_landlock_kfunc_set =3D { + .owner =3D THIS_MODULE, + .set =3D &bpf_landlock_kfunc_ids, + .filter =3D bpf_landlock_kfunc_filter, +}; + +static int __init bpf_lsm_policy_kfunc_init(void) +{ + int ret; + + ret =3D register_btf_kfunc_id_set(BPF_PROG_TYPE_LSM, + &bpf_landlock_kfunc_set); + if (ret) + return ret; + + return register_btf_kfunc_id_set(BPF_PROG_TYPE_SYSCALL, + &bpf_landlock_kfunc_set); +} +late_initcall(bpf_lsm_policy_kfunc_init); --=20 2.54.0 From nobody Fri Oct 2 14:03:02 2026 Received: from mail-yx1-f50.google.com (mail-yx1-f50.google.com [74.125.224.50]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id CA270311977 for ; Fri, 31 Jul 2026 02:21:25 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.224.50 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785464488; cv=none; b=RYUTXuAjR/oghjHrTA40qMTd2cTqkHAD53J5O4gjXbwLyjIvQdIWrFUPoRAUtlz1Gnh+gf1lf0n4jD2zgxS8wYrmCuuNxOkG34qzrPowhHCtSGMFXbxqs2SBMrUlUVHzKedEYFxGvEVZE1z8PoUIGVtHWLlAYrutqgtADcyFsZo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785464488; c=relaxed/simple; bh=NL8mBC7WHXg65243KavwzxselJ8EusI6R5QSuWKlbs8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=CrqFeYSg7mheaalyUmlkr8suRcAlExT4xaY5F+5+2MA3zWORLiT3WRUJwZ3MPFbmum2/qKqo1VQtAApIYI8QRMR1F0GlVEdczZVhevoVCVpT2JQWZl0znLuJqTo8JKcZWroHACEQlWRrWeccEJWqRS2P6YKEOGa6/gWbUAc74Eg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=oiFkuMY3; arc=none smtp.client-ip=74.125.224.50 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="oiFkuMY3" Received: by mail-yx1-f50.google.com with SMTP id 956f58d0204a3-6689f36ae56so617306d50.3 for ; Thu, 30 Jul 2026 19:21:25 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1785464484; x=1786069284; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=vTtFKNvHB2/Ojp2xHTtFD0mxvMCw7gVj2kPcAk5LXGw=; b=oiFkuMY3lsVKuL5Qqrwgzh1oEskSwGwnF/VvkkRpNEVvd7Ze1SZ7Sr6sFaEQP8VM1u +h5CM+4Mr3xQxgfXpsjb+lnScR40QZyEJ+4YzDr/LEyKL7uobe3hKZdfuN/K39k0ad/W JBClrrfN+8u3bU3jJfPLgDreaVabggDWRmMNcwISOKOZ7WmbTNAyn0lvt0zPIYM33dHA Nz38dwhtYkaEnZQoCbGkyr2M8zEk0zIAVspRyZt6H7iPbMYPFd/X++htTZUOkCdRmzBc kcIWZqDYX9VDa8LxGRu3rm/O2275AjVeiyQpVceoj2Y+G3uGe+SkX28InW5FQQnWKaJp Xqwg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1785464484; x=1786069284; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=vTtFKNvHB2/Ojp2xHTtFD0mxvMCw7gVj2kPcAk5LXGw=; b=hkZ3F5R99bp2Z+aQEVpXMP52cNnEeUchT/aCAGUqDs8tFuNyvYk2IHvlBVOdX1nS1b A3O+7OYF9J+fP5MYL20yzUdSjSVe6Cn4UGHcpo/6yi5gSnG68iBWFhA3ZJ7yYHRRB7cg wIX7wBVCcinVTnGDqOHSYdGH3yo+CdX04NCyUi5P9d0mxkigEuTSTQCUW1UzVGaDDn+2 Vbq3zxiUBkoSs/qZI5j4txrC6jTfVgF+1Yz7l0ZflMPoLBN2ZMjKMWSq8nCbs+cQyPEa JotohXlDhiuSroR8Ln74XkXyaxXHTJW+1Q6iSqo/Jqi41ZAQ4UEV/vnnl/5nzWjLkugr J/Ew== X-Forwarded-Encrypted: i=1; AHgh+Rr5ySGVHRXrzjKeHCuRDXElNkSP0gckwNHTWk4Ysgt4+YeHhiNDuRysTFDKf03PdCU3R5NKKnaVwh83Eek=@vger.kernel.org X-Gm-Message-State: AOJu0YzMjq7Q0T9Z8TiOHVIdulSSA74AxkDsWtR5nFxxDXCzF9Uhihx4 FN/3b5C20ZkVPPwAPf8GDVo9sqPGTx/JInAqsFE+PcJ156Xo/xNJ0Pca X-Gm-Gg: AR+sD13/sYhs1kOiqBja84+hvqXHOTpcSiTZdMpYsgOg6Cwbsmko47/Dt1IULqErEfP uqGIcoKWCmyzW6ORQBFwHzLtZq6z/fLa30+XxZsfLlnT9VRhFaGHxfITUNuqu4aMKNcU7D95P72 F7ogcDyTNLhbDyXtKbS2AbcVD4KmQRblMkTmZsYTFlVMXcJWT26YoCn8qGguZPu322ERtRalcUl v1aP4acXTvXxWvbiXvy0z1f71H6+oyY6l0mKbh9lbFt+DzKilUlyy7eu7L8SfTjAbB4aV+yHYE/ l+kz3SU750fCYbaVv4tiShMDAzcAEIynbh23BOa3hKkyc6xUQ50zBUkV8fW4x+EOJ0p293ThUf1 2N0EQGKSfF3BqnfjDwk83dh6LgnwfBIXP4UmCR9DUnsZFauZP0vGHdUdunxYLb1AIF7VNHt2Cyd UzZMKjTNBZdimvD2nxn/KiCXjqQHnSZbMQ/foV4IgUNGgQgAZzZS6qa4kFC6Ofm3KHdcJZpLDnT IqeYdfqNnD8rk2IMuoQzk8= X-Received: by 2002:a05:690c:3705:b0:80d:66b2:850 with SMTP id 00721157ae682-81fcc1238e6mr120377b3.42.1785464484423; Thu, 30 Jul 2026 19:21:24 -0700 (PDT) Received: from zenbox ([2600:1700:18fb:6011:e94:8a83:feea:6720]) by smtp.gmail.com with ESMTPSA id 00721157ae682-81fb8b26e8bsm20519047b3.47.2026.07.30.19.21.23 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 30 Jul 2026 19:21:24 -0700 (PDT) From: Justin Suess To: ast@kernel.org, daniel@iogearbox.net, andrii@kernel.org, kpsingh@kernel.org, paul@paul-moore.com, mic@digikod.net, viro@zeniv.linux.org.uk, brauner@kernel.org, kees@kernel.org Cc: gnoack@google.com, jack@suse.cz, song@kernel.org, yonghong.song@linux.dev, martin.lau@linux.dev, m@maowtm.org, bpf@vger.kernel.org, linux-security-module@vger.kernel.org, linux-kernel@vger.kernel.org, Justin Suess Subject: [PATCH bpf-next 08/13] bpf: Add the bpf_landlock_put_ruleset kfunc and ruleset destructor Date: Thu, 30 Jul 2026 22:20:41 -0400 Message-ID: <20260731022047.189137-9-utilityemal77@gmail.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260731022047.189137-1-utilityemal77@gmail.com> References: <20260731022047.189137-1-utilityemal77@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add the release kfunc for Landlock ruleset references: bpf_landlock_put_ruleset(ruleset) KF_RELEASE It is a thin front end to security_policy_kptr_put(), invoked with LSM_ID_LANDLOCK; the handle travels in the Landlock member of union lsm_policy_kptr, staying typed end to end. A ruleset reference is meant to be handed over through a map kptr field, so also register a destructor for struct bpf_landlock_ruleset: map-held references are dropped on map teardown. The release path may thus run from a context that cannot sleep, which the policy_kptr_put() hook contract requires implementations to support. The release kfunc is available to both program types the kfunc set is registered for. For BPF_PROG_TYPE_LSM, the filter only accepts programs attached to the bprm_creds_for_exec() or bprm_creds_from_file() hooks, where the upcoming enforcement kfunc is specified to operate, and rejects BPF_LSM_CGROUP programs, which run under classic RCU; KF_SLEEPABLE limits the callers to sleepable programs. Signed-off-by: Justin Suess --- kernel/bpf/bpf_lsm.c | 67 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 66 insertions(+), 1 deletion(-) diff --git a/kernel/bpf/bpf_lsm.c b/kernel/bpf/bpf_lsm.c index dd58c5bd0119..877dd0352607 100644 --- a/kernel/bpf/bpf_lsm.c +++ b/kernel/bpf/bpf_lsm.c @@ -14,8 +14,10 @@ #include #include #include +#include #include #include +#include =20 /* For every LSM hook that allows attachment of BPF programs, declare a nop * function where a BPF program can be attached. Notably, we qualify each = with @@ -481,9 +483,49 @@ int bpf_lsm_get_retval_range(const struct bpf_prog *pr= og, */ struct bpf_landlock_ruleset {}; =20 +/* + * The sleepable LSM hooks bpf_landlock_put_ruleset() may be called + * from. + */ +BTF_SET_START(bpf_landlock_kfunc_hooks) +BTF_ID(func, bpf_lsm_bprm_creds_for_exec) +BTF_ID(func, bpf_lsm_bprm_creds_from_file) +BTF_SET_END(bpf_landlock_kfunc_hooks) + +__bpf_kfunc_start_defs(); + +/** + * bpf_landlock_put_ruleset - Put a Landlock ruleset + * @ruleset: Landlock ruleset to put + * + * Release an acquired reference on a Landlock ruleset. + */ +__bpf_kfunc void bpf_landlock_put_ruleset(struct bpf_landlock_ruleset *rul= eset) +{ + union lsm_policy_kptr policy =3D { .landlock.ruleset =3D ruleset }; + + security_policy_kptr_put(LSM_ID_LANDLOCK, &policy); +} + +/* Destructor for referenced bpf_landlock_ruleset kptrs. */ +__bpf_kfunc void bpf_landlock_put_ruleset_dtor(void *ruleset) +{ + union lsm_policy_kptr policy =3D { .landlock.ruleset =3D ruleset }; + + security_policy_kptr_put(LSM_ID_LANDLOCK, &policy); +} +CFI_NOSEAL(bpf_landlock_put_ruleset_dtor); + +__bpf_kfunc_end_defs(); + BTF_KFUNCS_START(bpf_landlock_kfunc_ids) +BTF_ID_FLAGS(func, bpf_landlock_put_ruleset, KF_RELEASE | KF_SLEEPABLE) BTF_KFUNCS_END(bpf_landlock_kfunc_ids) =20 +BTF_ID_LIST(bpf_landlock_dtor_ids) +BTF_ID(struct, bpf_landlock_ruleset) +BTF_ID(func, bpf_landlock_put_ruleset_dtor) + /* * BPF_PROG_TYPE_LSM and BPF_PROG_TYPE_SYSCALL share their kfunc * lookup buckets with other program types, so restricting the LSM @@ -498,6 +540,17 @@ static int bpf_landlock_kfunc_filter(const struct bpf_= prog *prog, u32 kfunc_id) case BPF_PROG_TYPE_SYSCALL: return 0; case BPF_PROG_TYPE_LSM: + /* + * BPF_LSM_CGROUP programs run under classic RCU and + * cannot sleep. + */ + if (prog->expected_attach_type =3D=3D BPF_LSM_CGROUP) + return -EACCES; + + if (!btf_id_set_contains(&bpf_landlock_kfunc_hooks, + prog->aux->attach_btf_id)) + return -EACCES; + return 0; default: return -EACCES; @@ -512,6 +565,12 @@ static const struct btf_kfunc_id_set bpf_landlock_kfun= c_set =3D { =20 static int __init bpf_lsm_policy_kfunc_init(void) { + const struct btf_id_dtor_kfunc bpf_landlock_dtors[] =3D { + { + .btf_id =3D bpf_landlock_dtor_ids[0], + .kfunc_btf_id =3D bpf_landlock_dtor_ids[1], + }, + }; int ret; =20 ret =3D register_btf_kfunc_id_set(BPF_PROG_TYPE_LSM, @@ -519,7 +578,13 @@ static int __init bpf_lsm_policy_kfunc_init(void) if (ret) return ret; =20 - return register_btf_kfunc_id_set(BPF_PROG_TYPE_SYSCALL, + ret =3D register_btf_kfunc_id_set(BPF_PROG_TYPE_SYSCALL, &bpf_landlock_kfunc_set); + if (ret) + return ret; + + return register_btf_id_dtor_kfuncs(bpf_landlock_dtors, + ARRAY_SIZE(bpf_landlock_dtors), + THIS_MODULE); } late_initcall(bpf_lsm_policy_kfunc_init); --=20 2.54.0 From nobody Fri Oct 2 14:03:02 2026 Received: from mail-yx1-f41.google.com (mail-yx1-f41.google.com [74.125.224.41]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E5ACD339368 for ; Fri, 31 Jul 2026 02:21:26 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.224.41 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785464489; cv=none; b=kn0+guf6ONAyk7Q6gqEVJm+BAfLquEuvmIIK+nvFK7J/7mpzC6Ji7VTcF9uuMSLV04sY1OyS1ZFAWtbe3cCrIpa5GUQgWLU1QFIqy/wDE81UCyyB3fzqcjzBlJhbi4DmM4eEeummu0OXoLD8nJKxe0se7sOHK0K4KFGtfVyIdSI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785464489; c=relaxed/simple; bh=/VRKhMz6MNHP5GXT2Zjzxq2uq/8Y/syPrzNmmZIe/iM=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=TyymM9DtnjLR2+aca2RTWBHa2X8Te50c9lglieKcfFMlRWx7v4kP1ysFg18wBUk/V/YU2RP30zx5qtkM61+4RoLTEyT+cYetVVmpdjy4dK7VgxEpT0GnZMljK+pl2AgiIOC+oEDZXvWBBjXZyxT4nLCVVXPi8IGE1JSaCIc2bH0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=bDs3IgQM; arc=none smtp.client-ip=74.125.224.41 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="bDs3IgQM" Received: by mail-yx1-f41.google.com with SMTP id 956f58d0204a3-6688a2dceb8so264201d50.3 for ; Thu, 30 Jul 2026 19:21:26 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1785464486; x=1786069286; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=i0Bw5xZyWZrxJDT+Nb2bq5E0YUztUMCAvYE7Jg2F4Y4=; b=bDs3IgQMr+k9uIE7pAkvI/dggadTInDnK4023pc8ud5wSefpDJDH2cT//JKxRZxFbQ 8wu6SZ63N0cp2nhvUsSiFH3eb493u28XPygHJ/pKfKB/ez6id70uYPpTuIXyNwx4jVyU nCzU8eZuOu2Ri6yeZXN4wBdW7ealf8NT/PkepSYEfXD75f0ALxh1wieFSD0uGulbYKVD M3k0R+vOOHjQ4ey8ZEj21pY8phEnhy1Ja1nsyDVBm4QPYrxNjxVkcpXDCTBj+DORBsyQ 9ncvDqEswZ1VjCuRC93wHI5uBnI43JJb5XJYaIZl5S8hwpSLmwa1q3XzCmfEJ0kF6Ta4 cWIw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1785464486; x=1786069286; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=i0Bw5xZyWZrxJDT+Nb2bq5E0YUztUMCAvYE7Jg2F4Y4=; b=SdgJJrBgrQdV2hsVj0Xev2YyjFHyM1SzDZv7U09eJu3Cm5Xm6XZtSvM11B2h98oMSN gOGRvipTJYyOsQ/CY72KwpDZu9i5sS26l7zt8ccRfoIkOWNIfeQ2UxWYjo+OgrzJkT+2 /t2rWETcYs+fGvArfIBhhgfdFOLVY9ulbsUtd4tn0A95QOn1HHlGrZ8qadvul3Llh/iS qCXuLsRq6QAyhm6rsaLBmjBz1okN5GUPS6H167tOSFbxQeJXHYuuhcDg/2zmUdehvFyP VDkq4JZm3/1RO5SD/j8mSx+R+QfNst5k98ed6Iy23MqsciNAdWEHuOqFxmxpFnEVGBuy NZgA== X-Forwarded-Encrypted: i=1; AHgh+RpipAp1nvh7RDYL2Rc0OBxjwSQbgTOCubYeqX59x1lLnR0NsQGjl6dXm3vWa3YKxaoyw1KyGwRfvy5HSuQ=@vger.kernel.org X-Gm-Message-State: AOJu0YxQF5jFFa0RJWMMUXq94b7BNj7ljuY5kMzyZAXOEqjOaplV9L0n QJmwxVkb/WXAtGMEUnK4FtaO8ND7FDb7KBXCn3pRDtElXMSNky01ggvM X-Gm-Gg: AR+sD10Guix+WoNNaMXTiReyByg/8UqgKSYocDynDOjShDxYGn6xGVWv4PK3gd2NTU5 usdbMUbfZqPBpgve73MVXNsBohYFhXRgpSOQ5OCMJSSakpps/BIXZMa1QIA5MPI/SmMDuu6FzCd ZN67bL6zOCHtkvM5FxD77NJRS+uJUZepl118LzKH7aBj+yxXN7CdrrQWcezDrktIbL/SjTXmlXq HIwRkWE+xE9y+q3LnUk7FvOYKZH/fYL+zJUoryRTGHAnXE8Pww7Lve5eAhO8//YwXUPXPfc8QSv JA+5Qu6aKZ1CoC1sIc6bAM2eUvA9nnKER95xDil0z1j8f0e53aTk7RAl6Id+SyH5HalesUw09ur 6gmdydUz/x9c9kbCMPr5QLZZ3RCMMl0uEPg1CZZji8rN2i9z/2xzfsD8GM/oi2smZ/Iz7g0lRs1 H0vAdAxcsp3bG+Dx6Ahi7rVGn5YxY2785yh44DYj5tBJi9oxV2utANFMtkoW63k7qfT6OsO45KM ZX+gfPUk4ERyj1OnHyRiQ== X-Received: by 2002:a05:690c:3685:b0:80f:ddab:1cca with SMTP id 00721157ae682-81fcbbde12bmr429757b3.35.1785464485830; Thu, 30 Jul 2026 19:21:25 -0700 (PDT) Received: from zenbox ([2600:1700:18fb:6011:e94:8a83:feea:6720]) by smtp.gmail.com with ESMTPSA id 00721157ae682-81fb8b26e8bsm20519047b3.47.2026.07.30.19.21.25 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 30 Jul 2026 19:21:25 -0700 (PDT) From: Justin Suess To: ast@kernel.org, daniel@iogearbox.net, andrii@kernel.org, kpsingh@kernel.org, paul@paul-moore.com, mic@digikod.net, viro@zeniv.linux.org.uk, brauner@kernel.org, kees@kernel.org Cc: gnoack@google.com, jack@suse.cz, song@kernel.org, yonghong.song@linux.dev, martin.lau@linux.dev, m@maowtm.org, bpf@vger.kernel.org, linux-security-module@vger.kernel.org, linux-kernel@vger.kernel.org, Justin Suess Subject: [PATCH bpf-next 09/13] bpf: Add the bpf_landlock_get_ruleset_from_fd kfunc Date: Thu, 30 Jul 2026 22:20:42 -0400 Message-ID: <20260731022047.189137-10-utilityemal77@gmail.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260731022047.189137-1-utilityemal77@gmail.com> References: <20260731022047.189137-1-utilityemal77@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add the acquire kfunc for Landlock rulesets: bpf_landlock_get_ruleset_from_fd(fd) KF_ACQUIRE|KF_RET_NULL It acquires a reference on the Landlock ruleset referred to by @fd, as created by landlock_create_ruleset(2) and populated with landlock_add_rule(2), through security_policy_kptr_from_fd() invoked with LSM_ID_LANDLOCK. When Landlock is compiled out or not enabled in the LSM order, the call returns NULL. A ruleset fd is only meaningful in the fd table of the process that set the ruleset up, while an LSM program runs in the context of the task it mediates, so the filter makes this kfunc exclusive to syscall programs (BPF_PROG_TYPE_SYSCALL), which run in the context of the task invoking them. The acquired ruleset is meant to be handed over through a map kptr field to an enforcement program, and must be released with bpf_landlock_put_ruleset(). Signed-off-by: Justin Suess --- kernel/bpf/bpf_lsm.c | 46 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/kernel/bpf/bpf_lsm.c b/kernel/bpf/bpf_lsm.c index 877dd0352607..9ff1c35fcd6e 100644 --- a/kernel/bpf/bpf_lsm.c +++ b/kernel/bpf/bpf_lsm.c @@ -479,7 +479,9 @@ int bpf_lsm_get_retval_range(const struct bpf_prog *pro= g, /* LSM policy kfuncs */ =20 /* - * Opaque handle for a Landlock ruleset. Only Landlock resolves it. + * Opaque handle for a Landlock ruleset. Only + * bpf_landlock_get_ruleset_from_fd() produces one, and only Landlock + * resolves it. */ struct bpf_landlock_ruleset {}; =20 @@ -494,11 +496,37 @@ BTF_SET_END(bpf_landlock_kfunc_hooks) =20 __bpf_kfunc_start_defs(); =20 +/** + * bpf_landlock_get_ruleset_from_fd - Get a Landlock ruleset from a fd + * @fd: file descriptor of a Landlock ruleset, resolved in the file + * descriptor table of the task running the program + * + * Acquire a reference on the Landlock ruleset referred to by @fd, as + * created by landlock_create_ruleset(2) and populated with + * landlock_add_rule(2). Only syscall programs may call this kfunc: + * they run in the context of the task invoking them, where the + * ruleset fd is meaningful. The acquired ruleset can be handed to an + * enforcement program through a map kptr field. The reference must + * be released with bpf_landlock_put_ruleset(). + * + * Return: A referenced ruleset handle, or NULL if @fd is not a + * readable Landlock ruleset fd or the Landlock LSM is not enabled. + */ +__bpf_kfunc struct bpf_landlock_ruleset * +bpf_landlock_get_ruleset_from_fd(int fd) +{ + union lsm_policy_kptr policy; + + if (security_policy_kptr_from_fd(LSM_ID_LANDLOCK, fd, &policy)) + return NULL; + return policy.landlock.ruleset; +} + /** * bpf_landlock_put_ruleset - Put a Landlock ruleset * @ruleset: Landlock ruleset to put * - * Release an acquired reference on a Landlock ruleset. + * Release a reference acquired with bpf_landlock_get_ruleset_from_fd(). */ __bpf_kfunc void bpf_landlock_put_ruleset(struct bpf_landlock_ruleset *rul= eset) { @@ -519,6 +547,8 @@ CFI_NOSEAL(bpf_landlock_put_ruleset_dtor); __bpf_kfunc_end_defs(); =20 BTF_KFUNCS_START(bpf_landlock_kfunc_ids) +BTF_ID_FLAGS(func, bpf_landlock_get_ruleset_from_fd, + KF_ACQUIRE | KF_RET_NULL | KF_SLEEPABLE) BTF_ID_FLAGS(func, bpf_landlock_put_ruleset, KF_RELEASE | KF_SLEEPABLE) BTF_KFUNCS_END(bpf_landlock_kfunc_ids) =20 @@ -526,10 +556,17 @@ BTF_ID_LIST(bpf_landlock_dtor_ids) BTF_ID(struct, bpf_landlock_ruleset) BTF_ID(func, bpf_landlock_put_ruleset_dtor) =20 +BTF_ID_LIST_SINGLE(bpf_landlock_get_ruleset_ids, func, + bpf_landlock_get_ruleset_from_fd) + /* * BPF_PROG_TYPE_LSM and BPF_PROG_TYPE_SYSCALL share their kfunc * lookup buckets with other program types, so restricting the LSM - * policy kfuncs requires a filter. + * policy kfuncs requires a filter. A ruleset fd is only meaningful + * in the fd table of the task that set the ruleset up, so + * bpf_landlock_get_ruleset_from_fd() is exclusive to syscall + * programs, which run in that task's context; an LSM program runs in + * the context of the task it mediates. */ static int bpf_landlock_kfunc_filter(const struct bpf_prog *prog, u32 kfun= c_id) { @@ -540,6 +577,9 @@ static int bpf_landlock_kfunc_filter(const struct bpf_p= rog *prog, u32 kfunc_id) case BPF_PROG_TYPE_SYSCALL: return 0; case BPF_PROG_TYPE_LSM: + if (kfunc_id =3D=3D bpf_landlock_get_ruleset_ids[0]) + return -EACCES; + /* * BPF_LSM_CGROUP programs run under classic RCU and * cannot sleep. --=20 2.54.0 From nobody Fri Oct 2 14:03:02 2026 Received: from mail-yw1-f174.google.com (mail-yw1-f174.google.com [209.85.128.174]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 839873264D8 for ; Fri, 31 Jul 2026 02:21:28 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.174 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785464490; cv=none; b=iz6Psitijvx0YwxjQ8GwU7seyXD08ZaP9Cxc/yFlO7p/Dci5eNprhZhWYa93IuGkB6QY5SC5KRh+XNWvAyEdCsjwfvKYgKK80g0YyzBe21srd2PyEsxThAOqoW0Cunw5napyTPLRvBqdsTQc1zPhkmS2z9eyzgZY+a16hlSbBUM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785464490; c=relaxed/simple; bh=8CZuZUF4yZqOhhlu7a9VfiLoBIOnNth3ho1a5v4N4nI=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=NcqHAbg2rTm62mWZxwaTpb0T02MkgBZ15lXBHi1+8oz4yJDGSO/B2vfizQI/uTTXy0/p1zUH8D0y28woZXGF76WKEXnhtzgUfeGqQMF6jMGi4qgo5yfeOR8CT1sCxPLVXiT0J/VujDozIro7vIPNuq2ldmIepE1m3JsdCzjUC/Q= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=j0+zv721; arc=none smtp.client-ip=209.85.128.174 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="j0+zv721" Received: by mail-yw1-f174.google.com with SMTP id 00721157ae682-81e9f7491ffso7837557b3.0 for ; Thu, 30 Jul 2026 19:21:28 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1785464487; x=1786069287; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=pGCBiJwuFidm0XWVpVouPl8UKmEaO4aYAkoooI4m9uE=; b=j0+zv721AWeSEmTALkJ0kPOdOHyKhV9W34sTdS5GsT1Vgruk7Hz3ys5tFci0qYH0P9 P36SFeQ1jxomG/Dvb2D81Lr+YLUh9oMO17ddIFKjH+0R5mRT5yQRKhtbQahodkXlU3BT KVXiulkhC7woioK3whqqHsoGSnquIppOYbHvyVekBp8fCzUhNc078XoX0ZjbCVEmtb2C J7l2S1pIuUFF/uMrLV5vWPf8gGCsjU/UQ8PpMAYMriqRQfMQza+V1c/LdsSo9AGDMyMX zz84w6gcjZ96Rk5wxvqyzl3IoKC2mxl6QiT8yCqBXMjojEtYTh9O7kC+5gKFFTj6HJXZ 0kAA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1785464487; x=1786069287; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=pGCBiJwuFidm0XWVpVouPl8UKmEaO4aYAkoooI4m9uE=; b=LIWXfOu/RLPeh7pn+IUNdLrzQiC5ZEQk4zEMH8htmYWyoSUMIOzujbePZMbsDGYieK z8kunLMgwqz49WAk1VyfzSKs2JVPRbQuUZbJIxYX/rGPtqlj9u5zfP6cdKrX7sAZ+6lp k7fHLzSS4rMA0YftyQVwsujsNBa9mDCHEMjN7gMc/DsIjIxtQX1b1F/Aw17bunYn5ORE 0t/PGLnmL9UA7zUsvwAf9X+l4YQx4D15+kpx7anHH6mN8htbaDpGRjrlXRe0M3SD2SHS hY4W/hqmXsxuyWZBHBuSS4E9NRWH0i/DQQmBciWPHKggqWnnn+JSgzF+Feu1rS1sgbXT ZeBQ== X-Forwarded-Encrypted: i=1; AHgh+Rravua7zc4fHAdX1U++7rEBJoXj8Zn5K9MKz/o0OQR26Du/MjnE4RTxKJ2leAUyDimXZbR1PL1yH7+ZjFk=@vger.kernel.org X-Gm-Message-State: AOJu0YwDcl7qkwbaUPcDSNmHiZAc0zteC9ijCnhdH2EQKCWWbWec30zG fmhwydamZUfnxNso6VYoDDIZkQu3W8BMmV3VJqup43HckaZfwDO3gdh+ X-Gm-Gg: AR+sD13sl5ZXMoWS8Iy1WHFi8uXSg+8L3diTPkp1tY04P5s0oFQxOXkNhquJpO+eGdQ wm55xBfpJdjyV2LyBC8K+k8P1Z2SDxicaHWt2cXWUMcxS1Qjw5DsZSaNryRazKN0n9oJlLMg/kD lL5jZGRtmTwv4VjMT6xNN/XtIGWVxGn3cRl7fwv16KGNgLdQ4nLWHEPCfUJtQmuHWoiKj2Xw6ED JMT8oy55/zwUiI6+vISvyVKOyJY6EjLNF/oBRpIUQGsoKJhEXaoj6lXZb66e1oOKuQkp58vECRg IjiKzh7bI7gi7dm/DldCf8aq3zVuoxHR/lkhszMmY3OtTEzOZx2Lhxg14JJZ/wzLDTmHtfyFPvf ftzYnfRuIn0bvKDkhCj9l1wZsrKLdnvz8IaLRfUiP5k6w7JrrJRTsQplUsGkn5XRm/swNZ+Mm7A VJndux2zn+sM5u2Vh0juf9VG1yqDqhEQNJ5Hrs/rPtEnNSSt3PQRxaGoFoO2ulHy8WNULpKkGwr xHwUmMR3GLZ4ugtTBX5CA== X-Received: by 2002:a05:690c:c02:b0:81e:a425:fe7d with SMTP id 00721157ae682-81fcb9cdbc1mr766227b3.28.1785464487279; Thu, 30 Jul 2026 19:21:27 -0700 (PDT) Received: from zenbox ([2600:1700:18fb:6011:e94:8a83:feea:6720]) by smtp.gmail.com with ESMTPSA id 00721157ae682-81fb8b26e8bsm20519047b3.47.2026.07.30.19.21.26 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 30 Jul 2026 19:21:27 -0700 (PDT) From: Justin Suess To: ast@kernel.org, daniel@iogearbox.net, andrii@kernel.org, kpsingh@kernel.org, paul@paul-moore.com, mic@digikod.net, viro@zeniv.linux.org.uk, brauner@kernel.org, kees@kernel.org Cc: gnoack@google.com, jack@suse.cz, song@kernel.org, yonghong.song@linux.dev, martin.lau@linux.dev, m@maowtm.org, bpf@vger.kernel.org, linux-security-module@vger.kernel.org, linux-kernel@vger.kernel.org, Justin Suess Subject: [PATCH bpf-next 10/13] bpf: Add the bpf_landlock_restrict_binprm kfunc Date: Thu, 30 Jul 2026 22:20:43 -0400 Message-ID: <20260731022047.189137-11-utilityemal77@gmail.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260731022047.189137-1-utilityemal77@gmail.com> References: <20260731022047.189137-1-utilityemal77@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add the enforcement kfunc for Landlock rulesets: bpf_landlock_restrict_binprm(bprm, ruleset, flags) It restricts the credentials prepared in @bprm with @ruleset, so that the executed task starts confined by it, through security_bprm_enforce_policy_kptr() invoked with LSM_ID_LANDLOCK. When Landlock is compiled out or not enabled in the LSM order, the call fails with -EOPNOTSUPP. The kfunc keeps the BPF-facing interface strongly BTF-typed: with kfunc arguments trusted by default, both the binprm (from the LSM hook context) and the ruleset (an acquired reference) must be trusted pointers whose provenance the verifier guarantees, not merely type-matching ones. The flags take the landlock_restrict_self(2) flags with their usual semantics. Only LANDLOCK_RESTRICT_SELF_TSYNC is rejected (-EINVAL), as it targets the calling threads rather than the execution. The filter makes enforcement exclusive to the sleepable LSM programs attached to the bprm_creds_for_exec() or bprm_creds_from_file() hooks. Signed-off-by: Justin Suess --- kernel/bpf/bpf_lsm.c | 50 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 47 insertions(+), 3 deletions(-) diff --git a/kernel/bpf/bpf_lsm.c b/kernel/bpf/bpf_lsm.c index 9ff1c35fcd6e..67aa902f1257 100644 --- a/kernel/bpf/bpf_lsm.c +++ b/kernel/bpf/bpf_lsm.c @@ -486,8 +486,8 @@ int bpf_lsm_get_retval_range(const struct bpf_prog *pro= g, struct bpf_landlock_ruleset {}; =20 /* - * The sleepable LSM hooks bpf_landlock_put_ruleset() may be called - * from. + * The sleepable LSM hooks bpf_landlock_restrict_binprm() and + * bpf_landlock_put_ruleset() may be called from. */ BTF_SET_START(bpf_landlock_kfunc_hooks) BTF_ID(func, bpf_lsm_bprm_creds_for_exec) @@ -535,6 +535,42 @@ __bpf_kfunc void bpf_landlock_put_ruleset(struct bpf_l= andlock_ruleset *ruleset) security_policy_kptr_put(LSM_ID_LANDLOCK, &policy); } =20 +/** + * bpf_landlock_restrict_binprm - Enforce a Landlock ruleset on exec + * credentials + * @bprm: execution context providing the prepared credentials to + * restrict + * @ruleset: Landlock ruleset to enforce + * @flags: landlock_restrict_self(2) flags, except + * %LANDLOCK_RESTRICT_SELF_TSYNC + * + * Restrict the credentials prepared in @bprm with @ruleset, so that + * the executed task starts confined by it, following the same domain + * composition rules as landlock_restrict_self(2). The restriction is + * staged and only committed at the execution's point of no return: an + * execution that fails before that point is unaffected, and a later + * call on the same execution replaces a previously staged + * restriction. @ruleset is only borrowed: the caller keeps its + * reference. + * + * The landlock_restrict_self(2) flags apply with their usual + * semantics. Only %LANDLOCK_RESTRICT_SELF_TSYNC is rejected, as it + * targets the calling threads rather than the execution. + * + * Return: 0 on success, -EOPNOTSUPP if the Landlock LSM is not + * enabled, -EINVAL if @flags contains an unsupported flag, other + * negative values on failure as for landlock_restrict_self(2). + */ +__bpf_kfunc int bpf_landlock_restrict_binprm(struct linux_binprm *bprm, + struct bpf_landlock_ruleset *ruleset, + u32 flags) +{ + union lsm_policy_kptr policy =3D { .landlock.ruleset =3D ruleset }; + + return security_bprm_enforce_policy_kptr(LSM_ID_LANDLOCK, bprm, + &policy, flags); +} + /* Destructor for referenced bpf_landlock_ruleset kptrs. */ __bpf_kfunc void bpf_landlock_put_ruleset_dtor(void *ruleset) { @@ -550,6 +586,7 @@ BTF_KFUNCS_START(bpf_landlock_kfunc_ids) BTF_ID_FLAGS(func, bpf_landlock_get_ruleset_from_fd, KF_ACQUIRE | KF_RET_NULL | KF_SLEEPABLE) BTF_ID_FLAGS(func, bpf_landlock_put_ruleset, KF_RELEASE | KF_SLEEPABLE) +BTF_ID_FLAGS(func, bpf_landlock_restrict_binprm, KF_SLEEPABLE) BTF_KFUNCS_END(bpf_landlock_kfunc_ids) =20 BTF_ID_LIST(bpf_landlock_dtor_ids) @@ -558,6 +595,8 @@ BTF_ID(func, bpf_landlock_put_ruleset_dtor) =20 BTF_ID_LIST_SINGLE(bpf_landlock_get_ruleset_ids, func, bpf_landlock_get_ruleset_from_fd) +BTF_ID_LIST_SINGLE(bpf_landlock_restrict_binprm_ids, func, + bpf_landlock_restrict_binprm) =20 /* * BPF_PROG_TYPE_LSM and BPF_PROG_TYPE_SYSCALL share their kfunc @@ -566,7 +605,10 @@ BTF_ID_LIST_SINGLE(bpf_landlock_get_ruleset_ids, func, * in the fd table of the task that set the ruleset up, so * bpf_landlock_get_ruleset_from_fd() is exclusive to syscall * programs, which run in that task's context; an LSM program runs in - * the context of the task it mediates. + * the context of the task it mediates. Enforcement is exclusive to + * the sleepable bprm LSM hooks the policy operation is specified + * for, and the release kfunc is allowed wherever a reference can be + * held. */ static int bpf_landlock_kfunc_filter(const struct bpf_prog *prog, u32 kfun= c_id) { @@ -575,6 +617,8 @@ static int bpf_landlock_kfunc_filter(const struct bpf_p= rog *prog, u32 kfunc_id) =20 switch (prog->type) { case BPF_PROG_TYPE_SYSCALL: + if (kfunc_id =3D=3D bpf_landlock_restrict_binprm_ids[0]) + return -EACCES; return 0; case BPF_PROG_TYPE_LSM: if (kfunc_id =3D=3D bpf_landlock_get_ruleset_ids[0]) --=20 2.54.0 From nobody Fri Oct 2 14:03:02 2026 Received: from mail-yx1-f45.google.com (mail-yx1-f45.google.com [74.125.224.45]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9173C33FE0A for ; Fri, 31 Jul 2026 02:21:30 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.224.45 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785464493; cv=none; b=csIqT5Qpt+opDplD9dKuofeoybggGsyyAR7wluzBybPUjIgxBkSHmvLkdx0G4KKasCj8c2I0esxI4kIeGckOh+bCojuz9tICQrQMXWYuxnQDiVjz4X9EC2vDWQjdfJkXxLZtcpOrPEIT+8yBkhucbfMyxkW7uugLfMKtrmij/5M= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785464493; c=relaxed/simple; bh=79WDRkozdtPmwEzHohevZNQHC9JEcAYV/9wC5vE8rxs=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=ZO4PI7Y22ybBMybhXqi8pLMRvzN3iMlSBnP7BPKLmTv6HeJ1Idw+Wi81Fn3zD3LcxchFP3FHbIOv7T7gF1RELNiJvpXYFgeycE41Y6AJBQIZHuH083fmD44kWi9HkBTLb8DZ3ZEDKDZ4mlxiq8ZmqUUTWEIjN4bSaDNiJX5eTgU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=WupicYBQ; arc=none smtp.client-ip=74.125.224.45 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="WupicYBQ" Received: by mail-yx1-f45.google.com with SMTP id 956f58d0204a3-6681e7911b0so551187d50.0 for ; Thu, 30 Jul 2026 19:21:30 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1785464489; x=1786069289; darn=vger.kernel.org; h=content-transfer-encoding:content-type:mime-version:references :in-reply-to:message-id:date:subject:cc:to:from:from:to:cc:subject :date:message-id:reply-to:content-type; bh=gwkoVULYCNdZKy6/P+qRyiCnYdc6+SQZGzQ9Pl5KsVU=; b=WupicYBQR+MFEssGq+5oDBuybXWThbZGLiMqb8xJsbxlvzlWoyYAsFj+WsgV7pArDJ L1YLlSEmO4503spnE6luy395kkPhjlN0IyNCjecQHlZmjBMmLH7sh4XhrRblABaAJHJ7 qUcbjy/x0VFE6DrwSOlpMVIKxp0FZKK+5eu+pm0NYD8I1XqPkeiknZ96O4DM0E1xJ618 sRF9sbjufcJirNGlk9ioCHtpzoWc0wVUveg1nfJgaplnTEJiKOdhvDT0kYIk1x4TcjBl 2MFAi+7zqHIPxLC3J5VvoMWjltaQ9MaYIEWs5M3tx1QU19as80grBjX4hnt5eaxRKBkY yw7g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1785464489; x=1786069289; h=content-transfer-encoding:content-type:mime-version:references :in-reply-to:message-id:date:subject:cc:to:from:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=gwkoVULYCNdZKy6/P+qRyiCnYdc6+SQZGzQ9Pl5KsVU=; b=ArL9Icyr4bm0bzHKOCrVmOC5Ufk6DiYnR0vF8EMmGNXQtTzK/5d8BJKuGBmMzpCHW/ 4hrVXN+95DMnWmBKo4XV3rAvL8aLyFQaFuvZ3XVorP4qpJFWxg8JZb2b0mCoC/wXVpiT juGYmdFx9xy/bSEesy0z3nCvw6acJH+XtN/c2xWMXMW+0xi4fthBiHGNonlOXJ6SiRJr 2i14OR8MSmqvs3h8a0987oAGTX2qVVvwShRkhQUY0EX76RVsGrOvzRsYxUeaYXJXJ4rS /IWvETEWJVhqI2eQr6EdFxCLDuOVvL/v68Il3Se7/usiRDiti3cvP3A0H7p/YnSs/qkc UieA== X-Forwarded-Encrypted: i=1; AHgh+Rp5qZga64dZbfBtTi6asPCglCGXGA7+azJL3/wWTfw8GmLxeKSt4v2Ifu22XS/eXN+vmDsjF/coF5SC6XQ=@vger.kernel.org X-Gm-Message-State: AOJu0YzFicXfDK8Jl6bMr6zYis7TMxEGhKs1KAddwXFoPPbA2jQ40gbS bIVbjLJjK/MA776yptVicqI5u3NFJWiRNl82Jk21wFWwGoN5bnStIAka X-Gm-Gg: AR+sD13WJNFwOEGpuQJPYdrvofwK9wkkBAv+8kPlK6H6p7IXTowr/eZp4eZ9rNFzUF0 pEmkbTGBpYAmFbjgO27PcqC/a/LyGIBsulNtovh2MUSh3M5wwMuqupGwkXxRr6aE+Se0xfR8CBq aYg8O4hB/m71yL12iA9hm9CdKt+EeBRGvDPK4vU9lBKcYwspfAkE6IfBZbaL1dOXNJOAbgLEhr1 oxKuQvuu/kHUCvaP6m3mYrwQEHR8Ac6ITUmlXJTD6PgkNUasTgLAfV8eyld6pV6vZ/NMeuc6VuC kUfXQrfPPltB7UJK1p/1zwg9URWkC+LXkoJOM1AzTzza5B82cpno2DqD3bS7c4tyjKHlgWaLUwE o3YuFSiIyKi+0qBbVGt9YMHlXd5alk6CMpnSY6mHDWwqQKTICLUpYaad5HU2wQ9lHclRyvIxEH4 ntGcpxc4giTaTqNftEJMQOVHTyhmUOYQPiPWws1ToWAAUgIYA/y0yGODbYg3lyRgYNgjjFSxyaW X2QFbaY5rnS8RqKQHOvGA== X-Received: by 2002:a05:690c:64c1:b0:7ff:1399:9317 with SMTP id 00721157ae682-81fcb970fbbmr616447b3.11.1785464489235; Thu, 30 Jul 2026 19:21:29 -0700 (PDT) Received: from zenbox ([2600:1700:18fb:6011:e94:8a83:feea:6720]) by smtp.gmail.com with ESMTPSA id 00721157ae682-81fb8b26e8bsm20519047b3.47.2026.07.30.19.21.28 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 30 Jul 2026 19:21:28 -0700 (PDT) From: Justin Suess To: ast@kernel.org, daniel@iogearbox.net, andrii@kernel.org, kpsingh@kernel.org, paul@paul-moore.com, mic@digikod.net, viro@zeniv.linux.org.uk, brauner@kernel.org, kees@kernel.org Cc: gnoack@google.com, jack@suse.cz, song@kernel.org, yonghong.song@linux.dev, martin.lau@linux.dev, m@maowtm.org, bpf@vger.kernel.org, linux-security-module@vger.kernel.org, linux-kernel@vger.kernel.org, Justin Suess Subject: [PATCH bpf-next 11/13] selftests/bpf: Add tests for the Landlock policy kfuncs Date: Thu, 30 Jul 2026 22:20:44 -0400 Message-ID: <20260731022047.189137-12-utilityemal77@gmail.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260731022047.189137-1-utilityemal77@gmail.com> References: <20260731022047.189137-1-utilityemal77@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Exercise the LSM policy kfuncs end to end and cover the verifier-time restrictions enforced by the BPF-side kfunc filter. The success flow mirrors the intended deployment: a syscall program, run by the test runner through BPF_PROG_RUN, acquires the ruleset created by the runner from its fd (resolved in the runner's own fd table) and parks it in a map kptr slot; a program attached to the sleepable bprm_creds_for_exec() hook takes it from the map, enforces it on the monitored execution with bpf_landlock_restrict_binprm() with flags configured per scenario, and puts it back for the next execution. The runner creates a real Landlock ruleset handling LANDLOCK_ACCESS_FS_WRITE_FILE without any rule and checks that: - a monitored child ends up landlocked (writing to a tmp file fails while a control execution succeeds); - the audit log flags are accepted; - LANDLOCK_RESTRICT_SELF_TSYNC is rejected with -EINVAL and leaves the execution unrestricted; - a second bpf_landlock_restrict_binprm() call on the same execution replaces the previously staged domain instead of failing or stacking; - a staged restriction is discarded when the execution fails after the bprm hook: the child execs an ENOEXEC file with a restriction staged, and after the failed execve(2) verifies that it is not landlocked; - a negative fd resolved through the acquire kfunc returns NULL. The failure programs check that verification rejects: - a tracing program calling the kfuncs (LSM and syscall programs only), - an LSM program calling the acquire kfunc (syscall programs only, where the ruleset fd is meaningful), - a syscall program calling the enforcement kfunc (sleepable bprm LSM hooks only), - an LSM program on a hook other than the sleepable bprm hooks, - a non-sleepable LSM program on an allowed hook, - a program leaking the acquired ruleset reference. The test needs CONFIG_SECURITY_LANDLOCK and the landlock LSM enabled in the test kernel; the runner skips if the Landlock syscalls are unavailable. Signed-off-by: Justin Suess --- tools/testing/selftests/bpf/config | 1 + tools/testing/selftests/bpf/config.x86_64 | 2 +- .../bpf/prog_tests/lsm_policy_kfuncs.c | 329 ++++++++++++++++++ .../bpf/progs/lsm_policy_kfuncs_failure.c | 100 ++++++ .../bpf/progs/lsm_policy_kfuncs_success.c | 107 ++++++ 5 files changed, 538 insertions(+), 1 deletion(-) create mode 100644 tools/testing/selftests/bpf/prog_tests/lsm_policy_kfunc= s.c create mode 100644 tools/testing/selftests/bpf/progs/lsm_policy_kfuncs_fai= lure.c create mode 100644 tools/testing/selftests/bpf/progs/lsm_policy_kfuncs_suc= cess.c diff --git a/tools/testing/selftests/bpf/config b/tools/testing/selftests/b= pf/config index ea7044f30adc..2fa734497461 100644 --- a/tools/testing/selftests/bpf/config +++ b/tools/testing/selftests/bpf/config @@ -120,6 +120,7 @@ CONFIG_SAMPLES=3Dy CONFIG_SAMPLE_LIVEPATCH=3Dm CONFIG_SECURITY=3Dy CONFIG_SECURITYFS=3Dy +CONFIG_SECURITY_LANDLOCK=3Dy CONFIG_SYN_COOKIES=3Dy CONFIG_TEST_BPF=3Dm CONFIG_UDMABUF=3Dy diff --git a/tools/testing/selftests/bpf/config.x86_64 b/tools/testing/self= tests/bpf/config.x86_64 index 523e0d29bbd4..2c4d857f69f5 100644 --- a/tools/testing/selftests/bpf/config.x86_64 +++ b/tools/testing/selftests/bpf/config.x86_64 @@ -125,7 +125,7 @@ CONFIG_LEGACY_VSYSCALL_NONE=3Dy CONFIG_LOG_BUF_SHIFT=3D21 CONFIG_LOG_CPU_MAX_BUF_SHIFT=3D0 CONFIG_LOGO=3Dy -CONFIG_LSM=3D"selinux,bpf,integrity" +CONFIG_LSM=3D"landlock,selinux,bpf,integrity" CONFIG_MAC_PARTITION=3Dy CONFIG_MAGIC_SYSRQ=3Dy CONFIG_MCORE2=3Dy diff --git a/tools/testing/selftests/bpf/prog_tests/lsm_policy_kfuncs.c b/t= ools/testing/selftests/bpf/prog_tests/lsm_policy_kfuncs.c new file mode 100644 index 000000000000..c91929f98e88 --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/lsm_policy_kfuncs.c @@ -0,0 +1,329 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright =C2=A9 2026 Justin Suess */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "lsm_policy_kfuncs_success.skel.h" +#include "lsm_policy_kfuncs_failure.skel.h" + +/* Fallbacks for old system headers. */ +#ifndef LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON +#define LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON (1U << 1) +#endif +#ifndef LANDLOCK_RESTRICT_SELF_TSYNC +#define LANDLOCK_RESTRICT_SELF_TSYNC (1U << 3) +#endif + +static int create_ruleset(void) +{ + const struct landlock_ruleset_attr attr =3D { + .handled_access_fs =3D LANDLOCK_ACCESS_FS_WRITE_FILE, + }; + + return syscall(__NR_landlock_create_ruleset, &attr, sizeof(attr), 0); +} + +static void reset_prog_state(struct lsm_policy_kfuncs_success *skel) +{ + skel->bss->called =3D false; + skel->bss->no_ruleset =3D false; + skel->bss->restrict_err =3D -1; + skel->bss->restrict2_err =3D -1; + skel->bss->kfunc_flags =3D 0; + skel->bss->double_call =3D false; +} + +/* + * Runs the syscall program that acquires the ruleset from + * @ruleset_fd, in the runner's fd table, and parks it in the map kptr + * slot for the LSM program. + */ +static int load_ruleset_into_map(struct lsm_policy_kfuncs_success *skel) +{ + LIBBPF_OPTS(bpf_test_run_opts, opts); + int err; + + err =3D bpf_prog_test_run_opts(bpf_program__fd(skel->progs.load_ruleset), + &opts); + if (!ASSERT_OK(err, "load_ruleset_run")) + return -1; + if (!ASSERT_OK(opts.retval, "load_ruleset_retval")) + return -1; + ASSERT_TRUE(skel->bss->got_null_for_bad_fd, "bad_fd_null"); + return 0; +} + +/* + * Forks a child that execs "sh -c ''". The monitored pid + * is only known, and can only be published to the BPF program, once + * the child exists: the child waits on a pipe until the parent has + * updated it. Returns the child's exit status, or -1 on error. + */ +static int run_exec_child(struct lsm_policy_kfuncs_success *skel, + bool monitored, const char *shell_cmd) +{ + int pipe_fds[2], status; + char buf =3D 0; + pid_t pid; + + if (!ASSERT_OK(pipe(pipe_fds), "pipe")) + return -1; + + pid =3D fork(); + if (!ASSERT_GE(pid, 0, "fork")) { + close(pipe_fds[0]); + close(pipe_fds[1]); + return -1; + } + if (pid =3D=3D 0) { + char *argv[] =3D { "sh", "-c", (char *)shell_cmd, NULL }; + + close(pipe_fds[1]); + read(pipe_fds[0], &buf, 1); + close(pipe_fds[0]); + execv("/bin/sh", argv); + exit(127); + } + close(pipe_fds[0]); + skel->bss->monitored_pid =3D monitored ? pid : 0; + write(pipe_fds[1], &buf, 1); + close(pipe_fds[1]); + + if (!ASSERT_EQ(waitpid(pid, &status, 0), pid, "waitpid")) + return -1; + if (!ASSERT_TRUE(WIFEXITED(status), "child_exited")) + return -1; + return WEXITSTATUS(status); +} + +/* + * Exit codes: 4 =3D unexpected write outcome, 0 =3D everything as + * expected. + */ +static void format_child_cmd(char *cmd, size_t len, bool expect_write_ok, + const char *tmp_path) +{ + if (expect_write_ok) + snprintf(cmd, len, "echo x > %s || exit 4; exit 0", tmp_path); + else + snprintf(cmd, len, + "if echo x > %s 2>/dev/null; then exit 4; fi; exit 0", + tmp_path); +} + +static void test_restrict_binprm(void) +{ + struct lsm_policy_kfuncs_success *skel =3D NULL; + char tmp_path[] =3D "/tmp/lsm_policy_kfuncs_XXXXXX"; + char cmd[256]; + int ruleset_fd, tmp_fd, ret; + + tmp_fd =3D mkstemp(tmp_path); + if (!ASSERT_GE(tmp_fd, 0, "mkstemp")) + return; + close(tmp_fd); + + ruleset_fd =3D create_ruleset(); + if (ruleset_fd < 0) { + if (errno =3D=3D EOPNOTSUPP || errno =3D=3D ENOSYS) + test__skip(); + else + ASSERT_GE(ruleset_fd, 0, "landlock_create_ruleset"); + goto out_unlink; + } + + skel =3D lsm_policy_kfuncs_success__open_and_load(); + if (!ASSERT_OK_PTR(skel, "skel_open_and_load")) + goto out; + skel->bss->ruleset_fd =3D ruleset_fd; + + if (!ASSERT_OK(lsm_policy_kfuncs_success__attach(skel), "skel_attach")) + goto out; + + if (load_ruleset_into_map(skel)) + goto out; + + /* Control: an unmonitored execution may write to the tmp file. */ + reset_prog_state(skel); + format_child_cmd(cmd, sizeof(cmd), true, tmp_path); + ret =3D run_exec_child(skel, false, cmd); + if (!ASSERT_EQ(ret, 0, "control_child_exit")) + goto out; + ASSERT_FALSE(skel->bss->called, "control_not_monitored"); + + /* + * A monitored execution starts landlocked: the ruleset handles + * LANDLOCK_ACCESS_FS_WRITE_FILE without any rule, so the write + * must fail. + */ + reset_prog_state(skel); + format_child_cmd(cmd, sizeof(cmd), false, tmp_path); + ret =3D run_exec_child(skel, true, cmd); + if (!ASSERT_EQ(ret, 0, "restricted_child_exit")) + goto out; + ASSERT_TRUE(skel->bss->called, "lsm_prog_called"); + ASSERT_FALSE(skel->bss->no_ruleset, "ruleset_in_map"); + ASSERT_EQ(skel->bss->restrict_err, 0, "restrict_binprm"); + + /* The audit log flags of landlock_restrict_self(2) apply too. */ + reset_prog_state(skel); + skel->bss->kfunc_flags =3D LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON; + format_child_cmd(cmd, sizeof(cmd), false, tmp_path); + ret =3D run_exec_child(skel, true, cmd); + if (!ASSERT_EQ(ret, 0, "log_flags_child_exit")) + goto out; + ASSERT_EQ(skel->bss->restrict_err, 0, "log_flags_restrict_binprm"); + + /* + * LANDLOCK_RESTRICT_SELF_TSYNC targets the calling threads, not + * an execution: the kfunc must reject it and the execution must + * stay unrestricted. + */ + reset_prog_state(skel); + skel->bss->kfunc_flags =3D LANDLOCK_RESTRICT_SELF_TSYNC; + format_child_cmd(cmd, sizeof(cmd), true, tmp_path); + ret =3D run_exec_child(skel, true, cmd); + if (!ASSERT_EQ(ret, 0, "tsync_child_exit")) + goto out; + ASSERT_TRUE(skel->bss->called, "tsync_prog_called"); + ASSERT_EQ(skel->bss->restrict_err, -EINVAL, "tsync_rejected"); + + /* + * A second call on the same execution replaces the staged + * domain (and releases the first one): the result is a single + * restriction, not an error. + */ + reset_prog_state(skel); + skel->bss->double_call =3D true; + format_child_cmd(cmd, sizeof(cmd), false, tmp_path); + ret =3D run_exec_child(skel, true, cmd); + if (!ASSERT_EQ(ret, 0, "double_child_exit")) + goto out; + ASSERT_EQ(skel->bss->restrict_err, 0, "double_restrict_first"); + ASSERT_EQ(skel->bss->restrict2_err, 0, "double_restrict_second"); +out: + lsm_policy_kfuncs_success__destroy(skel); + close(ruleset_fd); +out_unlink: + unlink(tmp_path); +} + +/* + * Checks that a staged restriction is discarded, and the staged + * domain released, when the execution fails after the bprm hook: the + * calling task must not end up landlocked. + */ +static void test_restrict_binprm_discard(void) +{ + struct lsm_policy_kfuncs_success *skel =3D NULL; + char tmp_path[] =3D "/tmp/lsm_policy_kfuncs_XXXXXX"; + char garbage_path[] =3D "/tmp/lsm_policy_garbage_XXXXXX"; + int ruleset_fd =3D -1, tmp_fd, garbage_fd, pipe_fds[2], status; + char buf =3D 0; + pid_t pid; + + tmp_fd =3D mkstemp(tmp_path); + if (!ASSERT_GE(tmp_fd, 0, "mkstemp")) + return; + close(tmp_fd); + + /* + * An executable file that no binfmt handler accepts: the exec + * fails with ENOEXEC after bprm_creds_for_exec() has run. + */ + garbage_fd =3D mkstemp(garbage_path); + if (!ASSERT_GE(garbage_fd, 0, "mkstemp_garbage")) + goto out_unlink; + if (!ASSERT_EQ(write(garbage_fd, "junk\n", 5), 5, "write_garbage")) { + close(garbage_fd); + goto out_unlink; + } + if (!ASSERT_OK(fchmod(garbage_fd, 0700), "chmod_garbage")) { + close(garbage_fd); + goto out_unlink; + } + close(garbage_fd); + + ruleset_fd =3D create_ruleset(); + if (ruleset_fd < 0) { + if (errno =3D=3D EOPNOTSUPP || errno =3D=3D ENOSYS) + test__skip(); + else + ASSERT_GE(ruleset_fd, 0, "landlock_create_ruleset"); + goto out_unlink; + } + + skel =3D lsm_policy_kfuncs_success__open_and_load(); + if (!ASSERT_OK_PTR(skel, "skel_open_and_load")) + goto out; + skel->bss->ruleset_fd =3D ruleset_fd; + reset_prog_state(skel); + + if (!ASSERT_OK(lsm_policy_kfuncs_success__attach(skel), "skel_attach")) + goto out; + + if (load_ruleset_into_map(skel)) + goto out; + + if (!ASSERT_OK(pipe(pipe_fds), "pipe")) + goto out; + + pid =3D fork(); + if (!ASSERT_GE(pid, 0, "fork")) + goto out; + if (pid =3D=3D 0) { + char *argv[] =3D { "garbage", NULL }; + int fd; + + close(pipe_fds[1]); + read(pipe_fds[0], &buf, 1); + close(pipe_fds[0]); + execv(garbage_path, argv); + /* + * The failed execution must leave no trace: no + * Landlock domain, i.e. writing must still work + * (exit 6). + */ + fd =3D open(tmp_path, O_WRONLY | O_TRUNC); + if (fd < 0) + exit(6); + close(fd); + exit(0); + } + close(pipe_fds[0]); + skel->bss->monitored_pid =3D pid; + write(pipe_fds[1], &buf, 1); + close(pipe_fds[1]); + + if (!ASSERT_EQ(waitpid(pid, &status, 0), pid, "waitpid")) + goto out; + if (!ASSERT_TRUE(WIFEXITED(status), "child_exited")) + goto out; + ASSERT_EQ(WEXITSTATUS(status), 0, "discard_child_exit"); + ASSERT_TRUE(skel->bss->called, "lsm_prog_called"); + ASSERT_EQ(skel->bss->restrict_err, 0, "restrict_binprm"); +out: + lsm_policy_kfuncs_success__destroy(skel); + if (ruleset_fd >=3D 0) + close(ruleset_fd); +out_unlink: + unlink(garbage_path); + unlink(tmp_path); +} + +void test_lsm_policy_kfuncs(void) +{ + if (test__start_subtest("restrict_binprm")) + test_restrict_binprm(); + if (test__start_subtest("restrict_binprm_discard")) + test_restrict_binprm_discard(); + RUN_TESTS(lsm_policy_kfuncs_failure); +} diff --git a/tools/testing/selftests/bpf/progs/lsm_policy_kfuncs_failure.c = b/tools/testing/selftests/bpf/progs/lsm_policy_kfuncs_failure.c new file mode 100644 index 000000000000..0335db547040 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/lsm_policy_kfuncs_failure.c @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright =C2=A9 2026 Justin Suess */ + +#include +#include +#include +#include "bpf_misc.h" + +char _license[] SEC("license") =3D "GPL"; + +struct bpf_landlock_ruleset; + +extern struct bpf_landlock_ruleset * +bpf_landlock_get_ruleset_from_fd(int fd) __ksym; +extern void +bpf_landlock_put_ruleset(struct bpf_landlock_ruleset *ruleset) __ksym; +extern int bpf_landlock_restrict_binprm(struct linux_binprm *bprm, + struct bpf_landlock_ruleset *ruleset, + u32 flags) __ksym; + +/* + * The LSM policy kfuncs are limited to LSM and syscall programs by + * the BPF-side kfunc filter: a tracing program calling one must fail + * verification. + */ +SEC("tp_btf/task_newtask") +__failure __msg("calling kernel function bpf_landlock_get_ruleset_from_fd = is not allowed") +int BPF_PROG(tracing_prog, struct task_struct *task, u64 clone_flags) +{ + struct bpf_landlock_ruleset *ruleset; + + ruleset =3D bpf_landlock_get_ruleset_from_fd(-1); + if (ruleset) + bpf_landlock_put_ruleset(ruleset); + return 0; +} + +/* + * A ruleset fd is only meaningful in the fd table of the task that + * set the ruleset up: the acquire kfunc is exclusive to syscall + * programs and must be rejected in an LSM program, even on an + * allowed hook. + */ +SEC("lsm.s/bprm_creds_for_exec") +__failure __msg("calling kernel function bpf_landlock_get_ruleset_from_fd = is not allowed") +int BPF_PROG(lsm_get, struct linux_binprm *bprm) +{ + struct bpf_landlock_ruleset *ruleset; + + ruleset =3D bpf_landlock_get_ruleset_from_fd(-1); + if (ruleset) + bpf_landlock_put_ruleset(ruleset); + return 0; +} + +/* + * Enforcement needs an execution to restrict: the enforcement kfunc + * is exclusive to the sleepable bprm LSM hooks and must be rejected + * in a syscall program. + */ +SEC("syscall") +__failure __msg("calling kernel function bpf_landlock_restrict_binprm is n= ot allowed") +int syscall_restrict(void *ctx) +{ + return bpf_landlock_restrict_binprm(NULL, NULL, 0); +} + +/* + * Any LSM attach point other than the sleepable bprm hooks must be + * rejected. + */ +SEC("lsm.s/file_open") +__failure __msg("calling kernel function bpf_landlock_put_ruleset is not a= llowed") +int BPF_PROG(wrong_hook, struct file *file) +{ + bpf_landlock_put_ruleset(NULL); + return 0; +} + +/* + * The kfuncs may sleep: a non-sleepable program on an allowed hook + * must be rejected. + */ +SEC("lsm/bprm_creds_for_exec") +__failure +__msg("program must be sleepable to call sleepable kfunc bpf_landlock_put_= ruleset") +int BPF_PROG(nonsleepable_prog, struct linux_binprm *bprm) +{ + bpf_landlock_put_ruleset(NULL); + return 0; +} + +/* An acquired ruleset reference must be released before returning. */ +SEC("syscall") +__failure __msg("Unreleased reference") +int leak_ruleset(void *ctx) +{ + bpf_landlock_get_ruleset_from_fd(-1); + return 0; +} diff --git a/tools/testing/selftests/bpf/progs/lsm_policy_kfuncs_success.c = b/tools/testing/selftests/bpf/progs/lsm_policy_kfuncs_success.c new file mode 100644 index 000000000000..2107206ae144 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/lsm_policy_kfuncs_success.c @@ -0,0 +1,107 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright =C2=A9 2026 Justin Suess */ + +#include +#include +#include + +char _license[] SEC("license") =3D "GPL"; + +struct bpf_landlock_ruleset; + +extern struct bpf_landlock_ruleset * +bpf_landlock_get_ruleset_from_fd(int fd) __ksym; +extern void +bpf_landlock_put_ruleset(struct bpf_landlock_ruleset *ruleset) __ksym; +extern int bpf_landlock_restrict_binprm(struct linux_binprm *bprm, + struct bpf_landlock_ruleset *ruleset, + u32 flags) __ksym; + +struct ruleset_slot { + struct bpf_landlock_ruleset __kptr *ruleset; +}; + +struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, 1); + __type(key, int); + __type(value, struct ruleset_slot); +} ruleset_map SEC(".maps"); + +int monitored_pid; +int ruleset_fd; +u32 kfunc_flags; +bool double_call; +bool got_null_for_bad_fd; +bool no_ruleset; +int restrict_err; +int restrict2_err; +bool called; + +/* + * Runs in the test runner's context through BPF_PROG_RUN: + * @ruleset_fd is resolved in the runner's fd table and the acquired + * ruleset is handed to the LSM program through the map kptr slot. + */ +SEC("syscall") +int load_ruleset(void *ctx) +{ + struct bpf_landlock_ruleset *ruleset, *old; + struct ruleset_slot *slot; + int key =3D 0; + + slot =3D bpf_map_lookup_elem(&ruleset_map, &key); + if (!slot) + return 1; + + /* A fd that is not a Landlock ruleset must resolve to NULL. */ + ruleset =3D bpf_landlock_get_ruleset_from_fd(-1); + if (!ruleset) + got_null_for_bad_fd =3D true; + else + bpf_landlock_put_ruleset(ruleset); + + ruleset =3D bpf_landlock_get_ruleset_from_fd(ruleset_fd); + if (!ruleset) + return 2; + + old =3D bpf_kptr_xchg(&slot->ruleset, ruleset); + if (old) + bpf_landlock_put_ruleset(old); + return 0; +} + +SEC("lsm.s/bprm_creds_for_exec") +int BPF_PROG(restrict_exec, struct linux_binprm *bprm) +{ + struct bpf_landlock_ruleset *ruleset, *old; + struct ruleset_slot *slot; + int key =3D 0; + + if (monitored_pid !=3D (bpf_get_current_pid_tgid() >> 32)) + return 0; + + called =3D true; + + slot =3D bpf_map_lookup_elem(&ruleset_map, &key); + if (!slot) + return 0; + + ruleset =3D bpf_kptr_xchg(&slot->ruleset, NULL); + if (!ruleset) { + no_ruleset =3D true; + return 0; + } + + restrict_err =3D bpf_landlock_restrict_binprm(bprm, ruleset, kfunc_flags); + if (double_call) + /* Replaces the domain staged by the first call. */ + restrict2_err =3D bpf_landlock_restrict_binprm(bprm, ruleset, + kfunc_flags); + + /* Keep the ruleset for the next monitored execution. */ + old =3D bpf_kptr_xchg(&slot->ruleset, ruleset); + if (old) + bpf_landlock_put_ruleset(old); + return 0; +} --=20 2.54.0 From nobody Fri Oct 2 14:03:02 2026 Received: from mail-yw1-f176.google.com (mail-yw1-f176.google.com [209.85.128.176]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id BC21932B11E for ; Fri, 31 Jul 2026 02:21:31 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.176 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785464493; cv=none; b=KTQGzS72SEdRdfHI2lbwa+8gtPbBgpamKnoR0RMt6SnGdhzHTKyZacfVTDDf/fxii/Tc/FUbxa+WKNkLiKl5hFqFilWvyqRnj/25/YiwMRuRpwmb/nApmnTeTzYaX4r9S3WJq0Lgd68FG0RI0kM0AVPiQ+NJ5SL8XklsZWZuO0E= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785464493; c=relaxed/simple; bh=4pXwrm8HEe/yPPybo7ROzJO7tVzYi40tTEffwp8UrdM=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=P4XY1/NAMjn5pXWzG21jl3wKVtlh5LQsIw4+Zd2cuAMbzE0pxoEQoM3stgmFVowscfP9h5xdzl+6lhMgfV7jzfbBOWkgDZR1BAd12nrlgkLAsjvpTCytuQXB9Y9Jr0djcDm3COT+Vi5Z6eNlzrHLxoxb/yZk1ixUkjPulBP4NoM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=UZIfLPuQ; arc=none smtp.client-ip=209.85.128.176 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="UZIfLPuQ" Received: by mail-yw1-f176.google.com with SMTP id 00721157ae682-80cebd41372so6851217b3.3 for ; Thu, 30 Jul 2026 19:21:31 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1785464491; x=1786069291; darn=vger.kernel.org; h=content-transfer-encoding:content-type:mime-version:references :in-reply-to:message-id:date:subject:cc:to:from:from:to:cc:subject :date:message-id:reply-to:content-type; bh=Vb/WD/mrd6qKrKENAh+IH8pl9uBDuC0uFk5kGRLG0IU=; b=UZIfLPuQN37GKP0tcYcLwdjvb1KkeLwthYIMFzpVw3Th3a1QwYNOh5CUZsrC/jGDAh OAubSQ0PiqCJN7K40xkqPOpeqyZijJdPAzPw/134h/1hCgos6ZggabJ+2mYyJjleHUey IF2o21rMetvTupPtR8g3XnhtbB5q4okQbj4VwX5QHYy1r3aYlNCnQh87SeO2jDPQgfEM gxW/Do/bgEI38240xE10wks8YVVvGKHx2TLWzBkFBUPY/RzK9hvWQWmZRLwUR9JFjc/O QjXT7nvGBq4+3fnAiei4e81YCIlBfsfKOcNejWBDxoBIYeRWuHYtsx10WOR8PGwhP/gf HD+w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1785464491; x=1786069291; h=content-transfer-encoding:content-type:mime-version:references :in-reply-to:message-id:date:subject:cc:to:from:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=Vb/WD/mrd6qKrKENAh+IH8pl9uBDuC0uFk5kGRLG0IU=; b=qoWTygNf0gAWLT9KAUCBbqZCe8ogp0fYqxFBUAxZ6fmU/Kki0Xl7X+NCiVT1HTe4J8 /AjPHvPa5ehqvsNMG+hn57jkW2uvx07Ehv+fKIuX+gi3YS5o7POROT+vs9QA3jrfx62z XjJr1vDa8nk1YhDFVxo7WjpNhhMLMPeHs1GhGxb27I1tQ8KjHSkbWdsib/BSsrYKFPJp 2SN52SR5ikEkFoh+oGA67NOUFAv/AJ72zd26Nu9pxP/phrQO3vuPYpAvkC49p4oSAZj3 z2XF8sTgndpp4ghf/u/I1J3VqTfybgPvCahhrQUNWHE6AtUI/27ckEiEV3F0JQ/ruNvx 9RSg== X-Forwarded-Encrypted: i=1; AHgh+Rq6sP/MixREivOjCVCpbxKJxq8iSXGP0JZzF/wHnqLviAR8Wj3uPi1Nui14MM36FglHuynMLV673/0iev4=@vger.kernel.org X-Gm-Message-State: AOJu0Yxx5dZu/4c+v84vM229xcgifDsFH3AIVkaTUcWmtRaboTLMPlqM UUzB362p6+S3TFa/I3ngLYWddsor3Ho0q9PjoqZhV3p7VZu3N9G1Q8xx X-Gm-Gg: AR+sD11Wi/DummM2+iUVY6KugfOQspaTVt7gsgGqKl9RpFJ71W6PIY7BXUs8V7Jxa09 tHE1rmqchQqj8nY4wnqpfbl2FhZkCdX8i3qmoaHkSRxMGdsNoWpeRyHBFJZOf8u7h9uVlxMirLT hO5kWazmjE4cwtGELIqbK+raYrrlV0Muul54QT9F6h8WEG4GUnpriYvMsqHGvvM7d2en9w/ZLK8 Ja6V6Fg1NlEHPM7ZjJxqKIEolqeNQAMh01eDUSe5PAhcc/3dq2AtfYc3WpnuID30EFPUyLJqdok 86UgYopRYxs2C0ocn1AueUH+Dj+vBaxfZMF4KNVlsKIqaqYTLJ2kCn9Hg4FnQW5pcwwWz2G1Jdw 0M4zhD079kUAXE2elSdf7kVucIZsKqxCkRuoi/Rx2rQuOgQtLqRLS7rh4aLjfAVIZK5PeFp8NN5 3BJhoQ2jb2RQkYBCYR9EnqUn5/gTVl6XEv0DwiLUO8Zn3tr4IkYgxh2VpY5ZhIbYOefw69Fd+sH Wt0wGL0UpfTBYPUkdd1dQ== X-Received: by 2002:a05:690c:63c6:b0:80c:85c6:898f with SMTP id 00721157ae682-81fcbbaa180mr512937b3.62.1785464490721; Thu, 30 Jul 2026 19:21:30 -0700 (PDT) Received: from zenbox ([2600:1700:18fb:6011:e94:8a83:feea:6720]) by smtp.gmail.com with ESMTPSA id 00721157ae682-81fb8b26e8bsm20519047b3.47.2026.07.30.19.21.30 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 30 Jul 2026 19:21:30 -0700 (PDT) From: Justin Suess To: ast@kernel.org, daniel@iogearbox.net, andrii@kernel.org, kpsingh@kernel.org, paul@paul-moore.com, mic@digikod.net, viro@zeniv.linux.org.uk, brauner@kernel.org, kees@kernel.org Cc: gnoack@google.com, jack@suse.cz, song@kernel.org, yonghong.song@linux.dev, martin.lau@linux.dev, m@maowtm.org, bpf@vger.kernel.org, linux-security-module@vger.kernel.org, linux-kernel@vger.kernel.org, Justin Suess Subject: [PATCH bpf-next 12/13] landlock: Document the BPF kfunc interface Date: Thu, 30 Jul 2026 22:20:45 -0400 Message-ID: <20260731022047.189137-13-utilityemal77@gmail.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260731022047.189137-1-utilityemal77@gmail.com> References: <20260731022047.189137-1-utilityemal77@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Describe the new BPF kfuncs for Landlock. Cc: Micka=C3=ABl Sala=C3=BCn Signed-off-by: Justin Suess --- Documentation/security/landlock.rst | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/Documentation/security/landlock.rst b/Documentation/security/l= andlock.rst index c5186526e76f..01197c7580ec 100644 --- a/Documentation/security/landlock.rst +++ b/Documentation/security/landlock.rst @@ -129,6 +129,31 @@ The reasoning is: restrictions, because access within the same scope is already allowed based on ``LANDLOCK_ACCESS_FS_RESOLVE_UNIX``. =20 +BPF kfuncs +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +BPF programs can apply a userspace-created Landlock ruleset to an +execution. A syscall program (``BPF_PROG_TYPE_SYSCALL``), running in +the context of the process that set the ruleset up, acquires the +ruleset from its file descriptor and typically hands it over through +a map kptr field; a sleepable LSM BPF program attached to the +``bprm_creds_for_exec`` or ``bprm_creds_from_file`` hooks then +enforces it on an execution. + +This can be used to inspect the runtime context of a pending execution, +and enforce a Landlock policy through BPF. + +The restriction is staged in the Landlock blob of the +credentials prepared for the execution and committed past the exec +point of no return, so a failed execution leaves the calling task +untouched. The ``landlock_restrict_self(2)`` flags apply, with the +exception of ``LANDLOCK_RESTRICT_SELF_TSYNC``. + +.. kernel-doc:: kernel/bpf/bpf_lsm.c + :identifiers: bpf_landlock_get_ruleset_from_fd + bpf_landlock_put_ruleset + bpf_landlock_restrict_binprm + Tests =3D=3D=3D=3D=3D =20 --=20 2.54.0 From nobody Fri Oct 2 14:03:02 2026 Received: from mail-yx1-f49.google.com (mail-yx1-f49.google.com [74.125.224.49]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id AEAF731353B for ; Fri, 31 Jul 2026 02:21:33 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.224.49 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785464498; cv=none; b=cK/d9e+6dUDxZmEKv84W1QnNRRpskbWqYC4uDZ3u8Ol5zQV/20uANTUeHo8M+wlCJAhERVPODmhh3xh2s5W7a8KzCph0fEV8qrAjne4CDwXCqkxahHvkL2SmpIJx0JAQIdM2N4mj34CPHTROd4bxbRyImIPW5N1uo7fCTkMkWjI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785464498; c=relaxed/simple; bh=6ETYMAWu8ofZx0Y+WACFFKAJ2qtGC9uy+a+hCAKsrP0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=N5dxnLQPoVJrbqw9S26R/0fJW3RQP4iANgFAUjx6DyeCZprN+plufzDpJuKVaw9e4+7HGJ7gX1hhKxwmWoiAn3VCG4ay9vYyUmsNUjJZTxjtUL9GjdIX/4xpQN0r40wTc+feefVFSfcA85peb9QKe2F3/BJkfiZK3mlCnpqBrOc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=TpPIjvoX; arc=none smtp.client-ip=74.125.224.49 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="TpPIjvoX" Received: by mail-yx1-f49.google.com with SMTP id 956f58d0204a3-668c1b780e5so607832d50.2 for ; Thu, 30 Jul 2026 19:21:33 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1785464493; x=1786069293; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=tjpk63ZKju1bkNGm+aJ5Xh/f+GLe8/olsmxsdpk0+FY=; b=TpPIjvoX3rcrnHa8jYWkxB89VxV8ldNTsEGGEzHiBCL3IGx+Um4o0QxU2E5aocnB9e g3/cNUpaZz1kunK0r37Cc0/hMbm+MWkJWSdH3bLz1MMRo9t24NNinRq9C/N30ot7LqP1 uxvk6uar55XyahJbiT3+XyKxV9bPAYb2eR0skIRMoeSKxvx9w0Gl8y5RH1UNvNftk5ba 4tl6HstcbBsOD4iI2oTj7FG4/8Daba7ckCZ++ujaSObXqa8vrWGEKeqXpEaHg8Um4lfd BCmSaz1frgkP4xOjyg4HrRT8jK7DoROUdTGQHvrhSRA2HQenaZb+mxN6BFPfdNfLH14l dJJw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1785464493; x=1786069293; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=tjpk63ZKju1bkNGm+aJ5Xh/f+GLe8/olsmxsdpk0+FY=; b=brBqYTkAzdiI7LGEOBOSfI35HhGj36TJKgqE/vlsWkmGjhTAM4ywWZLrbswg61BG/Y mJZQv1bGdl97Ug+aoiVNqhxvTXPDr+5zpgFcAAgrgLH4DIZt+AVALdZNBZcZBnmeQNXm ptBGCJmoRAUrFQIKpwiWiQ3DKWwuQ8j6cNgfJ7PFLOlQAmT9chL7lsR7hW/EWVYT3dsN Fy68FH2ke/3/Gbha3VDy0lsVjaNSrldpKrcw9GZecADXVI38h2IxceuH5sTEreOUiL1e 4LUwuAYvnhyvWdsPZhIeQ7Ua8vEn9GAioZvNxWWlho9574aR9nKszKsp/6K5vJjSCfIJ iKnw== X-Forwarded-Encrypted: i=1; AHgh+RrPUhbnjWDeOrBFSZGTI0uR7Y9Q4259w6ilLyure45p1oVfkc1XB6FMLJ22nNsJvV8zOvmSt1KmfMnUWKU=@vger.kernel.org X-Gm-Message-State: AOJu0YyHM7NY9jDr+UsjlAIemz8Ql7GhmZSjcpsfeoP5OnBTabZ8+8Qi 4aihWxSbYIPtB0UqoAZSJdgBi+pTEz6Ncs0BiqN09KjPRCrtLoI5vbKP X-Gm-Gg: AR+sD11LqqOLlgeuVpnKC9lSSL3J6DRrMMfbHq/zUVazqZYXOakcsY9mTG5dek1QfCn NWx1qaQgDBf6kaeZl13Ij+w8M2OjaPn7vH58u6i9cTuKs0vFiCzVLfYd8Uvy2cgcDylIWqzMbkx bTdxK/tHkTMnqtFXws6PXLvPOnNRggDfyvMHYZptTqFKzZc+JwZRR7Bl+0rtUX6vwoArxEGl8Hr iTCWIOAUSyvYQqJrnDN/rv/sHC8h+Y0PZmKu9aTKy3op0VJBU3HxLRee4Br+s6QEa9MAiHLifUw zYeEPwrmvBF7cpmryBi+tNv4forKKfIiKl/HppzflHT+mjPKEyZRN62dr/GjpEpZNpGiDBawMMs 8fkYeRjuaSc6WVCRrth/aV9N6D1Ging0D0HXjQyehm5G22IqGVx7tBF068yIsaweLUWA5+HY6n6 4bUxysKR8P9piFMEOG82XRirgWkflreVVz5mq6wLZBi2cNiF5UQlQvVNI/Zvpc9IZp+J9+/S9NV FpJ+8F+xRKw+clWj43g5w== X-Received: by 2002:a05:690c:4c11:b0:80c:85e5:8756 with SMTP id 00721157ae682-81fcbb74b6dmr533417b3.63.1785464492602; Thu, 30 Jul 2026 19:21:32 -0700 (PDT) Received: from zenbox ([2600:1700:18fb:6011:e94:8a83:feea:6720]) by smtp.gmail.com with ESMTPSA id 00721157ae682-81fb8b26e8bsm20519047b3.47.2026.07.30.19.21.31 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 30 Jul 2026 19:21:32 -0700 (PDT) From: Justin Suess To: ast@kernel.org, daniel@iogearbox.net, andrii@kernel.org, kpsingh@kernel.org, paul@paul-moore.com, mic@digikod.net, viro@zeniv.linux.org.uk, brauner@kernel.org, kees@kernel.org Cc: gnoack@google.com, jack@suse.cz, song@kernel.org, yonghong.song@linux.dev, martin.lau@linux.dev, m@maowtm.org, bpf@vger.kernel.org, linux-security-module@vger.kernel.org, linux-kernel@vger.kernel.org, Justin Suess , Casey Schaufler Subject: [PATCH bpf-next 13/13] lsm: Document the LSM policy kptr hooks Date: Thu, 30 Jul 2026 22:20:46 -0400 Message-ID: <20260731022047.189137-14-utilityemal77@gmail.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260731022047.189137-1-utilityemal77@gmail.com> References: <20260731022047.189137-1-utilityemal77@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Describe the split of responsibilities in lsm-development.rst: an LSM exposes policy operations to any kernel-internal caller through generic LSM hooks. The BPF subsystem owns the strongly typed kfuncs built on top of them, and the providing LSM only implements ordinary LSM hooks. Cc: Paul Moore Cc: Casey Schaufler Signed-off-by: Justin Suess --- Notes: This attempts to clarify some of the conclusions in what an LSM can and can't do from Paul and Casey's feedback and make it hard documentation. Let me know if another place is more deserving of it. Documentation/security/lsm-development.rst | 27 ++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/Documentation/security/lsm-development.rst b/Documentation/sec= urity/lsm-development.rst index 5895e529da7f..fc3af206e795 100644 --- a/Documentation/security/lsm-development.rst +++ b/Documentation/security/lsm-development.rst @@ -15,3 +15,30 @@ see ``security/security.c`` and associated structures: =20 .. kernel-doc:: security/security.c :export: + +LSM policy kptr hooks and BPF kfuncs +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +The LSM framework implements an interface for individual LSMs to +expose their configuration through BPF kfuncs and kptrs. An LSM +may not export any kfunc or other BPF interface directly. + +An LSM wishing to expose a BPF kfunc must reuse an existing security +hook or implement a new sufficiently generic LSM hook for the desired +interface. The hooks are then called from kfunc definitions in +``kernel/bpf``. This allows LSM hooks to remain sufficiently generic +while allowing BPF programs to take advantage of the strong typing +and runtime checking offered by the BPF verifier. + +The LSM providing an operation implements the operation's hook with +``LSM_HOOK_INIT()`` like any other hook. A BPF program calling an +LSM kfunc therefore reaches the LSM the same way every other +kernel caller does: through an LSM hook. The hooks backing kfuncs +follow the usual rules for new LSM hooks: their contract must be +LSM agnostic so that other LSMs could provide a meaningful +implementation of the same operation. + +Whether the LSM providing an operation is built in and active is a +runtime property: the kfuncs are always registered when +``CONFIG_BPF_LSM`` is enabled. BPF program loading is thus +independent of the boot-time LSM configuration. --=20 2.54.0