* [GIT PULL] TPM DEVICE DRIVER: for-next-keys-7.2-rc1
From: Jarkko Sakkinen @ 2026-06-19 0:48 UTC (permalink / raw)
To: Linus Torvalds
Cc: Peter Huewe, Jason Gunthorpe, David Howells, keyrings,
linux-integrity, linux-kernel
The following changes since commit 0e0611827f3349d0a2ac121c023a6d3260dcecdb:
Merge tag 'pull-fixes' of gitolite.kernel.org:pub/scm/linux/kernel/git/viro/vfs (2026-06-15 15:53:57 +0530)
are available in the Git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/jarkko/linux-tpmdd.git tags/for-next-keys-7.2-rc1
for you to fetch changes up to 1b9524250996b1f2f49833a1b2ae21c34e486f85:
keys: keyctl_pkey: replace BUG with return -EOPNOTSUPP (2026-06-15 15:19:13 +0300)
----------------------------------------------------------------
Hi
Please, pull. This pull request contains only fixes.
BR, Jarkko
----------------------------------------------------------------
David Laight (1):
keys: Replace strcpy(derived_buf, "AUTH_KEY") with strscpy(..., HASH_SIZE)
Eric Biggers (1):
KEYS: encrypted: Remove unnecessary selection of CRYPTO_RNG
Gui-Dong Han (1):
KEYS: Use acquire when reading state in keyring search
Jarkko Sakkinen (2):
KEYS: fix overflow in keyctl_pkey_params_get_2()
KEYS: trusted: Debugging as a feature
Len Bao (1):
keys/trusted_keys: mark 'migratable' as __ro_after_init
Mohammed EL Kadiri (3):
keys: prevent slab cache merging for key_jar
keys: request_key: replace BUG with return -EINVAL
keys: keyctl_pkey: replace BUG with return -EOPNOTSUPP
Shaomin Chen (1):
keys: Pin request_key_auth payload in instantiate paths
Thorsten Blum (1):
keys: use kmalloc_flex in user_preparse
Documentation/admin-guide/kernel-parameters.txt | 16 +++++++++
include/keys/request_key_auth-type.h | 2 ++
include/keys/trusted-type.h | 21 +++++++-----
security/keys/Kconfig | 1 -
security/keys/encrypted-keys/encrypted.c | 4 +--
security/keys/internal.h | 2 ++
security/keys/key.c | 2 +-
security/keys/keyctl.c | 24 ++++++++++----
security/keys/keyctl_pkey.c | 14 ++++++--
security/keys/keyring.c | 2 +-
security/keys/request_key.c | 2 +-
security/keys/request_key_auth.c | 33 +++++++++++++++++--
security/keys/trusted-keys/Kconfig | 23 +++++++++++++
security/keys/trusted-keys/trusted_caam.c | 7 ++--
security/keys/trusted-keys/trusted_core.c | 8 ++++-
security/keys/trusted-keys/trusted_tpm1.c | 44 ++++++++++++++-----------
security/keys/user_defined.c | 2 +-
17 files changed, 158 insertions(+), 49 deletions(-)
^ permalink raw reply
* Re: inherit null-key across hibernate (was: Re: regression: kernel log "flooded" with tpm tpm0: A TPM error (2306) occurred attempting to create NULL primary)
From: Jarkko Sakkinen @ 2026-06-19 0:38 UTC (permalink / raw)
To: Daniel Golle; +Cc: James Bottomley, Christoph Anton Mitterer, linux-integrity
In-Reply-To: <ajDIDcW_EzgLB0qX@makrotopia.org>
On Tue, Jun 16, 2026 at 04:50:37AM +0100, Daniel Golle wrote:
> Hi Jarkko,
> Hi James,
>
> first of all, sorry for hijacking a thread from 2 years ago.
>
> On Thu, Nov 14, 2024 at 12:34:30AM +0200, Jarkko Sakkinen wrote:
> > On Wed Nov 13, 2024 at 8:12 PM EET, James Bottomley wrote:
> > > > I think we might have to expect the NULL name to change on actual
> > > > hibernation because unlike suspend to ram it does power off the TPM.
> > >
> > > I checked the code: we're coming in on the correct path to renew the
> > > null seed after hibernation, so it should all work. The problem seems
> > > to be that your TPM itself is doing something invalid because the name
> > > we calculate for the primary key doesn't match what your TPM says it
> > > should be. Absent some form of attack or bus integrity problem, that
> > > shouldn't ever happen, so I'm even more curious to know why it worked
> > > in 6.11.5 and before and whether current upstream works.
> > >
> > > I haven't found it yet, but I think the every 10s signature is because
> > > the hibernation path is trying to restart the TPM device and won't take
> > > no for an answer.
> >
> > My fix returned the behavior how it was before my earlier fix in this
> > corner case (i.e. disable TPM). The issue has gone unnoticed before
> > since it has emitted only a single klog entry.
> >
> > On suspend this has not happened to me so obvious deduction is that
> > hibernate resets the null seed.
> >
> > Hibernate needs an addition a fix to disable bus encryption from kernel
> > command-line completely, i.e. tpm.disable_integrity following the
> > convention from my earlier fix [1].
>
> I'd like to offer a way it might be resolvable with the null key after
> all, without provisioning a persistent NV key -- by changing the
> question from "re-derive the null primary and compare" to "inherit the
> trust the resume has already established".
>
> Resume-from-hibernation is a TPM Restart (Shutdown(STATE) ->
> Startup(CLEAR)), i.e. a firmware cold-init of the (f)TPM, after which
> the boot/initramfs kernel establishes a fresh, genuine null primary.
> In the common configuration (FDE with the resume/swap device inside
> a TPM-sealed LUKS2 container) that same TPM has, moments earlier and
> *before* the hibernation image is restored, cryptographically attested
> itself by unsealing the resume device. A substituted or interposed TPM
> cannot produce that unseal.
>
> So rather than letting the resumed kernel re-derive the null name,
> find a mismatch and disable the chip, the boot kernel's
> freshly-established and unseal-validated null primary could be
> inherited by the resumed image. The existing null-seed TOFU model is
> preserved; nothing new is provisioned.
>
> The gate is the unseal, and the adversary case shows why it is the
> right gate:
>
> Malice swaps (or interposes on) the TPM while the machine is
> hibernated, then leaves. Alice powers on. The initramfs attempts the
> TPM unseal of the resume device; with a foreign TPM it fails, so
> systemd-cryptsetup falls back to the passphrase, which Alice --
> seeing a prompt -- types. The disk opens and the system resumes.
>
> If trust were re-established here, Alice would have personally vouched
> for Malice's TPM. But the unseal *failed*, so under this scheme
> nothing is inherited and the chip stays fail-closed exactly as today.
> The passphrase proves a human is present; it never proves the TPM is
> the genuine one.
> Hence: unseal succeeded -> inherit the validated null primary;
> passphrase fallback -> trust is lost, stay disabled.
>
> This keeps the property the null-seed design wants -- an in-session
> reset is not on the hibernate-restore path and is still caught --
> while removing the false positive only where the platform has already
> re-attested the TPM.
>
> The hard parts, and where I'd value direction:
>
> - systemd-cryptsetup would need to signal "the resume device was
> unsealed by the TPM this boot" (vs. the passphrase fallback).
> This is per-resume runtime state; a static command-line parameter
> (and obviously build-time config as well) cannot represent it.
>
> - the validated null primary has to cross the boot -> resumed memory
> discontinuity (the initramfs kernel's state is overwritten by the
> restored image). Boot and image kernel are the same binary, so
> patching chip->null_key_name in the restored image is mechanically
> possible; a small reserved/nosave hand-off area may be cleaner.
> I don't know the hibernate path well enough to say which is right.
>
> It is admittedly cross-subsystem (tpm + pm/hibernate +
> systemd/cryptsetup), which is presumably why it hasn't been done.
> Compared with the persistent NV-key route
> (tpm.integrity_key=<handle>): that avoids the carry-across but needs
> the key provisioned and managed, and a persistent key's name no longer
> changes on a genuine reset, so the implicit reset detection has to be
> reconstructed. The null-key-inherit approach keeps the existing model
> and defers "is this the same TPM?" to the unseal that has already
> happened.
>
> Does this seem viable, or is there a reason the unseal-as-attestation
> gate does not hold that I'm missing?
>
> (For motivation: on a firmware TPM -- Intel PTT here -- there is no
> external bus to interpose, so the protection has no benefit on this
> class of hardware at all, yet the legitimate hibernation power-cycle
> still trips the disable. For fTPMs specifically, not enabling the
> feature is arguably the better answer; but for discrete TPMs that
> hibernate, a real solution seems a good idea if doable.)
I think this has at least changed since 2024: bootc container type
of deployment is where you can realistically configure hibernate,
and use it in a real application.
>
>
> Cheers
>
>
> Daniel
BR, Jarkko
^ permalink raw reply
* Re: [PATCH bpf-next v3 1/2] bpf: add bpf_init_inode_xattr kfunc for atomic inode labeling
From: David Windsor @ 2026-06-18 22:50 UTC (permalink / raw)
To: bot+bpf-ci
Cc: viro, brauner, jack, ast, daniel, john.fastabend, andrii, eddyz87,
memxor, martin.lau, song, yonghong.song, jolsa, emil, kpsingh,
mattbobrowski, paul, jmorris, serge, zohar, roberto.sassu,
dmitry.kasatkin, eric.snowberg, stephen.smalley.work, omosnace,
casey, shuah, linux-kernel, linux-fsdevel, bpf,
linux-security-module, linux-integrity, selinux, linux-kselftest,
martin.lau, clm, ihor.solodrai
In-Reply-To: <e9ec622a8a49be299cbbfd459e2fc10693bcd65c6dbf00c9e2645281564e5579@mail.kernel.org>
On Thu, Jun 18, 2026 at 5:22 PM <bot+bpf-ci@kernel.org> wrote:
>
> [ ... ]
> >
> > +struct xattr_ctx {
> > + struct xattr *xattrs;
> > + int *xattr_count;
> > +};
> > +
>
> Paul Moore suggested in v2 renaming this to 'struct lsm_xattrs' to avoid
> overloading the term 'ctx' in the LSM space. The struct was renamed from
> 'lsm_xattr_ctx' to 'xattr_ctx' in v3, but 'ctx' is still in the name. Was
> there a reason to keep 'ctx' in the name, or would renaming to 'lsm_xattrs'
> as Paul suggested be preferable?
>
Indeed he did; I mistakenly inverted his original request.
Will send a fix for this, as well as converting GFP_KERNEL to GFP_NOFS
in the allocation in __bpf_init_inode_xattr.
> > /* Default (no) options for the capable function */
> > #define CAP_OPT_NONE 0x0
> > /* If capable should audit the security request */
>
> [ ... ]
>
> > diff --git a/kernel/bpf/bpf_lsm.c b/kernel/bpf/bpf_lsm.c
> > index 564071a92d7d..86a8e188b900 100644
> > --- a/kernel/bpf/bpf_lsm.c
> > +++ b/kernel/bpf/bpf_lsm.c
> > @@ -113,6 +113,9 @@ void bpf_lsm_find_cgroup_shim(const struct bpf_prog *prog,
> > }
> > #endif
> >
> > +BTF_ID_LIST_SINGLE(bpf_lsm_inode_init_security_btf_ids, func,
> > + bpf_lsm_inode_init_security)
> > +
> > int bpf_lsm_verify_prog(struct bpf_verifier_log *vlog,
> > const struct bpf_prog *prog)
> > {
> > @@ -137,6 +140,12 @@ int bpf_lsm_verify_prog(struct bpf_verifier_log *vlog,
> > return -EINVAL;
> > }
> >
> > + /* bpf reserves a fixed number of xattr slots for itself.
> > + * Set the attach limit so the trampoline rejects excess attaches.
> > + */
>
> This isn't a bug, but a subsystem pattern flags this as potentially
> concerning: the BPF subsystem comment-style rule for files under kernel/bpf/
> requires multi-line comments to have the opening /* on its own line, with
> comment text starting on the next line. The preferred form would be:
>
> /*
> * bpf reserves a fixed number of xattr slots for itself.
> * Set the attach limit so the trampoline rejects excess attaches.
> */
>
> > + if (btf_id == bpf_lsm_inode_init_security_btf_ids[0])
> > + prog->aux->attach_limit = BPF_LSM_INODE_INIT_XATTRS;
> > +
> > return 0;
> > }
> >
>
> [ ... ]
>
>
> ---
> AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
> See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
>
> CI run summary: https://github.com/kernel-patches/bpf/actions/runs/27788616397
^ permalink raw reply
* Re: [PATCH bpf-next v3 1/2] bpf: add bpf_init_inode_xattr kfunc for atomic inode labeling
From: bot+bpf-ci @ 2026-06-18 21:22 UTC (permalink / raw)
To: dwindsor, viro, brauner, jack, ast, daniel, john.fastabend,
andrii, eddyz87, memxor, martin.lau, song, yonghong.song, jolsa,
emil, kpsingh, mattbobrowski, paul, jmorris, serge, zohar,
roberto.sassu, dmitry.kasatkin, eric.snowberg,
stephen.smalley.work, omosnace, casey, shuah
Cc: linux-kernel, linux-fsdevel, bpf, linux-security-module,
linux-integrity, selinux, linux-kselftest, dwindsor, ast, andrii,
daniel, martin.lau, eddyz87, yonghong.song, clm, ihor.solodrai
In-Reply-To: <20260618203411.73917-2-dwindsor@gmail.com>
[-- Attachment #1: Type: text/plain, Size: 2507 bytes --]
> diff --git a/fs/bpf_fs_kfuncs.c b/fs/bpf_fs_kfuncs.c
> index 768aca2dc0f0..7abc3f3d1a67 100644
> --- a/fs/bpf_fs_kfuncs.c
> +++ b/fs/bpf_fs_kfuncs.c
[ ... ]
> diff --git a/include/linux/security.h b/include/linux/security.h
> index 153e9043058f..1f8e84e7dd7e 100644
> --- a/include/linux/security.h
> +++ b/include/linux/security.h
> @@ -68,6 +68,11 @@ struct watch;
> struct watch_notification;
> struct lsm_ctx;
>
> +struct xattr_ctx {
> + struct xattr *xattrs;
> + int *xattr_count;
> +};
> +
Paul Moore suggested in v2 renaming this to 'struct lsm_xattrs' to avoid
overloading the term 'ctx' in the LSM space. The struct was renamed from
'lsm_xattr_ctx' to 'xattr_ctx' in v3, but 'ctx' is still in the name. Was
there a reason to keep 'ctx' in the name, or would renaming to 'lsm_xattrs'
as Paul suggested be preferable?
> /* Default (no) options for the capable function */
> #define CAP_OPT_NONE 0x0
> /* If capable should audit the security request */
[ ... ]
> diff --git a/kernel/bpf/bpf_lsm.c b/kernel/bpf/bpf_lsm.c
> index 564071a92d7d..86a8e188b900 100644
> --- a/kernel/bpf/bpf_lsm.c
> +++ b/kernel/bpf/bpf_lsm.c
> @@ -113,6 +113,9 @@ void bpf_lsm_find_cgroup_shim(const struct bpf_prog *prog,
> }
> #endif
>
> +BTF_ID_LIST_SINGLE(bpf_lsm_inode_init_security_btf_ids, func,
> + bpf_lsm_inode_init_security)
> +
> int bpf_lsm_verify_prog(struct bpf_verifier_log *vlog,
> const struct bpf_prog *prog)
> {
> @@ -137,6 +140,12 @@ int bpf_lsm_verify_prog(struct bpf_verifier_log *vlog,
> return -EINVAL;
> }
>
> + /* bpf reserves a fixed number of xattr slots for itself.
> + * Set the attach limit so the trampoline rejects excess attaches.
> + */
This isn't a bug, but a subsystem pattern flags this as potentially
concerning: the BPF subsystem comment-style rule for files under kernel/bpf/
requires multi-line comments to have the opening /* on its own line, with
comment text starting on the next line. The preferred form would be:
/*
* bpf reserves a fixed number of xattr slots for itself.
* Set the attach limit so the trampoline rejects excess attaches.
*/
> + if (btf_id == bpf_lsm_inode_init_security_btf_ids[0])
> + prog->aux->attach_limit = BPF_LSM_INODE_INIT_XATTRS;
> +
> return 0;
> }
>
[ ... ]
---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
CI run summary: https://github.com/kernel-patches/bpf/actions/runs/27788616397
^ permalink raw reply
* [PATCH bpf-next v3 2/2] selftests/bpf: add tests for bpf_init_inode_xattr kfunc
From: David Windsor @ 2026-06-18 20:34 UTC (permalink / raw)
To: viro, brauner, jack, ast, daniel, john.fastabend, andrii, eddyz87,
memxor, martin.lau, song, yonghong.song, jolsa, emil, kpsingh,
mattbobrowski, paul, jmorris, serge, zohar, roberto.sassu,
dmitry.kasatkin, eric.snowberg, stephen.smalley.work, omosnace,
casey, shuah
Cc: linux-kernel, linux-fsdevel, bpf, linux-security-module,
linux-integrity, selinux, linux-kselftest, David Windsor
In-Reply-To: <20260618203411.73917-1-dwindsor@gmail.com>
Test bpf atomic inode xattr labeling in inode_init_security.
Signed-off-by: David Windsor <dwindsor@gmail.com>
---
tools/testing/selftests/bpf/bpf_kfuncs.h | 5 +
.../selftests/bpf/prog_tests/fs_kfuncs.c | 105 +++++++++++++++++-
.../bpf/progs/test_init_inode_xattr.c | 31 ++++++
3 files changed, 140 insertions(+), 1 deletion(-)
create mode 100644 tools/testing/selftests/bpf/progs/test_init_inode_xattr.c
diff --git a/tools/testing/selftests/bpf/bpf_kfuncs.h b/tools/testing/selftests/bpf/bpf_kfuncs.h
index ae71e9b69051..69d3641ee2d8 100644
--- a/tools/testing/selftests/bpf/bpf_kfuncs.h
+++ b/tools/testing/selftests/bpf/bpf_kfuncs.h
@@ -92,4 +92,9 @@ extern int bpf_set_dentry_xattr(struct dentry *dentry, const char *name__str,
const struct bpf_dynptr *value_p, int flags) __ksym __weak;
extern int bpf_remove_dentry_xattr(struct dentry *dentry, const char *name__str) __ksym __weak;
+struct xattr_ctx;
+extern int bpf_init_inode_xattr(struct xattr_ctx *xattr_ctx,
+ const char *name__str,
+ const struct bpf_dynptr *value_p) __ksym __weak;
+
#endif
diff --git a/tools/testing/selftests/bpf/prog_tests/fs_kfuncs.c b/tools/testing/selftests/bpf/prog_tests/fs_kfuncs.c
index 43a26ec69a8e..0898898fb125 100644
--- a/tools/testing/selftests/bpf/prog_tests/fs_kfuncs.c
+++ b/tools/testing/selftests/bpf/prog_tests/fs_kfuncs.c
@@ -9,9 +9,10 @@
#include <test_progs.h>
#include "test_get_xattr.skel.h"
#include "test_set_remove_xattr.skel.h"
+#include "test_init_inode_xattr.skel.h"
#include "test_fsverity.skel.h"
-static const char testfile[] = "/tmp/test_progs_fs_kfuncs";
+static const char testfile[] = "/tmp/labelme";
static void test_get_xattr(const char *name, const char *value, bool allow_access)
{
@@ -268,6 +269,102 @@ static void test_fsverity(void)
remove(testfile);
}
+static void test_init_inode_xattr(void)
+{
+ struct test_init_inode_xattr *skel = NULL;
+ int fd = -1, err;
+ char value_out[64];
+ const char *testfile_new = "/tmp/test_progs_fs_kfuncs_new";
+
+ skel = test_init_inode_xattr__open_and_load();
+ if (!ASSERT_OK_PTR(skel, "test_init_inode_xattr__open_and_load"))
+ return;
+
+ skel->bss->monitored_pid = getpid();
+ err = test_init_inode_xattr__attach(skel);
+ if (!ASSERT_OK(err, "test_init_inode_xattr__attach"))
+ goto out;
+
+ /* Trigger inode_init_security */
+ fd = open(testfile_new, O_CREAT | O_RDWR, 0644);
+ if (!ASSERT_GE(fd, 0, "create_file"))
+ goto out;
+
+ ASSERT_EQ(skel->data->init_result, 0, "init_result");
+
+ /* initxattrs prepends "security." to the name. */
+ err = getxattr(testfile_new, "security.bpf.test_label", value_out,
+ sizeof(value_out));
+ if (err < 0 && errno == ENODATA) {
+ printf("%s:SKIP:filesystem did not apply LSM xattrs\n",
+ __func__);
+ test__skip();
+ goto out;
+ }
+ if (!ASSERT_GE(err, 0, "getxattr"))
+ goto out;
+
+ ASSERT_EQ(err, (int)sizeof(skel->data->xattr_value), "xattr_size");
+ ASSERT_EQ(strncmp(value_out, "unconfined_u:object_r:user_home_t:s0",
+ sizeof("unconfined_u:object_r:user_home_t:s0")), 0,
+ "xattr_value");
+
+out:
+ close(fd);
+ test_init_inode_xattr__destroy(skel);
+ remove(testfile_new);
+}
+
+/* Keep in sync with BPF_LSM_INODE_INIT_XATTRS in include/linux/bpf_lsm.h. */
+#define INIT_INODE_XATTR_MAX 4
+
+/* At most INIT_INODE_XATTR_MAX programs can attach to inode_init_security. */
+static void test_init_inode_xattr_attach_cap(void)
+{
+ struct test_init_inode_xattr *skel[INIT_INODE_XATTR_MAX + 1] = {};
+ struct bpf_link *link[INIT_INODE_XATTR_MAX + 1] = {};
+ struct bpf_link *extra = NULL;
+ int i, err;
+
+ /* Fill all available xattr slots */
+ for (i = 0; i < INIT_INODE_XATTR_MAX; i++) {
+ skel[i] = test_init_inode_xattr__open_and_load();
+ if (!ASSERT_OK_PTR(skel[i], "open_and_load"))
+ goto out;
+
+ link[i] = bpf_program__attach_lsm(skel[i]->progs.test_init_inode_xattr);
+ if (!ASSERT_OK_PTR(link[i], "attach_within_cap"))
+ goto out;
+ }
+
+ skel[INIT_INODE_XATTR_MAX] = test_init_inode_xattr__open_and_load();
+ if (!ASSERT_OK_PTR(skel[INIT_INODE_XATTR_MAX], "open_and_load_extra"))
+ goto out;
+
+ /* New additions fail with -E2BIG */
+ extra = bpf_program__attach_lsm(skel[INIT_INODE_XATTR_MAX]->progs.test_init_inode_xattr);
+ err = -errno;
+ if (!ASSERT_ERR_PTR(extra, "attach_over_cap_should_fail")) {
+ bpf_link__destroy(extra);
+ goto out;
+ }
+ ASSERT_EQ(err, -E2BIG, "attach_over_cap_errno");
+
+ bpf_link__destroy(link[0]);
+ link[0] = NULL; /* avoid double free in cleanup */
+
+ /* Freeing a slot lets the extra program attach */
+ extra = bpf_program__attach_lsm(skel[INIT_INODE_XATTR_MAX]->progs.test_init_inode_xattr);
+ ASSERT_OK_PTR(extra, "attach_after_detach");
+
+out:
+ bpf_link__destroy(extra);
+ for (i = 0; i <= INIT_INODE_XATTR_MAX; i++) {
+ bpf_link__destroy(link[i]);
+ test_init_inode_xattr__destroy(skel[i]);
+ }
+}
+
void test_fs_kfuncs(void)
{
/* Matches xattr_names in progs/test_get_xattr.c */
@@ -286,6 +383,12 @@ void test_fs_kfuncs(void)
if (test__start_subtest("set_remove_xattr"))
test_set_remove_xattr();
+ if (test__start_subtest("init_inode_xattr"))
+ test_init_inode_xattr();
+
+ if (test__start_subtest("init_inode_xattr_attach_cap"))
+ test_init_inode_xattr_attach_cap();
+
if (test__start_subtest("fsverity"))
test_fsverity();
}
diff --git a/tools/testing/selftests/bpf/progs/test_init_inode_xattr.c b/tools/testing/selftests/bpf/progs/test_init_inode_xattr.c
new file mode 100644
index 000000000000..6f0e8b02ff88
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/test_init_inode_xattr.c
@@ -0,0 +1,31 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2026 Cisco Systems, Inc. */
+
+#include "vmlinux.h"
+#include <bpf/bpf_tracing.h>
+#include "bpf_kfuncs.h"
+
+char _license[] SEC("license") = "GPL";
+
+__u32 monitored_pid;
+int init_result = -1;
+
+static const char xattr_name[] = "bpf.test_label";
+char xattr_value[] = "unconfined_u:object_r:user_home_t:s0";
+
+SEC("lsm.s/inode_init_security")
+int BPF_PROG(test_init_inode_xattr, struct inode *inode, struct inode *dir,
+ const struct qstr *qstr, struct xattr_ctx *xattr_ctx)
+{
+ struct bpf_dynptr value_ptr;
+ __u32 pid;
+
+ pid = bpf_get_current_pid_tgid() >> 32;
+ if (pid != monitored_pid)
+ return 0;
+
+ bpf_dynptr_from_mem(xattr_value, sizeof(xattr_value), 0, &value_ptr);
+ init_result = bpf_init_inode_xattr(xattr_ctx, xattr_name, &value_ptr);
+
+ return 0;
+}
--
2.53.0
^ permalink raw reply related
* [PATCH bpf-next v3 1/2] bpf: add bpf_init_inode_xattr kfunc for atomic inode labeling
From: David Windsor @ 2026-06-18 20:34 UTC (permalink / raw)
To: viro, brauner, jack, ast, daniel, john.fastabend, andrii, eddyz87,
memxor, martin.lau, song, yonghong.song, jolsa, emil, kpsingh,
mattbobrowski, paul, jmorris, serge, zohar, roberto.sassu,
dmitry.kasatkin, eric.snowberg, stephen.smalley.work, omosnace,
casey, shuah
Cc: linux-kernel, linux-fsdevel, bpf, linux-security-module,
linux-integrity, selinux, linux-kselftest, David Windsor
In-Reply-To: <20260618203411.73917-1-dwindsor@gmail.com>
Add bpf_init_inode_xattr() kfunc for BPF LSM programs to atomically set
xattrs via the inode_init_security hook using lsm_get_xattr_slot().
The inode_init_security hook previously took the xattr array and count
as two separate output parameters (struct xattr *xattrs, int
*xattr_count), which BPF programs cannot write to. Pass the xattr state
as a single context object (struct xattr_ctx) instead, and have
bpf_init_inode_xattr() take that context directly. Update the existing
in-tree callers of inode_init_security to take and forward the new
xattr_ctx.
A previous attempt [1] required a kmalloc string output protocol for
the xattr name. Since commit 6bcdfd2cac55 ("security: Allow all LSMs to
provide xattrs for inode_init_security hook") [2], the xattr name is no
longer allocated; it is a static constant.
Because we rely on the hook-specific ctx layout, the kfunc is
restricted to lsm/inode_init_security. Restrict the xattr names that
may be set via this kfunc to the bpf.* namespace.
Link: https://kernsec.org/pipermail/linux-security-module-archive/2022-October/034878.html [1]
Link: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=6bcdfd2cac55 [2]
Suggested-by: Song Liu <song@kernel.org>
Signed-off-by: David Windsor <dwindsor@gmail.com>
---
fs/bpf_fs_kfuncs.c | 106 +++++++++++++++++++++++++++++-
include/linux/bpf.h | 1 +
include/linux/bpf_lsm.h | 3 +
include/linux/evm.h | 9 +--
include/linux/lsm_hook_defs.h | 4 +-
include/linux/lsm_hooks.h | 16 ++---
include/linux/security.h | 5 ++
kernel/bpf/bpf_lsm.c | 10 +++
kernel/bpf/trampoline.c | 3 +
security/bpf/hooks.c | 1 +
security/integrity/evm/evm_main.c | 8 ++-
security/security.c | 7 +-
security/selinux/hooks.c | 4 +-
security/smack/smack_lsm.c | 27 ++++----
14 files changed, 166 insertions(+), 38 deletions(-)
diff --git a/fs/bpf_fs_kfuncs.c b/fs/bpf_fs_kfuncs.c
index 768aca2dc0f0..7abc3f3d1a67 100644
--- a/fs/bpf_fs_kfuncs.c
+++ b/fs/bpf_fs_kfuncs.c
@@ -10,6 +10,7 @@
#include <linux/fsnotify.h>
#include <linux/file.h>
#include <linux/kernfs.h>
+#include <linux/lsm_hooks.h>
#include <linux/mm.h>
#include <linux/xattr.h>
@@ -374,6 +375,97 @@ __bpf_kfunc struct inode *bpf_real_inode(struct dentry *dentry)
return d_real_inode(dentry);
}
+static int bpf_xattrs_used(const struct xattr_ctx *ctx)
+{
+ const size_t prefix_len = sizeof(XATTR_BPF_LSM_SUFFIX) - 1;
+ int i, n = 0;
+
+ for (i = 0; i < *ctx->xattr_count; i++) {
+ const char *name = ctx->xattrs[i].name;
+
+ if (name && !strncmp(name, XATTR_BPF_LSM_SUFFIX, prefix_len))
+ n++;
+ }
+ return n;
+}
+
+static int __bpf_init_inode_xattr(struct xattr_ctx *xattr_ctx,
+ const char *name__str,
+ const struct bpf_dynptr *value_p)
+{
+ struct bpf_dynptr_kern *value_ptr = (struct bpf_dynptr_kern *)value_p;
+ size_t name_len;
+ void *xattr_value;
+ struct xattr *xattr;
+ struct xattr *xattrs;
+ int *xattr_count;
+ const void *value;
+ u32 value_len;
+
+ if (!xattr_ctx || !name__str)
+ return -EINVAL;
+
+ xattrs = xattr_ctx->xattrs;
+ xattr_count = xattr_ctx->xattr_count;
+ if (!xattrs || !xattr_count)
+ return -EINVAL;
+ if (bpf_xattrs_used(xattr_ctx) >= BPF_LSM_INODE_INIT_XATTRS)
+ return -ENOSPC;
+
+ name_len = strlen(name__str);
+ if (name_len == 0 || name_len > XATTR_NAME_MAX)
+ return -EINVAL;
+ if (strncmp(name__str, XATTR_BPF_LSM_SUFFIX,
+ sizeof(XATTR_BPF_LSM_SUFFIX) - 1))
+ return -EPERM;
+
+ value_len = __bpf_dynptr_size(value_ptr);
+ if (value_len == 0 || value_len > XATTR_SIZE_MAX)
+ return -EINVAL;
+
+ value = __bpf_dynptr_data(value_ptr, value_len);
+ if (!value)
+ return -EINVAL;
+
+ /* Combine xattr value + name into one allocation. */
+ xattr_value = kmalloc(value_len + name_len + 1, GFP_KERNEL);
+ if (!xattr_value)
+ return -ENOMEM;
+
+ memcpy(xattr_value, value, value_len);
+ memcpy(xattr_value + value_len, name__str, name_len);
+ ((char *)xattr_value)[value_len + name_len] = '\0';
+
+ xattr = lsm_get_xattr_slot(xattr_ctx);
+ if (!xattr) {
+ kfree(xattr_value);
+ return -ENOSPC;
+ }
+
+ xattr->value = xattr_value;
+ xattr->name = (const char *)xattr_value + value_len;
+ xattr->value_len = value_len;
+
+ return 0;
+}
+
+/**
+ * bpf_init_inode_xattr - set an xattr on a new inode from inode_init_security
+ * @xattr_ctx: inode_init_security xattr state from the hook context
+ * @name__str: xattr name (e.g., "bpf.file_label")
+ * @value_p: dynptr containing the xattr value
+ *
+ * Only callable from lsm/inode_init_security programs.
+ *
+ * Return: 0 on success, negative error on failure.
+ */
+__bpf_kfunc int bpf_init_inode_xattr(struct xattr_ctx *xattr_ctx,
+ const char *name__str,
+ const struct bpf_dynptr *value_p)
+{
+ return __bpf_init_inode_xattr(xattr_ctx, name__str, value_p);
+}
+
__bpf_kfunc_end_defs();
BTF_KFUNCS_START(bpf_fs_kfunc_set_ids)
@@ -385,13 +477,25 @@ BTF_ID_FLAGS(func, bpf_get_file_xattr, KF_SLEEPABLE)
BTF_ID_FLAGS(func, bpf_set_dentry_xattr, KF_SLEEPABLE)
BTF_ID_FLAGS(func, bpf_remove_dentry_xattr, KF_SLEEPABLE)
BTF_ID_FLAGS(func, bpf_real_inode, KF_SLEEPABLE | KF_RET_NULL)
+BTF_ID_FLAGS(func, bpf_init_inode_xattr, KF_SLEEPABLE)
BTF_KFUNCS_END(bpf_fs_kfunc_set_ids)
+BTF_ID_LIST(bpf_lsm_inode_init_security_btf_ids)
+BTF_ID(func, bpf_lsm_inode_init_security)
+
+BTF_ID_LIST(bpf_init_inode_xattr_btf_ids)
+BTF_ID(func, bpf_init_inode_xattr)
+
static int bpf_fs_kfuncs_filter(const struct bpf_prog *prog, u32 kfunc_id)
{
if (!btf_id_set8_contains(&bpf_fs_kfunc_set_ids, kfunc_id) ||
- prog->type == BPF_PROG_TYPE_LSM)
+ prog->type == BPF_PROG_TYPE_LSM) {
+ /* bpf_init_inode_xattr only attaches to inode_init_security. */
+ if (kfunc_id == bpf_init_inode_xattr_btf_ids[0] &&
+ prog->aux->attach_btf_id != bpf_lsm_inode_init_security_btf_ids[0])
+ return -EACCES;
return 0;
+ }
return -EACCES;
}
diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 7719f6528445..f14bfcda78db 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -1752,6 +1752,7 @@ struct bpf_prog_aux {
u32 real_func_cnt; /* includes hidden progs, only used for JIT and freeing progs */
u32 func_idx; /* 0 for non-func prog, the index in func array for func prog */
u32 attach_btf_id; /* in-kernel BTF type id to attach to */
+ u32 attach_limit; /* max concurrent attachments (0 = unlimited) */
u32 attach_st_ops_member_off;
u32 ctx_arg_info_size;
u32 max_rdonly_access;
diff --git a/include/linux/bpf_lsm.h b/include/linux/bpf_lsm.h
index 143775a27a2a..b655c708818e 100644
--- a/include/linux/bpf_lsm.h
+++ b/include/linux/bpf_lsm.h
@@ -19,6 +19,9 @@
#include <linux/lsm_hook_defs.h>
#undef LSM_HOOK
+/* max bpf xattrs per inode */
+#define BPF_LSM_INODE_INIT_XATTRS 4
+
struct bpf_storage_blob {
struct bpf_local_storage __rcu *storage;
};
diff --git a/include/linux/evm.h b/include/linux/evm.h
index 913f4573b203..0aa151288b36 100644
--- a/include/linux/evm.h
+++ b/include/linux/evm.h
@@ -12,6 +12,8 @@
#include <linux/integrity.h>
#include <linux/xattr.h>
+struct xattr_ctx;
+
#ifdef CONFIG_EVM
extern int evm_set_key(void *key, size_t keylen);
extern enum integrity_status evm_verifyxattr(struct dentry *dentry,
@@ -21,8 +23,8 @@ extern enum integrity_status evm_verifyxattr(struct dentry *dentry,
int evm_fix_hmac(struct dentry *dentry, const char *xattr_name,
const char *xattr_value, size_t xattr_value_len);
int evm_inode_init_security(struct inode *inode, struct inode *dir,
- const struct qstr *qstr, struct xattr *xattrs,
- int *xattr_count);
+ const struct qstr *qstr,
+ struct xattr_ctx *xattr_ctx);
extern bool evm_revalidate_status(const char *xattr_name);
extern int evm_protected_xattr_if_enabled(const char *req_xattr_name);
extern int evm_read_protected_xattrs(struct dentry *dentry, u8 *buffer,
@@ -63,8 +65,7 @@ static inline int evm_fix_hmac(struct dentry *dentry, const char *xattr_name,
static inline int evm_inode_init_security(struct inode *inode, struct inode *dir,
const struct qstr *qstr,
- struct xattr *xattrs,
- int *xattr_count)
+ struct xattr_ctx *xattr_ctx)
{
return 0;
}
diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
index 65c9609ec207..f62780fbeb9e 100644
--- a/include/linux/lsm_hook_defs.h
+++ b/include/linux/lsm_hook_defs.h
@@ -116,8 +116,8 @@ LSM_HOOK(int, 0, inode_alloc_security, struct inode *inode)
LSM_HOOK(void, LSM_RET_VOID, inode_free_security, struct inode *inode)
LSM_HOOK(void, LSM_RET_VOID, inode_free_security_rcu, void *inode_security)
LSM_HOOK(int, -EOPNOTSUPP, inode_init_security, struct inode *inode,
- struct inode *dir, const struct qstr *qstr, struct xattr *xattrs,
- int *xattr_count)
+ struct inode *dir, const struct qstr *qstr,
+ struct xattr_ctx *xattr_ctx)
LSM_HOOK(int, 0, inode_init_security_anon, struct inode *inode,
const struct qstr *name, const struct inode *context_inode)
LSM_HOOK(int, 0, inode_create, struct inode *dir, struct dentry *dentry,
diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
index b4f8cad53ddb..710e48caaeba 100644
--- a/include/linux/lsm_hooks.h
+++ b/include/linux/lsm_hooks.h
@@ -200,20 +200,18 @@ extern struct lsm_static_calls_table static_calls_table __ro_after_init;
/**
* lsm_get_xattr_slot - Return the next available slot and increment the index
- * @xattrs: array storing LSM-provided xattrs
- * @xattr_count: number of already stored xattrs (updated)
+ * @ctx: xattr state shared by inode_init_security hooks
*
- * Retrieve the first available slot in the @xattrs array to fill with an xattr,
- * and increment @xattr_count.
+ * Retrieve the first available slot in the @ctx->xattrs array to fill with an
+ * xattr, and increment @ctx->xattr_count.
*
- * Return: The slot to fill in @xattrs if non-NULL, NULL otherwise.
+ * Return: The slot to fill in @ctx->xattrs if non-NULL, NULL otherwise.
*/
-static inline struct xattr *lsm_get_xattr_slot(struct xattr *xattrs,
- int *xattr_count)
+static inline struct xattr *lsm_get_xattr_slot(struct xattr_ctx *ctx)
{
- if (unlikely(!xattrs))
+ if (unlikely(!ctx || !ctx->xattrs || !ctx->xattr_count))
return NULL;
- return &xattrs[(*xattr_count)++];
+ return &ctx->xattrs[(*ctx->xattr_count)++];
}
#endif /* ! __LINUX_LSM_HOOKS_H */
diff --git a/include/linux/security.h b/include/linux/security.h
index 153e9043058f..1f8e84e7dd7e 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -68,6 +68,11 @@ struct watch;
struct watch_notification;
struct lsm_ctx;
+struct xattr_ctx {
+ struct xattr *xattrs;
+ int *xattr_count;
+};
+
/* Default (no) options for the capable function */
#define CAP_OPT_NONE 0x0
/* If capable should audit the security request */
diff --git a/kernel/bpf/bpf_lsm.c b/kernel/bpf/bpf_lsm.c
index 564071a92d7d..86a8e188b900 100644
--- a/kernel/bpf/bpf_lsm.c
+++ b/kernel/bpf/bpf_lsm.c
@@ -113,6 +113,9 @@ void bpf_lsm_find_cgroup_shim(const struct bpf_prog *prog,
}
#endif
+BTF_ID_LIST_SINGLE(bpf_lsm_inode_init_security_btf_ids, func,
+ bpf_lsm_inode_init_security)
+
int bpf_lsm_verify_prog(struct bpf_verifier_log *vlog,
const struct bpf_prog *prog)
{
@@ -137,6 +140,12 @@ int bpf_lsm_verify_prog(struct bpf_verifier_log *vlog,
return -EINVAL;
}
+ /* bpf reserves a fixed number of xattr slots for itself.
+ * Set the attach limit so the trampoline rejects excess attaches.
+ */
+ if (btf_id == bpf_lsm_inode_init_security_btf_ids[0])
+ prog->aux->attach_limit = BPF_LSM_INODE_INIT_XATTRS;
+
return 0;
}
@@ -315,6 +324,7 @@ BTF_ID(func, bpf_lsm_inode_create)
BTF_ID(func, bpf_lsm_inode_free_security)
BTF_ID(func, bpf_lsm_inode_getattr)
BTF_ID(func, bpf_lsm_inode_getxattr)
+BTF_ID(func, bpf_lsm_inode_init_security)
BTF_ID(func, bpf_lsm_inode_mknod)
BTF_ID(func, bpf_lsm_inode_need_killpriv)
BTF_ID(func, bpf_lsm_inode_post_setxattr)
diff --git a/kernel/bpf/trampoline.c b/kernel/bpf/trampoline.c
index 1a721fc4bef5..b41b02173e24 100644
--- a/kernel/bpf/trampoline.c
+++ b/kernel/bpf/trampoline.c
@@ -859,6 +859,9 @@ static int bpf_trampoline_add_prog(struct bpf_trampoline *tr,
}
if (cnt >= BPF_MAX_TRAMP_LINKS)
return -E2BIG;
+ if (node->link->prog->aux->attach_limit &&
+ tr->progs_cnt[kind] >= node->link->prog->aux->attach_limit)
+ return -E2BIG;
if (!hlist_unhashed(&node->tramp_hlist))
/* prog already linked */
return -EBUSY;
diff --git a/security/bpf/hooks.c b/security/bpf/hooks.c
index 40efde233f3a..d7c44c5c0e30 100644
--- a/security/bpf/hooks.c
+++ b/security/bpf/hooks.c
@@ -30,6 +30,7 @@ static int __init bpf_lsm_init(void)
struct lsm_blob_sizes bpf_lsm_blob_sizes __ro_after_init = {
.lbs_inode = sizeof(struct bpf_storage_blob),
+ .lbs_xattr_count = BPF_LSM_INODE_INIT_XATTRS,
};
DEFINE_LSM(bpf) = {
diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c
index b59e3f121b8a..e0a05162accc 100644
--- a/security/integrity/evm/evm_main.c
+++ b/security/integrity/evm/evm_main.c
@@ -1062,14 +1062,16 @@ static int evm_inode_copy_up_xattr(struct dentry *src, const char *name)
* evm_inode_init_security - initializes security.evm HMAC value
*/
int evm_inode_init_security(struct inode *inode, struct inode *dir,
- const struct qstr *qstr, struct xattr *xattrs,
- int *xattr_count)
+ const struct qstr *qstr,
+ struct xattr_ctx *xattr_ctx)
{
struct evm_xattr *xattr_data;
struct xattr *xattr, *evm_xattr;
+ struct xattr *xattrs;
bool evm_protected_xattrs = false;
int rc;
+ xattrs = xattr_ctx ? xattr_ctx->xattrs : NULL;
if (!(evm_initialized & EVM_INIT_HMAC) || !xattrs)
return 0;
@@ -1087,7 +1089,7 @@ int evm_inode_init_security(struct inode *inode, struct inode *dir,
if (!evm_protected_xattrs)
return 0;
- evm_xattr = lsm_get_xattr_slot(xattrs, xattr_count);
+ evm_xattr = lsm_get_xattr_slot(xattr_ctx);
/*
* Array terminator (xattr name = NULL) must be the first non-filled
* xattr slot.
diff --git a/security/security.c b/security/security.c
index 71aea8fdf014..8f82a1352356 100644
--- a/security/security.c
+++ b/security/security.c
@@ -1334,6 +1334,7 @@ int security_inode_init_security(struct inode *inode, struct inode *dir,
{
struct lsm_static_call *scall;
struct xattr *new_xattrs = NULL;
+ struct xattr_ctx xattr_ctx;
int ret = -EOPNOTSUPP, xattr_count = 0;
if (unlikely(IS_PRIVATE(inode)))
@@ -1349,10 +1350,12 @@ int security_inode_init_security(struct inode *inode, struct inode *dir,
if (!new_xattrs)
return -ENOMEM;
}
+ xattr_ctx.xattrs = new_xattrs;
+ xattr_ctx.xattr_count = &xattr_count;
lsm_for_each_hook(scall, inode_init_security) {
- ret = scall->hl->hook.inode_init_security(inode, dir, qstr, new_xattrs,
- &xattr_count);
+ ret = scall->hl->hook.inode_init_security(inode, dir, qstr,
+ &xattr_ctx);
if (ret && ret != -EOPNOTSUPP)
goto out;
/*
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 1a713d96206f..faa8a6b9c45b 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -2962,7 +2962,7 @@ static int selinux_dentry_create_files_as(struct dentry *dentry, int mode,
static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
const struct qstr *qstr,
- struct xattr *xattrs, int *xattr_count)
+ struct xattr_ctx *xattr_ctx)
{
const struct cred_security_struct *crsec = selinux_cred(current_cred());
struct superblock_security_struct *sbsec;
@@ -2992,7 +2992,7 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
!(sbsec->flags & SBLABEL_MNT))
return -EOPNOTSUPP;
- xattr = lsm_get_xattr_slot(xattrs, xattr_count);
+ xattr = lsm_get_xattr_slot(xattr_ctx);
if (xattr) {
rc = security_sid_to_context_force(newsid,
&context, &clen);
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index ff115068c5c0..8ed5648a0116 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -981,10 +981,10 @@ smk_rule_transmutes(struct smack_known *subject,
}
static int
-xattr_dupval(struct xattr *xattrs, int *xattr_count,
+xattr_dupval(struct xattr_ctx *xattr_ctx,
const char *name, const void *value, unsigned int vallen)
{
- struct xattr * const xattr = lsm_get_xattr_slot(xattrs, xattr_count);
+ struct xattr * const xattr = lsm_get_xattr_slot(xattr_ctx);
if (!xattr)
return 0;
@@ -1003,14 +1003,13 @@ xattr_dupval(struct xattr *xattrs, int *xattr_count,
* @inode: the newly created inode
* @dir: containing directory object
* @qstr: unused
- * @xattrs: where to put the attributes
- * @xattr_count: current number of LSM-provided xattrs (updated)
+ * @xattr_ctx: where to put attributes and update count
*
* Returns 0 if it all works out, -ENOMEM if there's no memory
*/
static int smack_inode_init_security(struct inode *inode, struct inode *dir,
const struct qstr *qstr,
- struct xattr *xattrs, int *xattr_count)
+ struct xattr_ctx *xattr_ctx)
{
struct task_smack *tsp = smack_cred(current_cred());
struct inode_smack * const issp = smack_inode(inode);
@@ -1057,21 +1056,19 @@ static int smack_inode_init_security(struct inode *inode, struct inode *dir,
if (S_ISDIR(inode->i_mode)) {
transflag = SMK_INODE_TRANSMUTE;
- if (xattr_dupval(xattrs, xattr_count,
- XATTR_SMACK_TRANSMUTE,
- TRANS_TRUE,
- TRANS_TRUE_SIZE
- ))
+ if (xattr_dupval(xattr_ctx,
+ XATTR_SMACK_TRANSMUTE,
+ TRANS_TRUE,
+ TRANS_TRUE_SIZE))
rc = -ENOMEM;
}
}
if (rc == 0)
- if (xattr_dupval(xattrs, xattr_count,
- XATTR_SMACK_SUFFIX,
- issp->smk_inode->smk_known,
- strlen(issp->smk_inode->smk_known)
- ))
+ if (xattr_dupval(xattr_ctx,
+ XATTR_SMACK_SUFFIX,
+ issp->smk_inode->smk_known,
+ strlen(issp->smk_inode->smk_known)))
rc = -ENOMEM;
instant_inode:
issp->smk_flags |= (SMK_INODE_INSTANT | transflag);
--
2.53.0
^ permalink raw reply related
* [PATCH bpf-next v3 0/2] bpf: add bpf_init_inode_xattr kfunc for atomic inode labeling
From: David Windsor @ 2026-06-18 20:34 UTC (permalink / raw)
To: viro, brauner, jack, ast, daniel, john.fastabend, andrii, eddyz87,
memxor, martin.lau, song, yonghong.song, jolsa, emil, kpsingh,
mattbobrowski, paul, jmorris, serge, zohar, roberto.sassu,
dmitry.kasatkin, eric.snowberg, stephen.smalley.work, omosnace,
casey, shuah
Cc: linux-kernel, linux-fsdevel, bpf, linux-security-module,
linux-integrity, selinux, linux-kselftest, David Windsor
Many in-kernel LSMs (SELinux, Smack, IMA) store security labels in
extended attributes. For these LSMs, atomic labeling during inode
creation is critical: if the inode becomes accessible before its xattr
is set, it is briefly unlabeled, which can disrupt LSMs making policy
decisions based on file labels.
Existing LSMs solve this by setting xattrs directly in the
inode_init_security hook, which runs before the inode becomes
accessible. BPF LSM programs currently lack this capability because
the hook uses an output parameter (xattr_count) that BPF programs
cannot write to, and existing kfuncs like bpf_set_dentry_xattr
require a dentry that isn't available until after the inode is
accessible.
This series introduces the bpf_init_inode_xattr() kfunc, which takes
the combined inode_init_security xattr context argument to access
xattrs and xattr_count, and internally writes to xattr_count via
lsm_get_xattr_slot().
v3:
- rename struct lsm_xattr_ctx to struct xattr_ctx (Paul)
- increase BPF_LSM_INODE_INIT_XATTRS to 4 (Song)
- enforce per-hook attachment cap at attach time to prevent
runtime rejection (Paul)
- add init_inode_xattr_attach_cap selftest
v2:
- pass the xattr state as a combined context object and drop the
verifier fixup path (Kumar)
- restrict bpf_init_inode_xattr labels to bpf.* namespace (Matt)
- cap bpf_init_inode_xattr() at BPF_LSM_INODE_INIT_XATTRS slots per
invocation (AI)
Link: https://lore.kernel.org/all/20260503211835.16103-1-dwindsor@gmail.com/ [v2]
David Windsor (2):
bpf: add bpf_init_inode_xattr kfunc for atomic inode labeling
selftests/bpf: add tests for bpf_init_inode_xattr kfunc
fs/bpf_fs_kfuncs.c | 106 +++++++++++++++++-
include/linux/bpf.h | 1 +
include/linux/bpf_lsm.h | 3 +
include/linux/evm.h | 9 +-
include/linux/lsm_hook_defs.h | 4 +-
include/linux/lsm_hooks.h | 16 ++-
include/linux/security.h | 5 +
kernel/bpf/bpf_lsm.c | 10 ++
kernel/bpf/trampoline.c | 3 +
security/bpf/hooks.c | 1 +
security/integrity/evm/evm_main.c | 8 +-
security/security.c | 7 +-
security/selinux/hooks.c | 4 +-
security/smack/smack_lsm.c | 27 ++---
tools/testing/selftests/bpf/bpf_kfuncs.h | 5 +
.../selftests/bpf/prog_tests/fs_kfuncs.c | 105 ++++++++++++++++-
.../bpf/progs/test_init_inode_xattr.c | 31 +++++
17 files changed, 306 insertions(+), 39 deletions(-)
create mode 100644 tools/testing/selftests/bpf/progs/test_init_inode_xattr.c
base-commit: e771677c937da5808f7b6c1f0e4a97ec1a84f8a8
--
2.53.0
^ permalink raw reply
* [GIT PULL] integrity: subsystem fixes for v7.2
From: Mimi Zohar @ 2026-06-18 14:58 UTC (permalink / raw)
To: Linus Torvalds; +Cc: linux-integrity, linux-kernel, Roberto Sassu
[Resending pull request with minor wording tweaks.]
Hi Linus,
There are 2 main changes, some code cleanup, and a couple of bug fixes.
Main changes:
- Introduce IMA and EVM post-quantum ML-DSA signature support
ML-DSA signature support for IMA and EVM is limited to sigv3 signatures, which
calculates and verifies a hash of a compact structure containing the file
data/metadata hash, hash type, and hash algorithm. IMA and EVM still calculate
the file data/metadata hashes respectively.
- Introduce support for removing IMA measurement list records stored in kernel
memory
The IMA measurement list can grow large depending on policy, but removing
records breaks remote attestation, unless they are safely preserved and made
available for attestation requests. Until environments are prepared to preserve
the measurement records, a new CONFIG_IMA_STAGING Kconfig option is introduced
to guard against deletion.
Several approaches for removing measurement list records were evaluated but
rejected due to filesystem constraints, the introduction of a new critical data
record, and locking concerns. Two methods are being upstreamed: staged deletion
with confirmation, and staged deletion of N records without confirmation. Both
methods minimize the period during which new measurements are blocked from being
appended to the measurement list by staging the measurement list.` A comparison
of the two methods is included in the documentation.
Thanks,
Mimi
The following changes since commit 254f49634ee16a731174d2ae34bc50bd5f45e731:
Linux 7.1-rc1 (2026-04-26 14:19:00 -0700)
are available in the Git repository at:
https://git.kernel.org/pub/scm/linux/kernel/git/zohar/linux-integrity.git/ tags/integrity-v7.2
for you to fetch changes up to 35d6f5e788dae0dcc4c42d1280360f19aef9ab52:
doc: security: Add documentation of exporting and deleting IMA measurements (2026-06-08 11:43:36 -0400)
----------------------------------------------------------------
integrity-v7.2
----------------------------------------------------------------
Goldwyn Rodrigues (1):
ima: return error early if file xattr cannot be changed
Kamlesh Kumar (1):
ima: Fix sigv3 signature handling for EVM_IMA_XATTR_DIGSIG
Pengpeng Hou (1):
evm: terminate and bound the evm_xattrs read buffer
Roberto Sassu (12):
ima: Remove ima_h_table structure
ima: Replace static htable queue with dynamically allocated array
ima: Introduce per binary measurements list type ima_num_records counter
ima: Introduce per binary measurements list type binary_runtime_size value
ima: Introduce _ima_measurements_start() and _ima_measurements_next()
ima: Mediate open/release method of the measurements list
ima: Use snprintf() in create_securityfs_measurement_lists
ima: Introduce ima_dump_measurement()
ima: Add support for staging measurements with prompt
ima: Add support for flushing the hash table when staging measurements
ima: Support staging and deleting N measurements records
doc: security: Add documentation of exporting and deleting IMA measurements
Stefan Berger (4):
integrity: Check for NULL returned by asymmetric_key_public_key
integrity: Check that algo parameter is within valid range
integrity: Refactor asymmetric_verify for reusability
integrity: Add support for sigv3 verification using ML-DSA keys
Documentation/admin-guide/kernel-parameters.txt | 6 +
Documentation/security/IMA-export-delete.rst | 203 ++++++++++++++
Documentation/security/index.rst | 1 +
MAINTAINERS | 2 +
security/integrity/digsig_asymmetric.c | 152 +++++++++--
security/integrity/evm/evm_secfs.c | 16 +-
security/integrity/ima/Kconfig | 15 +
security/integrity/ima/ima.h | 28 +-
security/integrity/ima/ima_api.c | 2 +-
security/integrity/ima/ima_appraise.c | 10 +-
security/integrity/ima/ima_fs.c | 346 +++++++++++++++++++++---
security/integrity/ima/ima_init.c | 5 +
security/integrity/ima/ima_kexec.c | 42 ++-
security/integrity/ima/ima_policy.c | 3 +-
security/integrity/ima/ima_queue.c | 327 ++++++++++++++++++++--
15 files changed, 1057 insertions(+), 101 deletions(-)
create mode 100644 Documentation/security/IMA-export-delete.rst
^ permalink raw reply
* [GIT PULL] integrity: subsystem fixes for v7.2
From: Mimi Zohar @ 2026-06-18 14:06 UTC (permalink / raw)
To: Linus Torvalds; +Cc: linux-integrity, linux-kernel, Roberto Sassu
Hi Linus,
There are 2 main changes, some code cleanup, and a couple of bug fixes.
Main changes:
- Introduce IMA and EVM post-quantum ML-DSA signature support
ML-DSA signature support for IMA and EVM is limited to sigv3 signatures, which
signs a hash of a compact structure containing the file data/metadata hash, hash
type, and hash algorithm. IMA and EVM still calculate the file data/metadata
hashes respectively.
- Introduce support for removing IMA measurement list records stored in kernel
memory
The IMA measurement list can grow large depending on policy, but removing
records breaks remote attestation, unless they are safely preserved and made
available for attestation requests. Until environments are prepared to preserve
the measurement records, a new CONFIG_IMA_STAGING Kconfig option is introduced
to guard against deletion.
Several approaches for removing measurement list records were evaluated but
rejected due to filesystem constraints, the introduction of a new critical data
record, and locking concerns. Two methods are being upstreamed: staged deletion
with confirmation, and staged deletion of N records without confirmation. Both
methods reduce the period during which new measurements are blocked from being
appended to the measurement list by staging the measurement list.` A comparison
of the two methods is included in the documentation.
Thanks,
Mimi
The following changes since commit 254f49634ee16a731174d2ae34bc50bd5f45e731:
Linux 7.1-rc1 (2026-04-26 14:19:00 -0700)
are available in the Git repository at:
https://git.kernel.org/pub/scm/linux/kernel/git/zohar/linux-integrity.git/ tags/integrity-v7.2
for you to fetch changes up to 35d6f5e788dae0dcc4c42d1280360f19aef9ab52:
doc: security: Add documentation of exporting and deleting IMA measurements (2026-06-08 11:43:36 -0400)
----------------------------------------------------------------
integrity-v7.2
----------------------------------------------------------------
Goldwyn Rodrigues (1):
ima: return error early if file xattr cannot be changed
Kamlesh Kumar (1):
ima: Fix sigv3 signature handling for EVM_IMA_XATTR_DIGSIG
Pengpeng Hou (1):
evm: terminate and bound the evm_xattrs read buffer
Roberto Sassu (12):
ima: Remove ima_h_table structure
ima: Replace static htable queue with dynamically allocated array
ima: Introduce per binary measurements list type ima_num_records counter
ima: Introduce per binary measurements list type binary_runtime_size value
ima: Introduce _ima_measurements_start() and _ima_measurements_next()
ima: Mediate open/release method of the measurements list
ima: Use snprintf() in create_securityfs_measurement_lists
ima: Introduce ima_dump_measurement()
ima: Add support for staging measurements with prompt
ima: Add support for flushing the hash table when staging measurements
ima: Support staging and deleting N measurements records
doc: security: Add documentation of exporting and deleting IMA measurements
Stefan Berger (4):
integrity: Check for NULL returned by asymmetric_key_public_key
integrity: Check that algo parameter is within valid range
integrity: Refactor asymmetric_verify for reusability
integrity: Add support for sigv3 verification using ML-DSA keys
Documentation/admin-guide/kernel-parameters.txt | 6 +
Documentation/security/IMA-export-delete.rst | 203 ++++++++++++++
Documentation/security/index.rst | 1 +
MAINTAINERS | 2 +
security/integrity/digsig_asymmetric.c | 152 +++++++++--
security/integrity/evm/evm_secfs.c | 16 +-
security/integrity/ima/Kconfig | 15 +
security/integrity/ima/ima.h | 28 +-
security/integrity/ima/ima_api.c | 2 +-
security/integrity/ima/ima_appraise.c | 10 +-
security/integrity/ima/ima_fs.c | 346 +++++++++++++++++++++---
security/integrity/ima/ima_init.c | 5 +
security/integrity/ima/ima_kexec.c | 42 ++-
security/integrity/ima/ima_policy.c | 3 +-
security/integrity/ima/ima_queue.c | 327 ++++++++++++++++++++--
15 files changed, 1057 insertions(+), 101 deletions(-)
create mode 100644 Documentation/security/IMA-export-delete.rst
^ permalink raw reply
* [PATCH v4 2/2] ima: measure buffer sent to securityfs policy file
From: Enrico Bravi @ 2026-06-17 15:58 UTC (permalink / raw)
To: linux-integrity, zohar, dmitry.kasatkin, roberto.sassu
Cc: eric.snowberg, Enrico Bravi
In-Reply-To: <20260617155832.434517-1-enrico.bravi@polito.it>
When a signed policy is not mandatory, it is possible to write the IMA
policy directly on the corresponding securityfs file:
echo -e "measure func=BPRM_CHECK mask=MAY_EXEC\n" \
"audit func=BPRM_CHECK mask=MAY_EXEC\n" \
> /sys/kernel/security/ima/policy
or by cat'ing the entire IMA custom policy file:
cat ima-policy-file > /sys/kernel/security/ima/policy
Add input buffer measurement, regardless of whether the new policy
will be accepted or not, that can be caught when
'measure func=POLICY_CHECK' is enabled (e.g., ima_policy=tcb). The
measurement template is forced to ima-buf.
This follows the "measure & load" paradigm, exposing potential bugs in
the policy code and detecting attempts to corrupt IMA. It also completes
the POLICY_CHECK hook, which already measures partial policy load by file.
To verify the template data hash value, convert the buffer policy data
to binary:
grep "ima_policy_written" \
/sys/kernel/security/integrity/ima/ascii_runtime_measurements | \
tail -1 | cut -d' ' -f 6 | xxd -r -p | sha256sum
Suggested-by: Roberto Sassu <roberto.sassu@huawei.com>
Signed-off-by: Enrico Bravi <enrico.bravi@polito.it>
---
security/integrity/ima/ima.h | 1 +
security/integrity/ima/ima_fs.c | 1 +
security/integrity/ima/ima_main.c | 19 +++++++++++++++++++
security/integrity/ima/ima_policy.c | 3 +++
4 files changed, 24 insertions(+)
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index befa221716e5..d477fc06821f 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -455,6 +455,7 @@ void *ima_policy_next(struct seq_file *m, void *v, loff_t *pos);
void ima_policy_stop(struct seq_file *m, void *v);
int ima_policy_show(struct seq_file *m, void *v);
void ima_measure_loaded_policy(void);
+int ima_measure_policy_buf(const char *buf, size_t buf_len);
/* Appraise integrity measurements */
#define IMA_APPRAISE_ENFORCE 0x01
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index 65e7812c702f..a277c9135944 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -356,6 +356,7 @@ static ssize_t ima_write_policy(struct file *file, const char __user *buf,
1, 0);
result = -EACCES;
} else {
+ ima_measure_policy_buf(data, datalen);
result = ima_parse_add_rule(data);
}
mutex_unlock(&ima_write_mutex);
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index 5cea53fc36df..599495304712 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -1221,6 +1221,25 @@ int ima_measure_critical_data(const char *event_label,
}
EXPORT_SYMBOL_GPL(ima_measure_critical_data);
+/**
+ * ima_measure_policy_buf - Measure the policy write buffer
+ * @buf: pointer to the buffer containing the policy write data
+ * @buf_len: size of the buffer
+ *
+ * Measure the buffer sent to the IMA policy securityfs file.
+ *
+ * Return 0 on success, a negative value otherwise.
+ */
+int ima_measure_policy_buf(const char *buf, size_t buf_len)
+{
+ if (!buf || !buf_len)
+ return -EINVAL;
+
+ return process_buffer_measurement(&nop_mnt_idmap, NULL, buf, buf_len,
+ "ima_policy_written", POLICY_CHECK, 0,
+ NULL, false, NULL, 0);
+}
+
#ifdef CONFIG_INTEGRITY_ASYMMETRIC_KEYS
/**
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
index 0a70d10da70a..fc747f391e41 100644
--- a/security/integrity/ima/ima_policy.c
+++ b/security/integrity/ima/ima_policy.c
@@ -541,6 +541,8 @@ static bool ima_match_rule_data(struct ima_rule_entry *rule,
opt_list = rule->label;
break;
+ case POLICY_CHECK:
+ return true;
default:
return false;
}
@@ -589,6 +591,7 @@ static bool ima_match_rules(struct ima_rule_entry *rule,
switch (func) {
case KEY_CHECK:
case CRITICAL_DATA:
+ case POLICY_CHECK:
return ((rule->func == func) &&
ima_match_rule_data(rule, func_data, cred));
default:
--
2.52.0
^ permalink raw reply related
* [PATCH v4 1/2] ima: measure loaded policy after write on securityfs policy file
From: Enrico Bravi @ 2026-06-17 15:58 UTC (permalink / raw)
To: linux-integrity, zohar, dmitry.kasatkin, roberto.sassu
Cc: eric.snowberg, Enrico Bravi
In-Reply-To: <20260617155832.434517-1-enrico.bravi@polito.it>
IMA policy can be written multiple times in the securityfs policy file
at runtime if CONFIG_IMA_WRITE_POLICY=y. When IMA_APPRAISE_POLICY is
required, the policy needs to be signed to be loaded, writing the absolute
path of the file containing the new policy:
echo /path/of/custom_ima_policy > /sys/kernel/security/ima/policy
When this is not required, policy can be written directly, rule by rule:
echo -e "measure func=BPRM_CHECK mask=MAY_EXEC\n" \
"audit func=BPRM_CHECK mask=MAY_EXEC\n" \
> /sys/kernel/security/ima/policy
In this case, a new policy can be loaded without being measured or
appraised.
Add a new critical data record to measure the textual policy
representation when it becomes effective. Include in the
architecture-specific policy the new critical data record only when it
is not mandatory to load a signed policy. Additionally, enable the
policy serialization code even when CONFIG_IMA_READ_POLICY=n.
To verify the template data hash value, convert the buffer policy data
to binary:
grep "ima_policy_loaded" \
/sys/kernel/security/integrity/ima/ascii_runtime_measurements | \
tail -1 | cut -d' ' -f 6 | xxd -r -p | sha256sum
Signed-off-by: Enrico Bravi <enrico.bravi@polito.it>
---
security/integrity/ima/ima.h | 3 ++
security/integrity/ima/ima_efi.c | 2 +
security/integrity/ima/ima_fs.c | 6 ++-
security/integrity/ima/ima_policy.c | 70 ++++++++++++++++++++++++++++-
4 files changed, 78 insertions(+), 3 deletions(-)
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index 69e9bf0b82c6..befa221716e5 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -46,6 +46,8 @@ enum tpm_pcrs { TPM_PCR0 = 0, TPM_PCR8 = 8, TPM_PCR10 = 10 };
/* current content of the policy */
extern int ima_policy_flag;
+extern struct mutex ima_write_mutex;
+
/* bitset of digests algorithms allowed in the setxattr hook */
extern atomic_t ima_setxattr_allowed_hash_algorithms;
@@ -452,6 +454,7 @@ void *ima_policy_start(struct seq_file *m, loff_t *pos);
void *ima_policy_next(struct seq_file *m, void *v, loff_t *pos);
void ima_policy_stop(struct seq_file *m, void *v);
int ima_policy_show(struct seq_file *m, void *v);
+void ima_measure_loaded_policy(void);
/* Appraise integrity measurements */
#define IMA_APPRAISE_ENFORCE 0x01
diff --git a/security/integrity/ima/ima_efi.c b/security/integrity/ima/ima_efi.c
index bca57d836cb9..be7911009454 100644
--- a/security/integrity/ima/ima_efi.c
+++ b/security/integrity/ima/ima_efi.c
@@ -17,6 +17,8 @@ static const char * const sb_arch_rules[] = {
#endif
#if IS_ENABLED(CONFIG_INTEGRITY_MACHINE_KEYRING) && IS_ENABLED(CONFIG_IMA_KEYRINGS_PERMIT_SIGNED_BY_BUILTIN_OR_SECONDARY)
"appraise func=POLICY_CHECK appraise_type=imasig",
+#else
+ "measure func=CRITICAL_DATA label=ima_policy",
#endif
"measure func=MODULE_CHECK",
NULL
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index ca4931a95098..65e7812c702f 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -24,7 +24,7 @@
#include "ima.h"
-static DEFINE_MUTEX(ima_write_mutex);
+DEFINE_MUTEX(ima_write_mutex);
bool ima_canonical_fmt;
static int __init default_canonical_fmt_setup(char *str)
@@ -478,6 +478,10 @@ static int ima_release_policy(struct inode *inode, struct file *file)
}
ima_update_policy();
+
+ mutex_lock(&ima_write_mutex);
+ ima_measure_loaded_policy();
+ mutex_unlock(&ima_write_mutex);
#if !defined(CONFIG_IMA_WRITE_POLICY) && !defined(CONFIG_IMA_READ_POLICY)
securityfs_remove(file->f_path.dentry);
#elif defined(CONFIG_IMA_WRITE_POLICY)
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
index f7f940a76922..0a70d10da70a 100644
--- a/security/integrity/ima/ima_policy.c
+++ b/security/integrity/ima/ima_policy.c
@@ -17,6 +17,7 @@
#include <linux/slab.h>
#include <linux/rculist.h>
#include <linux/seq_file.h>
+#include <linux/vmalloc.h>
#include <linux/ima.h>
#include "ima.h"
@@ -2020,7 +2021,6 @@ const char *const func_tokens[] = {
__ima_hooks(__ima_hook_stringify)
};
-#ifdef CONFIG_IMA_READ_POLICY
enum {
mask_exec = 0, mask_write, mask_read, mask_append
};
@@ -2322,7 +2322,6 @@ int ima_policy_show(struct seq_file *m, void *v)
seq_puts(m, "\n");
return 0;
}
-#endif /* CONFIG_IMA_READ_POLICY */
#if defined(CONFIG_IMA_APPRAISE) && defined(CONFIG_INTEGRITY_TRUSTED_KEYRING)
/*
@@ -2379,3 +2378,70 @@ bool ima_appraise_signature(enum kernel_read_file_id id)
return found;
}
#endif /* CONFIG_IMA_APPRAISE && CONFIG_INTEGRITY_TRUSTED_KEYRING */
+
+/**
+* ima_measure_loaded_policy - measure the active IMA policy ruleset
+*
+* Must be called with ima_write_mutex held, as it performs two
+* separate RCU read passes over ima_rules and relies on the mutex
+* to prevent concurrent policy updates between them.
+*/
+void ima_measure_loaded_policy(void)
+{
+ const char *event_name = "ima_policy_loaded";
+ const char *op = "measure_loaded_ima_policy";
+ struct ima_rule_entry *rule_entry;
+ struct list_head *ima_rules_tmp;
+ struct seq_file file;
+ int result = -ENOMEM;
+ size_t file_len = 0;
+ char rule[512];
+
+ /* calculate IMA policy rules memory size */
+ file.buf = rule;
+ file.read_pos = 0;
+ file.size = 512;
+ file.count = 0;
+
+ lockdep_assert_held(&ima_write_mutex);
+
+ rcu_read_lock();
+ ima_rules_tmp = rcu_dereference(ima_rules);
+ list_for_each_entry_rcu(rule_entry, ima_rules_tmp, list) {
+ ima_policy_show(&file, rule_entry);
+ if (seq_has_overflowed(&file)) {
+ result = -E2BIG;
+ integrity_audit_msg(AUDIT_INTEGRITY_PCR, NULL, event_name,
+ op, "rule_length", result, 1);
+ return;
+ }
+
+ file_len += file.count;
+ file.count = 0;
+ }
+ rcu_read_unlock();
+
+ /* copy IMA policy rules to a buffer for measuring */
+ file.buf = vmalloc(file_len);
+ if (!file.buf) {
+ integrity_audit_msg(AUDIT_INTEGRITY_PCR, NULL, event_name,
+ op, "ENOMEM", result, 1);
+ return;
+ }
+
+ file.read_pos = 0;
+ file.size = file_len;
+ file.count = 0;
+
+ rcu_read_lock();
+ ima_rules_tmp = rcu_dereference(ima_rules);
+ list_for_each_entry_rcu(rule_entry, ima_rules_tmp, list) {
+ ima_policy_show(&file, rule_entry);
+ }
+ rcu_read_unlock();
+
+ ima_measure_critical_data("ima_policy", event_name, file.buf,
+ file.count, false, NULL, 0);
+
+ vfree(file.buf);
+}
--
2.52.0
^ permalink raw reply related
* [PATCH v4 0/2] ima: measure write on securityfs policy file
From: Enrico Bravi @ 2026-06-17 15:58 UTC (permalink / raw)
To: linux-integrity, zohar, dmitry.kasatkin, roberto.sassu
Cc: eric.snowberg, Enrico Bravi
This series aims to introduce integrity measurements when the IMA policy is
written on the securityfs file.
In particular, when a signed policy is not mandatory, it can be written
directly on the securityfs file. This allows to override the boot policy
at the first write, and append new policy rules at the subsequent writes (if
CONFIG_IMA_WRITE_POLICY=y). In this case new policy can be loaded
without being measured.
The patch #1 introduces a new critical-data record for the newly loaded
policy. The measurement is performed over the textual representation of the
new policy once it becomes effective (after ima_update_policy()). As
suggested by Mimi, the new critical-data rule is added to the arch
specific policy rules (only when a signed policy is not mandatory).
The patch #2, following what was suggested by Roberto, measures the input
buffer sent to the securityfs policy file, regardless of whether the new
policy will be accepted or not. This is done by calling
process_buffer_measurement(), enabling POLICY_CHECK in ima_match_rules() and
ima_match_rule_data() in order to catch it when 'measure func=POLICY_CHECK'
is defined (e.g., ima_policy=tcb).
Changes in v4:
- Added kernel-doc for the new ima_measure_loaded_policy() function as
suggested by Mimi.
- Increased the rule buffer size in ima_measure_loaded_policy() (suggested
by Mimi) checking if seq_has_overflowed() calculating the policy length.
- Acquire ima_write_mutex in before calling ima_measure_loaded_policy()
and verify lockdep_assert_held(&ima_write_mutex) as suggested by Mimi.
- Initialize file_len to zero in ima_measure_loaded_policy() as
suggested by Mimi.
- Changed ima_measure_policy_buf() returned error from -ENOPARAM to
-EINVAL as suggested by Mimi.
- Changed event name from "ima_write_policy_buf" to "ima_policy_written"
as suggested by Mimi.
- Updated patches description.
Changes in v3:
- Include the newly defined critical-data rule only if a signed policy is
not mandatory as suggested by Mimi.
- Removed the ima_policy_text_len() function as suggested by Mimi.
- Moved policy input buffer measurement from process_measurement() to
process_buffer_measurement() as suggested by Mimi.
- Changed ima_measure_policy_write() function name to ima_measure_policy_buf()
as suggested by Mimi.
- Updated patches description.
Changes in v2:
- Set a new critical-data rule for measuring the loaded IMA policy.
- Add the new critical-data rule to the specific arch policy rules.
- Add patch #2 for measuring the input buffer sent to the securityfs
policy file.
Enrico Bravi (2):
ima: measure loaded policy after write on securityfs policy file
ima: measure buffer sent to securityfs policy file
security/integrity/ima/ima.h | 4 ++
security/integrity/ima/ima_efi.c | 2 +
security/integrity/ima/ima_fs.c | 7 ++-
security/integrity/ima/ima_main.c | 19 ++++++++
security/integrity/ima/ima_policy.c | 73 ++++++++++++++++++++++++++++-
5 files changed, 102 insertions(+), 3 deletions(-)
base-commit: 8cd9520d35a6c38db6567e97dd93b1f11f185dc6
--
2.52.0
^ permalink raw reply
* inherit null-key across hibernate (was: Re: regression: kernel log "flooded" with tpm tpm0: A TPM error (2306) occurred attempting to create NULL primary)
From: Daniel Golle @ 2026-06-16 3:50 UTC (permalink / raw)
To: Jarkko Sakkinen
Cc: James Bottomley, Christoph Anton Mitterer, linux-integrity
In-Reply-To: <D5LEQGJ9X3NF.3K3YVPNE6KQJK@kernel.org>
Hi Jarkko,
Hi James,
first of all, sorry for hijacking a thread from 2 years ago.
On Thu, Nov 14, 2024 at 12:34:30AM +0200, Jarkko Sakkinen wrote:
> On Wed Nov 13, 2024 at 8:12 PM EET, James Bottomley wrote:
> > > I think we might have to expect the NULL name to change on actual
> > > hibernation because unlike suspend to ram it does power off the TPM.
> >
> > I checked the code: we're coming in on the correct path to renew the
> > null seed after hibernation, so it should all work. The problem seems
> > to be that your TPM itself is doing something invalid because the name
> > we calculate for the primary key doesn't match what your TPM says it
> > should be. Absent some form of attack or bus integrity problem, that
> > shouldn't ever happen, so I'm even more curious to know why it worked
> > in 6.11.5 and before and whether current upstream works.
> >
> > I haven't found it yet, but I think the every 10s signature is because
> > the hibernation path is trying to restart the TPM device and won't take
> > no for an answer.
>
> My fix returned the behavior how it was before my earlier fix in this
> corner case (i.e. disable TPM). The issue has gone unnoticed before
> since it has emitted only a single klog entry.
>
> On suspend this has not happened to me so obvious deduction is that
> hibernate resets the null seed.
>
> Hibernate needs an addition a fix to disable bus encryption from kernel
> command-line completely, i.e. tpm.disable_integrity following the
> convention from my earlier fix [1].
I'd like to offer a way it might be resolvable with the null key after
all, without provisioning a persistent NV key -- by changing the
question from "re-derive the null primary and compare" to "inherit the
trust the resume has already established".
Resume-from-hibernation is a TPM Restart (Shutdown(STATE) ->
Startup(CLEAR)), i.e. a firmware cold-init of the (f)TPM, after which
the boot/initramfs kernel establishes a fresh, genuine null primary.
In the common configuration (FDE with the resume/swap device inside
a TPM-sealed LUKS2 container) that same TPM has, moments earlier and
*before* the hibernation image is restored, cryptographically attested
itself by unsealing the resume device. A substituted or interposed TPM
cannot produce that unseal.
So rather than letting the resumed kernel re-derive the null name,
find a mismatch and disable the chip, the boot kernel's
freshly-established and unseal-validated null primary could be
inherited by the resumed image. The existing null-seed TOFU model is
preserved; nothing new is provisioned.
The gate is the unseal, and the adversary case shows why it is the
right gate:
Malice swaps (or interposes on) the TPM while the machine is
hibernated, then leaves. Alice powers on. The initramfs attempts the
TPM unseal of the resume device; with a foreign TPM it fails, so
systemd-cryptsetup falls back to the passphrase, which Alice --
seeing a prompt -- types. The disk opens and the system resumes.
If trust were re-established here, Alice would have personally vouched
for Malice's TPM. But the unseal *failed*, so under this scheme
nothing is inherited and the chip stays fail-closed exactly as today.
The passphrase proves a human is present; it never proves the TPM is
the genuine one.
Hence: unseal succeeded -> inherit the validated null primary;
passphrase fallback -> trust is lost, stay disabled.
This keeps the property the null-seed design wants -- an in-session
reset is not on the hibernate-restore path and is still caught --
while removing the false positive only where the platform has already
re-attested the TPM.
The hard parts, and where I'd value direction:
- systemd-cryptsetup would need to signal "the resume device was
unsealed by the TPM this boot" (vs. the passphrase fallback).
This is per-resume runtime state; a static command-line parameter
(and obviously build-time config as well) cannot represent it.
- the validated null primary has to cross the boot -> resumed memory
discontinuity (the initramfs kernel's state is overwritten by the
restored image). Boot and image kernel are the same binary, so
patching chip->null_key_name in the restored image is mechanically
possible; a small reserved/nosave hand-off area may be cleaner.
I don't know the hibernate path well enough to say which is right.
It is admittedly cross-subsystem (tpm + pm/hibernate +
systemd/cryptsetup), which is presumably why it hasn't been done.
Compared with the persistent NV-key route
(tpm.integrity_key=<handle>): that avoids the carry-across but needs
the key provisioned and managed, and a persistent key's name no longer
changes on a genuine reset, so the implicit reset detection has to be
reconstructed. The null-key-inherit approach keeps the existing model
and defers "is this the same TPM?" to the unseal that has already
happened.
Does this seem viable, or is there a reason the unseal-as-attestation
gate does not hold that I'm missing?
(For motivation: on a firmware TPM -- Intel PTT here -- there is no
external bus to interpose, so the protection has no benefit on this
class of hardware at all, yet the legitimate hibernation power-cycle
still trips the disable. For fTPMs specifically, not enabling the
feature is arguably the better answer; but for discrete TPMs that
hibernate, a real solution seems a good idea if doable.)
Cheers
Daniel
^ permalink raw reply
* Re: [PATCH] tpm: tpm_tis: add settle delay after releasing locality
From: Daniel Golle @ 2026-06-15 12:50 UTC (permalink / raw)
To: Jarkko Sakkinen
Cc: Peter Huewe, Jason Gunthorpe, Chen Jun, linux-integrity,
linux-kernel
In-Reply-To: <ai_tZ0VM9PDCRMfl@kernel.org>
On Mon, Jun 15, 2026 at 03:17:43PM +0300, Jarkko Sakkinen wrote:
> On Mon, Jun 15, 2026 at 05:48:43AM +0100, Daniel Golle wrote:
> > tpm_tis_core_init() releases locality 0 then immediately reclaims it via
> > tpm_chip_start(); some TPMs (e.g. Nuvoton NPCT, TPM 2.0) need a few ms
> > before granting it again, so probe fails with -1. This back-to-back
> > release/request was added with the locality claim around TPM_INT_ENABLE.
> >
> > Wait for the chip to settle after releasing the locality. A delay of
> > TPM_TIMEOUT (5 ms) in __tpm_tis_relinquish_locality() is reliable; values
> > below 3 ms are not.
> >
> > Fixes: 0ef333f5ba7f ("tpm: add request_locality before write TPM_INT_ENABLE")
> > Cc: stable@vger.kernel.org
> > Signed-off-by: Daniel Golle <daniel@makrotopia.org>
>
> Can rebase to my for-next-tpm and resend v2?
I figured the fix is already in place in your tree, added by
34bc0fabf166 ("tpm: tpm_tis: Add settle time for some TPMs")
device_id 0x00FE, vendor_id 0x1050 exactly matches my TPM as well,
so please drop my patch.
Sorry for the noise...
^ permalink raw reply
* Re: [PATCH] tpm: tpm_tis: add settle delay after releasing locality
From: Jarkko Sakkinen @ 2026-06-15 12:17 UTC (permalink / raw)
To: Daniel Golle
Cc: Peter Huewe, Jason Gunthorpe, Chen Jun, linux-integrity,
linux-kernel
In-Reply-To: <086949bcf2c10bead892b0b4befd98da370cd3ee.1781498837.git.daniel@makrotopia.org>
On Mon, Jun 15, 2026 at 05:48:43AM +0100, Daniel Golle wrote:
> tpm_tis_core_init() releases locality 0 then immediately reclaims it via
> tpm_chip_start(); some TPMs (e.g. Nuvoton NPCT, TPM 2.0) need a few ms
> before granting it again, so probe fails with -1. This back-to-back
> release/request was added with the locality claim around TPM_INT_ENABLE.
>
> Wait for the chip to settle after releasing the locality. A delay of
> TPM_TIMEOUT (5 ms) in __tpm_tis_relinquish_locality() is reliable; values
> below 3 ms are not.
>
> Fixes: 0ef333f5ba7f ("tpm: add request_locality before write TPM_INT_ENABLE")
> Cc: stable@vger.kernel.org
> Signed-off-by: Daniel Golle <daniel@makrotopia.org>
> ---
> drivers/char/tpm/tpm_tis_core.c | 2 ++
> 1 file changed, 2 insertions(+)
>
> diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c
> index 21d79ad3b164..6b90ff50c78d 100644
> --- a/drivers/char/tpm/tpm_tis_core.c
> +++ b/drivers/char/tpm/tpm_tis_core.c
> @@ -171,6 +171,8 @@ static int __tpm_tis_relinquish_locality(struct tpm_tis_data *priv, int l)
> {
> tpm_tis_write8(priv, TPM_ACCESS(l), TPM_ACCESS_ACTIVE_LOCALITY);
>
> + tpm_msleep(TPM_TIMEOUT);
> +
> return 0;
> }
>
> --
> 2.54.0
>
Can rebase to my for-next-tpm and resend v2?
BR, Jarkko
^ permalink raw reply
* Re: [PATCH] tpm: tpm_tis: add settle delay after releasing locality
From: Jarkko Sakkinen @ 2026-06-15 12:15 UTC (permalink / raw)
To: Daniel Golle
Cc: Peter Huewe, Jason Gunthorpe, Chen Jun, linux-integrity,
linux-kernel
In-Reply-To: <086949bcf2c10bead892b0b4befd98da370cd3ee.1781498837.git.daniel@makrotopia.org>
On Mon, Jun 15, 2026 at 05:48:43AM +0100, Daniel Golle wrote:
> tpm_tis_core_init() releases locality 0 then immediately reclaims it via
> tpm_chip_start(); some TPMs (e.g. Nuvoton NPCT, TPM 2.0) need a few ms
> before granting it again, so probe fails with -1. This back-to-back
> release/request was added with the locality claim around TPM_INT_ENABLE.
>
> Wait for the chip to settle after releasing the locality. A delay of
> TPM_TIMEOUT (5 ms) in __tpm_tis_relinquish_locality() is reliable; values
> below 3 ms are not.
>
> Fixes: 0ef333f5ba7f ("tpm: add request_locality before write TPM_INT_ENABLE")
> Cc: stable@vger.kernel.org
> Signed-off-by: Daniel Golle <daniel@makrotopia.org>
> ---
> drivers/char/tpm/tpm_tis_core.c | 2 ++
> 1 file changed, 2 insertions(+)
>
> diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c
> index 21d79ad3b164..6b90ff50c78d 100644
> --- a/drivers/char/tpm/tpm_tis_core.c
> +++ b/drivers/char/tpm/tpm_tis_core.c
> @@ -171,6 +171,8 @@ static int __tpm_tis_relinquish_locality(struct tpm_tis_data *priv, int l)
> {
> tpm_tis_write8(priv, TPM_ACCESS(l), TPM_ACCESS_ACTIVE_LOCALITY);
>
> + tpm_msleep(TPM_TIMEOUT);
> +
> return 0;
> }
>
> --
> 2.54.0
>
I think this is totally fine.
Reviewed-by: Jarkko Sakkinen <jarkko@kernel.org>
BR, Jarkko
^ permalink raw reply
* Re: [PATCH v2] tpm: fix event_size output in tpm1_binary_bios_measurements_show
From: Jarkko Sakkinen @ 2026-06-15 12:03 UTC (permalink / raw)
To: Thorsten Blum
Cc: Peter Huewe, Jason Gunthorpe, Colin Ian King, Harald Hoyer,
stable, linux-integrity, linux-kernel
In-Reply-To: <ainKo2YOGN0hxWwE@linux.dev>
On Wed, Jun 10, 2026 at 10:35:47PM +0200, Thorsten Blum wrote:
> On Fri, May 22, 2026 at 03:55:03PM +0300, Jarkko Sakkinen wrote:
> > On Fri, May 22, 2026 at 11:44:38AM +0200, Thorsten Blum wrote:
> > > Commit 186d124f07da ("tpm_eventlog.c: fix binary_bios_measurements")
> > > split the output to write the endian-converted event header first and
> > > then the variable-length event data.
> > >
> > > However, the split was at sizeof(struct tcpa_event) - 1, even though
> > > event_data was a zero-length array, and later a flexible array member,
> > > both of which already excluded the event data.
> > >
> > > Therefore, the current code writes the first three bytes of event_size
> > > from the endian-converted header and then the last byte from the raw
> > > header, which can emit a corrupted event_size on PPC64, where
> > > do_endian_conversion() maps to be32_to_cpu().
> > >
> > > Split one byte later to write the full endian-converted header first,
> > > followed by the variable-length event->event_data.
> > >
> > > Fixes: 186d124f07da ("tpm_eventlog.c: fix binary_bios_measurements")
> > > Cc: stable@vger.kernel.org
> > > Signed-off-by: Thorsten Blum <thorsten.blum@linux.dev>
> > > ---
> > > Changes in v2:
> > > - Minimal fix without using seq_write()
> > > - v1: https://lore.kernel.org/lkml/20260521093639.162095-3-thorsten.blum@linux.dev/
> > > ---
> > > drivers/char/tpm/eventlog/tpm1.c | 4 ++--
> > > 1 file changed, 2 insertions(+), 2 deletions(-)
> > >
> > > diff --git a/drivers/char/tpm/eventlog/tpm1.c b/drivers/char/tpm/eventlog/tpm1.c
> > > index e7913b2853d5..0397e3361020 100644
> > > --- a/drivers/char/tpm/eventlog/tpm1.c
> > > +++ b/drivers/char/tpm/eventlog/tpm1.c
> > > @@ -236,12 +236,12 @@ static int tpm1_binary_bios_measurements_show(struct seq_file *m, void *v)
> > >
> > > temp_ptr = (char *) &temp_event;
> > >
> > > - for (i = 0; i < (sizeof(struct tcpa_event) - 1) ; i++)
> > > + for (i = 0; i < sizeof(struct tcpa_event); i++)
> > > seq_putc(m, temp_ptr[i]);
> > >
> > > temp_ptr = (char *) v;
> > >
> > > - for (i = (sizeof(struct tcpa_event) - 1);
> > > + for (i = sizeof(struct tcpa_event);
> > > i < (sizeof(struct tcpa_event) + temp_event.event_size); i++)
> > > seq_putc(m, temp_ptr[i]);
> > >
> >
> > This was really good catch, thank you. I'll apply in a minute.
>
> Has this already been applied somewhere?
My bad, and thanks for pingin! queued
>
> Thanks,
> Thorsten
BR, Jarkko
^ permalink raw reply
* Re: [PATCH v2 v2] evm: check return values of crypto_shash functions
From: Roberto Sassu @ 2026-06-15 7:25 UTC (permalink / raw)
To: Mimi Zohar, Daniel Hodges
Cc: roberto.sassu, dmitry.kasatkin, eric.snowberg, paul, jmorris,
serge, linux-integrity, linux-security-module, linux-kernel
In-Reply-To: <c1d570271884159e6c14617fa7dcd39bc2103e45.camel@linux.ibm.com>
On 3/9/2026 4:03 PM, Mimi Zohar wrote:
> On Thu, 2026-02-19 at 10:26 +0100, Roberto Sassu wrote:
>> On Thu, 2026-02-05 at 21:42 -0500, Daniel Hodges wrote:
>>> The crypto_shash_update() and crypto_shash_final() functions can fail
>>> and return error codes, but their return values were not being checked
>>> in several places in security/integrity/evm/evm_crypto.c:
>>>
>>> - hmac_add_misc() ignored returns from crypto_shash_update() and
>>> crypto_shash_final()
>>> - evm_calc_hmac_or_hash() ignored returns from crypto_shash_update()
>>> - evm_init_hmac() ignored returns from crypto_shash_update()
>>>
>>> If these hash operations fail silently, the resulting HMAC could be
>>> invalid or incomplete, which could weaken the integrity verification
>>> security that EVM provides.
>>>
>>> This patch converts hmac_add_misc() from void to int return type and
>>> adds proper error checking and propagation for all crypto_shash_*
>>> function calls. All callers are updated to handle the new return values.
>>> Additionally, error messages are logged when cryptographic operations
>>> fail to provide visibility into the failure rather than silently
>>> returning error codes.
>>>
>>> Fixes: 66dbc325afce ("evm: re-release")
>>> Signed-off-by: Daniel Hodges <git@danielhodges.dev>
>>
>> After fixing the minor issue below:
>>
>> Reviewed-by: Roberto Sassu <roberto.sassu@huawei.com>
>
> Thanks Daniel, Roberto. Daniel there are a couple of places where the line
> length is greater than 80. To see them, add "--max-line-length=80" to
> scripts/checkpatch.pl. I'd appreciate your fixing them. Otherwise, the patch
> looks good.
Daniel, do you have time to fix the style issues, so that we upstream
your patch?
Thanks
Roberto
^ permalink raw reply
* [PATCH] tpm: tpm_tis: add settle delay after releasing locality
From: Daniel Golle @ 2026-06-15 4:48 UTC (permalink / raw)
To: Peter Huewe, Jarkko Sakkinen, Jason Gunthorpe, Chen Jun,
linux-integrity, linux-kernel
tpm_tis_core_init() releases locality 0 then immediately reclaims it via
tpm_chip_start(); some TPMs (e.g. Nuvoton NPCT, TPM 2.0) need a few ms
before granting it again, so probe fails with -1. This back-to-back
release/request was added with the locality claim around TPM_INT_ENABLE.
Wait for the chip to settle after releasing the locality. A delay of
TPM_TIMEOUT (5 ms) in __tpm_tis_relinquish_locality() is reliable; values
below 3 ms are not.
Fixes: 0ef333f5ba7f ("tpm: add request_locality before write TPM_INT_ENABLE")
Cc: stable@vger.kernel.org
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
drivers/char/tpm/tpm_tis_core.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c
index 21d79ad3b164..6b90ff50c78d 100644
--- a/drivers/char/tpm/tpm_tis_core.c
+++ b/drivers/char/tpm/tpm_tis_core.c
@@ -171,6 +171,8 @@ static int __tpm_tis_relinquish_locality(struct tpm_tis_data *priv, int l)
{
tpm_tis_write8(priv, TPM_ACCESS(l), TPM_ACCESS_ACTIVE_LOCALITY);
+ tpm_msleep(TPM_TIMEOUT);
+
return 0;
}
--
2.54.0
^ permalink raw reply related
* Re: [RFC][PATCH v3 1/2] ima: measure loaded policy after write on securityfs policy file
From: Enrico Bravi @ 2026-06-12 9:19 UTC (permalink / raw)
To: roberto.sassu@huawei.com, linux-integrity@vger.kernel.org,
zohar@linux.ibm.com, dmitry.kasatkin@gmail.com
Cc: eric.snowberg@oracle.com
In-Reply-To: <5703152fed864a39eb54cfccb571e9781a493760.camel@linux.ibm.com>
Hi Mimi,
On Thu, 2026-06-11 at 10:30 -0400, Mimi Zohar wrote:
> On Thu, 2026-06-11 at 12:51 +0000, Enrico Bravi wrote:
> >
> > > > diff --git a/security/integrity/ima/ima_efi.c
> > > > b/security/integrity/ima/ima_efi.c
> > > > index 138029bfcce1..8e9f85ec9a86 100644
> > > > --- a/security/integrity/ima/ima_efi.c
> > > > +++ b/security/integrity/ima/ima_efi.c
> > > > @@ -60,6 +60,8 @@ static const char * const sb_arch_rules[] = {
> > > > #endif
> > > > #if IS_ENABLED(CONFIG_INTEGRITY_MACHINE_KEYRING) &&
> > > > IS_ENABLED(CONFIG_IMA_KEYRINGS_PERMIT_SIGNED_BY_BUILTIN_OR_SECONDARY)
> > > > "appraise func=POLICY_CHECK appraise_type=imasig",
> > > > +#else
> > > > + "measure func=CRITICAL_DATA label=ima_policy",
> > > > #endif
> > >
> > > None of the other arch "measure" policy rules are conditional. Should
> > > the
> > > new
> > > "measure" rule be limited?
> >
> > This condition aims to avoid measuring the policy loaded even if a signed
> > policy
> > is required. In that case, it would not be possible to directly write the
> > policy
> > in the securityfs file.
>
> Good point. Since it is different than the other rules, could you add a
> comment
> here or in the patch description.
Sure, will do.
> >
> > > > "measure func=MODULE_CHECK",
> > > > NULL
> > > > diff --git a/security/integrity/ima/ima_fs.c
> > > > b/security/integrity/ima/ima_fs.c
> > > > index 012a58959ff0..75cb308cf01f 100644
> > > > --- a/security/integrity/ima/ima_fs.c
> > > > +++ b/security/integrity/ima/ima_fs.c
> > > >
> > > > @@ -2381,3 +2380,55 @@ bool ima_appraise_signature(enum
> > > > kernel_read_file_id
> > > > id)
> > > > return found;
> > > > }
> > > > #endif /* CONFIG_IMA_APPRAISE && CONFIG_INTEGRITY_TRUSTED_KEYRING */
> > > > +
> > >
> > > Please add kernel-doc here, something like:
> > >
> > > /**
> > > * ima_measure_loaded_policy - measure the active IMA policy ruleset
> > > *
> > > * Must be called with ima_write_mutex held, as it performs two
> > > * separate RCU read passes over ima_rules and relies on the mutex
> > > * to prevent concurrent policy updates between them.
> > > */
> >
> > Sure, thank you. If it is ok for you I can directly add what you suggested.
>
> This was suggested by Claude, so it should be acceptable to use.
>
> >
> > > > +void ima_measure_loaded_policy(void)
> > > > +{
> > > > + const char *event_name = "ima_policy_loaded";
> > > > + const char *op = "measure_loaded_ima_policy";
> > > > + const char *audit_cause = "ENOMEM";
> > > > + struct ima_rule_entry *rule_entry;
> > > > + struct list_head *ima_rules_tmp;
> > > > + struct seq_file file;
> > > > + int result = -ENOMEM;
> > > > + size_t file_len;
> > > > + char rule[255];
> > >
> > > The 255-byte buffer may be insufficient for custom policy rules that
> > > include
> > > additional fields such as LSM labels and other file metadata, unlike the
> > > simpler
> > > built-in and architecture-specific rules. Please increase the buffer size
> > > to
> > > accommodate the worst-case serialized rule length.
> >
> > Yes, I wrongly took as reference the arch policy rules case. I don't know if
> > the
> > worst-case can be precisely estimated. I could increase the buffer size and
> > check in any case if seq_has_overflowed(). Could it be an idea?
>
> Sounds good.
Perfect, thank you.
Enrico
> >
> > > > +
> > > > + /* calculate IMA policy rules memory size */
> > > > + file.buf = rule;
> > > > + file.read_pos = 0;
> > > > + file.size = 255;
> > > > + file.count = 0;
> > > > +
> > >
> > > Please add "lockdep_assert_held(&ima_write_mutex);" here.
> >
> > Yes, and this would actually fail because I'm not acquiring ima_write_mutex
> > in
> > ima_release_policy().
>
> Thanks.
>
> Mimi
^ permalink raw reply
* Re: [RFC][PATCH v3 1/2] ima: measure loaded policy after write on securityfs policy file
From: Mimi Zohar @ 2026-06-11 14:30 UTC (permalink / raw)
To: Enrico Bravi, roberto.sassu@huawei.com,
linux-integrity@vger.kernel.org, dmitry.kasatkin@gmail.com
Cc: eric.snowberg@oracle.com
In-Reply-To: <7149d8e873fe59fedffd23a06c9c647d42660328.camel@polito.it>
On Thu, 2026-06-11 at 12:51 +0000, Enrico Bravi wrote:
>
> > > diff --git a/security/integrity/ima/ima_efi.c
> > > b/security/integrity/ima/ima_efi.c
> > > index 138029bfcce1..8e9f85ec9a86 100644
> > > --- a/security/integrity/ima/ima_efi.c
> > > +++ b/security/integrity/ima/ima_efi.c
> > > @@ -60,6 +60,8 @@ static const char * const sb_arch_rules[] = {
> > > #endif
> > > #if IS_ENABLED(CONFIG_INTEGRITY_MACHINE_KEYRING) &&
> > > IS_ENABLED(CONFIG_IMA_KEYRINGS_PERMIT_SIGNED_BY_BUILTIN_OR_SECONDARY)
> > > "appraise func=POLICY_CHECK appraise_type=imasig",
> > > +#else
> > > + "measure func=CRITICAL_DATA label=ima_policy",
> > > #endif
> >
> > None of the other arch "measure" policy rules are conditional. Should the
> > new
> > "measure" rule be limited?
>
> This condition aims to avoid measuring the policy loaded even if a signed policy
> is required. In that case, it would not be possible to directly write the policy
> in the securityfs file.
Good point. Since it is different than the other rules, could you add a comment
here or in the patch description.
>
> > > "measure func=MODULE_CHECK",
> > > NULL
> > > diff --git a/security/integrity/ima/ima_fs.c
> > > b/security/integrity/ima/ima_fs.c
> > > index 012a58959ff0..75cb308cf01f 100644
> > > --- a/security/integrity/ima/ima_fs.c
> > > +++ b/security/integrity/ima/ima_fs.c
> > >
> > > @@ -2381,3 +2380,55 @@ bool ima_appraise_signature(enum kernel_read_file_id
> > > id)
> > > return found;
> > > }
> > > #endif /* CONFIG_IMA_APPRAISE && CONFIG_INTEGRITY_TRUSTED_KEYRING */
> > > +
> >
> > Please add kernel-doc here, something like:
> >
> > /**
> > * ima_measure_loaded_policy - measure the active IMA policy ruleset
> > *
> > * Must be called with ima_write_mutex held, as it performs two
> > * separate RCU read passes over ima_rules and relies on the mutex
> > * to prevent concurrent policy updates between them.
> > */
>
> Sure, thank you. If it is ok for you I can directly add what you suggested.
This was suggested by Claude, so it should be acceptable to use.
>
> > > +void ima_measure_loaded_policy(void)
> > > +{
> > > + const char *event_name = "ima_policy_loaded";
> > > + const char *op = "measure_loaded_ima_policy";
> > > + const char *audit_cause = "ENOMEM";
> > > + struct ima_rule_entry *rule_entry;
> > > + struct list_head *ima_rules_tmp;
> > > + struct seq_file file;
> > > + int result = -ENOMEM;
> > > + size_t file_len;
> > > + char rule[255];
> >
> > The 255-byte buffer may be insufficient for custom policy rules that include
> > additional fields such as LSM labels and other file metadata, unlike the
> > simpler
> > built-in and architecture-specific rules. Please increase the buffer size to
> > accommodate the worst-case serialized rule length.
>
> Yes, I wrongly took as reference the arch policy rules case. I don't know if the
> worst-case can be precisely estimated. I could increase the buffer size and
> check in any case if seq_has_overflowed(). Could it be an idea?
Sounds good.
>
> > > +
> > > + /* calculate IMA policy rules memory size */
> > > + file.buf = rule;
> > > + file.read_pos = 0;
> > > + file.size = 255;
> > > + file.count = 0;
> > > +
> >
> > Please add "lockdep_assert_held(&ima_write_mutex);" here.
>
> Yes, and this would actually fail because I'm not acquiring ima_write_mutex in
> ima_release_policy().
Thanks.
Mimi
> >
^ permalink raw reply
* Re: [RFC][PATCH v3 2/2] ima: measure buffer sent to securityfs policy file
From: Roberto Sassu @ 2026-06-11 13:41 UTC (permalink / raw)
To: Mimi Zohar, Enrico Bravi, linux-integrity, dmitry.kasatkin,
roberto.sassu
Cc: eric.snowberg
In-Reply-To: <2658c55c9d6a97ee8edca682d27369138aab67f5.camel@linux.ibm.com>
On Wed, 2026-06-10 at 14:59 -0400, Mimi Zohar wrote:
> On Tue, 2026-05-26 at 15:51 +0200, Enrico Bravi wrote:
> > When a signed policy is not mandatory, it is possible to write the IMA
> > policy directly on the corresponding securityfs file:
> >
> > echo -e "measure func=BPRM_CHECK mask=MAY_EXEC\n" \
> > "audit func=BPRM_CHECK mask=MAY_EXEC\n" \
> > > /sys/kernel/security/ima/policy
>
> Or by cat'ing the entire IMA custom policy file.
>
> >
> > Add input buffer measurement, regardless of whether the new policy
> > will be accepted or not, that can be caught when
> > 'measure func=POLICY_CHECK' is enabled (e.g., ima_policy=tcb).
>
> Enrco, Roberto, a reason for measuring invalid or malformed IMA policy rules
> needs to be provided here.
One reason would be to be able to detect attempts to corrupt IMA by
loading malformed data, since the measurement is performed before the
policy is parsed.
> In addition to the "ima_policy" critical data, why is this mechanism needed?
POLICY_CHECK already measures partial policy load by file. This patch
would just complete the POLICY_CHECK hook by measuring policy load
by buffer.
I believe they can both cohexist, both would allow to know which policy
is loaded at any time. With the critical data, the existing policy is
included in every entry, but it is easier to derive the current policy
loaded. It would be up to the remote attestation solution to determine
which solution is more suitable.
Roberto
> > The
> > measurement template is forced to ima-buf.
>
> Please include directions for verifying the measurement record here in the patch
> description.
>
> >
> > Suggested-by: Roberto Sassu <roberto.sassu@huawei.com>
> > Signed-off-by: Enrico Bravi <enrico.bravi@polito.it>
> > ---
> > security/integrity/ima/ima.h | 1 +
> > security/integrity/ima/ima_fs.c | 1 +
> > security/integrity/ima/ima_main.c | 19 +++++++++++++++++++
> > security/integrity/ima/ima_policy.c | 3 +++
> > 4 files changed, 24 insertions(+)
> >
> > diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
> > index a223d3f30d88..320c80a1a847 100644
> > --- a/security/integrity/ima/ima.h
> > +++ b/security/integrity/ima/ima.h
> > @@ -426,6 +426,7 @@ void *ima_policy_next(struct seq_file *m, void *v, loff_t *pos);
> > void ima_policy_stop(struct seq_file *m, void *v);
> > int ima_policy_show(struct seq_file *m, void *v);
> > void ima_measure_loaded_policy(void);
> > +int ima_measure_policy_buf(const char *buf, size_t buf_len);
> >
> > /* Appraise integrity measurements */
> > #define IMA_APPRAISE_ENFORCE 0x01
> > diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
> > index 75cb308cf01f..601718e02429 100644
> > --- a/security/integrity/ima/ima_fs.c
> > +++ b/security/integrity/ima/ima_fs.c
> > @@ -362,6 +362,7 @@ static ssize_t ima_write_policy(struct file *file, const char __user *buf,
> > 1, 0);
> > result = -EACCES;
> > } else {
> > + ima_measure_policy_buf(data, datalen);
> > result = ima_parse_add_rule(data);
> > }
> > mutex_unlock(&ima_write_mutex);
> > diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
> > index 1d6229b156fb..174110da0030 100644
> > --- a/security/integrity/ima/ima_main.c
> > +++ b/security/integrity/ima/ima_main.c
> > @@ -1204,6 +1204,25 @@ int ima_measure_critical_data(const char *event_label,
> > }
> > EXPORT_SYMBOL_GPL(ima_measure_critical_data);
> >
> > +/**
> > + * ima_measure_policy_buf - Measure the policy write buffer
> > + * @buf: pointer to the buffer containing the policy write data
> > + * @buf_len: size of the buffer
> > + *
> > + * Measure the buffer sent to the IMA policy securityfs file.
> > + *
> > + * Return 0 on success, a negative value otherwise.
> > + */
> > +int ima_measure_policy_buf(const char *buf, size_t buf_len)
> > +{
> > + if (!buf || !buf_len)
> > + return -ENOPARAM;
>
> Please return -EINVAL.
>
>
> > +
> > + return process_buffer_measurement(&nop_mnt_idmap, NULL, buf, buf_len,
> > + "ima_write_policy_buf", POLICY_CHECK,
> > + 0, NULL, false, NULL, 0);
>
> Parallel to "ima_policy_loaded" consider naming the record as
> "ima_policy_written".
>
> The indentation is off by a character.
>
> > +}
> > +
> > #ifdef CONFIG_INTEGRITY_ASYMMETRIC_KEYS
> >
> > /**
>
> [...]
>
> thanks,
>
> Mimi
>
^ permalink raw reply
* Re: [RFC][PATCH v3 1/2] ima: measure loaded policy after write on securityfs policy file
From: Enrico Bravi @ 2026-06-11 12:51 UTC (permalink / raw)
To: roberto.sassu@huawei.com, linux-integrity@vger.kernel.org,
zohar@linux.ibm.com, dmitry.kasatkin@gmail.com
Cc: eric.snowberg@oracle.com
In-Reply-To: <f48148da72e9111235cb06d9c4d6c959d5c67035.camel@linux.ibm.com>
Hi Mimi,
On Wed, 2026-06-10 at 10:32 -0400, Mimi Zohar wrote:
> On Tue, 2026-05-26 at 15:51 +0200, Enrico Bravi wrote:
> > IMA policy can be written multiple times in the securityfs policy file
> > at runtime if CONFIG_IMA_WRITE_POLICY=y. When IMA_APPRAISE_POLICY is
> > required, the policy needs to be signed to be loaded, writing the absolute
> > path of the file containing the new policy:
> >
> > echo /path/of/custom_ima_policy > /sys/kernel/security/ima/policy
> >
> > When this is not required, policy can be written directly, rule by rule:
> >
> > echo -e "measure func=BPRM_CHECK mask=MAY_EXEC\n" \
> > "audit func=BPRM_CHECK mask=MAY_EXEC\n" \
> > > /sys/kernel/security/ima/policy
> >
> > In this case, a new policy can be loaded without being measured or
> > appraised.
> >
> > Add a new critical data record to measure the textual policy
> > representation when it becomes effective.
> > To verify the template data hash value, convert the buffer policy data
> > to binary:
> > grep "ima_policy_loaded" \
> > /sys/kernel/security/integrity/ima/ascii_runtime_measurements | \
> > tail -1 | cut -d' ' -f 6 | xxd -r -p | sha256sum
> >
> > Signed-off-by: Enrico Bravi <enrico.bravi@polito.it>
>
> Thanks, Enrico. Just a few inline comments.
Thank you very much for your feedback.
> > ---
> > security/integrity/ima/ima.h | 1 +
> > security/integrity/ima/ima_efi.c | 2 ++
> > security/integrity/ima/ima_fs.c | 1 +
> > security/integrity/ima/ima_policy.c | 55 +++++++++++++++++++++++++++--
> > 4 files changed, 57 insertions(+), 2 deletions(-)
> >
> > diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
> > index 89ebe98ffc5e..a223d3f30d88 100644
> > --- a/security/integrity/ima/ima.h
> > +++ b/security/integrity/ima/ima.h
> > @@ -425,6 +425,7 @@ void *ima_policy_start(struct seq_file *m, loff_t *pos);
> > void *ima_policy_next(struct seq_file *m, void *v, loff_t *pos);
> > void ima_policy_stop(struct seq_file *m, void *v);
> > int ima_policy_show(struct seq_file *m, void *v);
> > +void ima_measure_loaded_policy(void);
> >
> > /* Appraise integrity measurements */
> > #define IMA_APPRAISE_ENFORCE 0x01
> > diff --git a/security/integrity/ima/ima_efi.c
> > b/security/integrity/ima/ima_efi.c
> > index 138029bfcce1..8e9f85ec9a86 100644
> > --- a/security/integrity/ima/ima_efi.c
> > +++ b/security/integrity/ima/ima_efi.c
> > @@ -60,6 +60,8 @@ static const char * const sb_arch_rules[] = {
> > #endif
> > #if IS_ENABLED(CONFIG_INTEGRITY_MACHINE_KEYRING) &&
> > IS_ENABLED(CONFIG_IMA_KEYRINGS_PERMIT_SIGNED_BY_BUILTIN_OR_SECONDARY)
> > "appraise func=POLICY_CHECK appraise_type=imasig",
> > +#else
> > + "measure func=CRITICAL_DATA label=ima_policy",
> > #endif
>
> None of the other arch "measure" policy rules are conditional. Should the
> new
> "measure" rule be limited?
This condition aims to avoid measuring the policy loaded even if a signed policy
is required. In that case, it would not be possible to directly write the policy
in the securityfs file.
> > "measure func=MODULE_CHECK",
> > NULL
> > diff --git a/security/integrity/ima/ima_fs.c
> > b/security/integrity/ima/ima_fs.c
> > index 012a58959ff0..75cb308cf01f 100644
> > --- a/security/integrity/ima/ima_fs.c
> > +++ b/security/integrity/ima/ima_fs.c
> > @@ -476,6 +476,7 @@ static int ima_release_policy(struct inode *inode,
> > struct file *file)
> > }
> >
> > ima_update_policy();
> > + ima_measure_loaded_policy();
> > #if !defined(CONFIG_IMA_WRITE_POLICY) && !defined(CONFIG_IMA_READ_POLICY)
> > securityfs_remove(file->f_path.dentry);
> > #elif defined(CONFIG_IMA_WRITE_POLICY)
> > diff --git a/security/integrity/ima/ima_policy.c
> > b/security/integrity/ima/ima_policy.c
> > index bf2d7ba4c14a..e0b4dae922b6 100644
> > --- a/security/integrity/ima/ima_policy.c
> > +++ b/security/integrity/ima/ima_policy.c
> > @@ -17,6 +17,7 @@
> > #include <linux/slab.h>
> > #include <linux/rculist.h>
> > #include <linux/seq_file.h>
> > +#include <linux/vmalloc.h>
> > #include <linux/ima.h>
> >
> > #include "ima.h"
> > @@ -2022,7 +2023,6 @@ const char *const func_tokens[] = {
> > __ima_hooks(__ima_hook_stringify)
> > };
> >
> > -#ifdef CONFIG_IMA_READ_POLICY
>
> Removing the ifdef, here, does not affect viewing the IMA measurement lists,
> but
> allows copying and measuring the policy rules. Please include a comment in
> the
> patch description.
Sure, will add in the next version.
> > enum {
> > mask_exec = 0, mask_write, mask_read, mask_append
> > };
> > @@ -2324,7 +2324,6 @@ int ima_policy_show(struct seq_file *m, void *v)
> > seq_puts(m, "\n");
> > return 0;
> > }
> > -#endif /* CONFIG_IMA_READ_POLICY */
> >
> > #if defined(CONFIG_IMA_APPRAISE) &&
> > defined(CONFIG_INTEGRITY_TRUSTED_KEYRING)
> > /*
> > @@ -2381,3 +2380,55 @@ bool ima_appraise_signature(enum kernel_read_file_id
> > id)
> > return found;
> > }
> > #endif /* CONFIG_IMA_APPRAISE && CONFIG_INTEGRITY_TRUSTED_KEYRING */
> > +
>
> Please add kernel-doc here, something like:
>
> /**
> * ima_measure_loaded_policy - measure the active IMA policy ruleset
> *
> * Must be called with ima_write_mutex held, as it performs two
> * separate RCU read passes over ima_rules and relies on the mutex
> * to prevent concurrent policy updates between them.
> */
Sure, thank you. If it is ok for you I can directly add what you suggested.
> > +void ima_measure_loaded_policy(void)
> > +{
> > + const char *event_name = "ima_policy_loaded";
> > + const char *op = "measure_loaded_ima_policy";
> > + const char *audit_cause = "ENOMEM";
> > + struct ima_rule_entry *rule_entry;
> > + struct list_head *ima_rules_tmp;
> > + struct seq_file file;
> > + int result = -ENOMEM;
> > + size_t file_len;
> > + char rule[255];
>
> The 255-byte buffer may be insufficient for custom policy rules that include
> additional fields such as LSM labels and other file metadata, unlike the
> simpler
> built-in and architecture-specific rules. Please increase the buffer size to
> accommodate the worst-case serialized rule length.
Yes, I wrongly took as reference the arch policy rules case. I don't know if the
worst-case can be precisely estimated. I could increase the buffer size and
check in any case if seq_has_overflowed(). Could it be an idea?
> > +
> > + /* calculate IMA policy rules memory size */
> > + file.buf = rule;
> > + file.read_pos = 0;
> > + file.size = 255;
> > + file.count = 0;
> > +
>
> Please add "lockdep_assert_held(&ima_write_mutex);" here.
Yes, and this would actually fail because I'm not acquiring ima_write_mutex in
ima_release_policy().
> > + rcu_read_lock();
> > + ima_rules_tmp = rcu_dereference(ima_rules);
> > + list_for_each_entry_rcu(rule_entry, ima_rules_tmp, list) {
> > + ima_policy_show(&file, rule_entry);
> > + file_len += file.count;
> > + file.count = 0;
> > + }
>
> Variables defined on the stack need to be initialized before being used.
> Please
> iniitalize file_len to zero.
Sure, will fix that.
Thank you,
Enrico
> > + rcu_read_unlock();
> > +
> > + /* copy IMA policy rules to a buffer for measuring */
> > + file.buf = vmalloc(file_len);
> > + if (!file.buf) {
> > + integrity_audit_msg(AUDIT_INTEGRITY_PCR, NULL, event_name,
> > + op, audit_cause, result, 1);
> > + return;
> > + }
> > +
> > + file.read_pos = 0;
> > + file.size = file_len;
> > + file.count = 0;
> > +
> > + rcu_read_lock();
> > + ima_rules_tmp = rcu_dereference(ima_rules);
> > + list_for_each_entry_rcu(rule_entry, ima_rules_tmp, list) {
> > + ima_policy_show(&file, rule_entry);
> > + }
> > + rcu_read_unlock();
> > +
> > + ima_measure_critical_data("ima_policy", event_name, file.buf,
> > + file.count, false, NULL, 0);
> > +
> > + vfree(file.buf);
> > +}
>
> Thanks,
>
> Mimi
^ permalink raw reply
* Re: [PATCH v2] tpm: fix event_size output in tpm1_binary_bios_measurements_show
From: Thorsten Blum @ 2026-06-10 20:35 UTC (permalink / raw)
To: Jarkko Sakkinen
Cc: Peter Huewe, Jason Gunthorpe, Colin Ian King, Harald Hoyer,
stable, linux-integrity, linux-kernel
In-Reply-To: <ahBSJ-hVcVHEWTeZ@kernel.org>
On Fri, May 22, 2026 at 03:55:03PM +0300, Jarkko Sakkinen wrote:
> On Fri, May 22, 2026 at 11:44:38AM +0200, Thorsten Blum wrote:
> > Commit 186d124f07da ("tpm_eventlog.c: fix binary_bios_measurements")
> > split the output to write the endian-converted event header first and
> > then the variable-length event data.
> >
> > However, the split was at sizeof(struct tcpa_event) - 1, even though
> > event_data was a zero-length array, and later a flexible array member,
> > both of which already excluded the event data.
> >
> > Therefore, the current code writes the first three bytes of event_size
> > from the endian-converted header and then the last byte from the raw
> > header, which can emit a corrupted event_size on PPC64, where
> > do_endian_conversion() maps to be32_to_cpu().
> >
> > Split one byte later to write the full endian-converted header first,
> > followed by the variable-length event->event_data.
> >
> > Fixes: 186d124f07da ("tpm_eventlog.c: fix binary_bios_measurements")
> > Cc: stable@vger.kernel.org
> > Signed-off-by: Thorsten Blum <thorsten.blum@linux.dev>
> > ---
> > Changes in v2:
> > - Minimal fix without using seq_write()
> > - v1: https://lore.kernel.org/lkml/20260521093639.162095-3-thorsten.blum@linux.dev/
> > ---
> > drivers/char/tpm/eventlog/tpm1.c | 4 ++--
> > 1 file changed, 2 insertions(+), 2 deletions(-)
> >
> > diff --git a/drivers/char/tpm/eventlog/tpm1.c b/drivers/char/tpm/eventlog/tpm1.c
> > index e7913b2853d5..0397e3361020 100644
> > --- a/drivers/char/tpm/eventlog/tpm1.c
> > +++ b/drivers/char/tpm/eventlog/tpm1.c
> > @@ -236,12 +236,12 @@ static int tpm1_binary_bios_measurements_show(struct seq_file *m, void *v)
> >
> > temp_ptr = (char *) &temp_event;
> >
> > - for (i = 0; i < (sizeof(struct tcpa_event) - 1) ; i++)
> > + for (i = 0; i < sizeof(struct tcpa_event); i++)
> > seq_putc(m, temp_ptr[i]);
> >
> > temp_ptr = (char *) v;
> >
> > - for (i = (sizeof(struct tcpa_event) - 1);
> > + for (i = sizeof(struct tcpa_event);
> > i < (sizeof(struct tcpa_event) + temp_event.event_size); i++)
> > seq_putc(m, temp_ptr[i]);
> >
>
> This was really good catch, thank you. I'll apply in a minute.
Has this already been applied somewhere?
Thanks,
Thorsten
^ permalink raw reply
* Re: [RFC][PATCH v3 2/2] ima: measure buffer sent to securityfs policy file
From: Mimi Zohar @ 2026-06-10 18:59 UTC (permalink / raw)
To: Enrico Bravi, linux-integrity, dmitry.kasatkin, roberto.sassu
Cc: eric.snowberg
In-Reply-To: <20260526135118.289633-3-enrico.bravi@polito.it>
On Tue, 2026-05-26 at 15:51 +0200, Enrico Bravi wrote:
> When a signed policy is not mandatory, it is possible to write the IMA
> policy directly on the corresponding securityfs file:
>
> echo -e "measure func=BPRM_CHECK mask=MAY_EXEC\n" \
> "audit func=BPRM_CHECK mask=MAY_EXEC\n" \
> > /sys/kernel/security/ima/policy
Or by cat'ing the entire IMA custom policy file.
>
> Add input buffer measurement, regardless of whether the new policy
> will be accepted or not, that can be caught when
> 'measure func=POLICY_CHECK' is enabled (e.g., ima_policy=tcb).
Enrco, Roberto, a reason for measuring invalid or malformed IMA policy rules
needs to be provided here.
In addition to the "ima_policy" critical data, why is this mechanism needed?
> The
> measurement template is forced to ima-buf.
Please include directions for verifying the measurement record here in the patch
description.
>
> Suggested-by: Roberto Sassu <roberto.sassu@huawei.com>
> Signed-off-by: Enrico Bravi <enrico.bravi@polito.it>
> ---
> security/integrity/ima/ima.h | 1 +
> security/integrity/ima/ima_fs.c | 1 +
> security/integrity/ima/ima_main.c | 19 +++++++++++++++++++
> security/integrity/ima/ima_policy.c | 3 +++
> 4 files changed, 24 insertions(+)
>
> diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
> index a223d3f30d88..320c80a1a847 100644
> --- a/security/integrity/ima/ima.h
> +++ b/security/integrity/ima/ima.h
> @@ -426,6 +426,7 @@ void *ima_policy_next(struct seq_file *m, void *v, loff_t *pos);
> void ima_policy_stop(struct seq_file *m, void *v);
> int ima_policy_show(struct seq_file *m, void *v);
> void ima_measure_loaded_policy(void);
> +int ima_measure_policy_buf(const char *buf, size_t buf_len);
>
> /* Appraise integrity measurements */
> #define IMA_APPRAISE_ENFORCE 0x01
> diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
> index 75cb308cf01f..601718e02429 100644
> --- a/security/integrity/ima/ima_fs.c
> +++ b/security/integrity/ima/ima_fs.c
> @@ -362,6 +362,7 @@ static ssize_t ima_write_policy(struct file *file, const char __user *buf,
> 1, 0);
> result = -EACCES;
> } else {
> + ima_measure_policy_buf(data, datalen);
> result = ima_parse_add_rule(data);
> }
> mutex_unlock(&ima_write_mutex);
> diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
> index 1d6229b156fb..174110da0030 100644
> --- a/security/integrity/ima/ima_main.c
> +++ b/security/integrity/ima/ima_main.c
> @@ -1204,6 +1204,25 @@ int ima_measure_critical_data(const char *event_label,
> }
> EXPORT_SYMBOL_GPL(ima_measure_critical_data);
>
> +/**
> + * ima_measure_policy_buf - Measure the policy write buffer
> + * @buf: pointer to the buffer containing the policy write data
> + * @buf_len: size of the buffer
> + *
> + * Measure the buffer sent to the IMA policy securityfs file.
> + *
> + * Return 0 on success, a negative value otherwise.
> + */
> +int ima_measure_policy_buf(const char *buf, size_t buf_len)
> +{
> + if (!buf || !buf_len)
> + return -ENOPARAM;
Please return -EINVAL.
> +
> + return process_buffer_measurement(&nop_mnt_idmap, NULL, buf, buf_len,
> + "ima_write_policy_buf", POLICY_CHECK,
> + 0, NULL, false, NULL, 0);
Parallel to "ima_policy_loaded" consider naming the record as
"ima_policy_written".
The indentation is off by a character.
> +}
> +
> #ifdef CONFIG_INTEGRITY_ASYMMETRIC_KEYS
>
> /**
[...]
thanks,
Mimi
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox