* [PATCH v6 2/6] powerpc/pseries: move the PLPKS config inside its own sysfs directory
From: Srish Srinivasan @ 2026-02-01 13:59 UTC (permalink / raw)
To: linux-integrity, keyrings, linuxppc-dev
Cc: maddy, mpe, npiggin, christophe.leroy, James.Bottomley, jarkko,
zohar, nayna, rnsastry, linux-kernel, linux-security-module,
ssrish
In-Reply-To: <20260201135930.898721-1-ssrish@linux.ibm.com>
The /sys/firmware/secvar/config directory represents Power LPAR Platform
KeyStore (PLPKS) configuration properties such as max_object_size, signed_
update_algorithms, supported_policies, total_size, used_space, and version.
These attributes describe the PLPKS, and not the secure boot variables
(secvars).
Create /sys/firmware/plpks directory and move the PLPKS config inside this
directory. For backwards compatibility, create a soft link from the secvar
sysfs directory to this config and emit a warning stating that the older
sysfs path has been deprecated. Separate out the plpks specific
documentation from secvar.
Signed-off-by: Srish Srinivasan <ssrish@linux.ibm.com>
Reviewed-by: Mimi Zohar <zohar@linux.ibm.com>
Reviewed-by: Nayna Jain <nayna@linux.ibm.com>
Tested-by: Nayna Jain <nayna@linux.ibm.com>
---
.../ABI/testing/sysfs-firmware-plpks | 50 ++++++++++
Documentation/ABI/testing/sysfs-secvar | 65 -------------
arch/powerpc/include/asm/plpks.h | 5 +
arch/powerpc/include/asm/secvar.h | 1 -
arch/powerpc/kernel/secvar-sysfs.c | 21 ++---
arch/powerpc/platforms/pseries/Makefile | 2 +-
arch/powerpc/platforms/pseries/plpks-secvar.c | 29 ------
arch/powerpc/platforms/pseries/plpks-sysfs.c | 94 +++++++++++++++++++
8 files changed, 156 insertions(+), 111 deletions(-)
create mode 100644 Documentation/ABI/testing/sysfs-firmware-plpks
create mode 100644 arch/powerpc/platforms/pseries/plpks-sysfs.c
diff --git a/Documentation/ABI/testing/sysfs-firmware-plpks b/Documentation/ABI/testing/sysfs-firmware-plpks
new file mode 100644
index 000000000000..af0353f34115
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-firmware-plpks
@@ -0,0 +1,50 @@
+What: /sys/firmware/plpks/config
+Date: February 2023
+Contact: Nayna Jain <nayna@linux.ibm.com>
+Description: This optional directory contains read-only config attributes as
+ defined by the PLPKS implementation. All data is in ASCII
+ format.
+
+What: /sys/firmware/plpks/config/version
+Date: February 2023
+Contact: Nayna Jain <nayna@linux.ibm.com>
+Description: Config version as reported by the hypervisor in ASCII decimal
+ format.
+
+What: /sys/firmware/plpks/config/max_object_size
+Date: February 2023
+Contact: Nayna Jain <nayna@linux.ibm.com>
+Description: Maximum allowed size of objects in the keystore in bytes,
+ represented in ASCII decimal format.
+
+ This is not necessarily the same as the max size that can be
+ written to an update file as writes can contain more than
+ object data, you should use the size of the update file for
+ that purpose.
+
+What: /sys/firmware/plpks/config/total_size
+Date: February 2023
+Contact: Nayna Jain <nayna@linux.ibm.com>
+Description: Total size of the PLPKS in bytes, represented in ASCII decimal
+ format.
+
+What: /sys/firmware/plpks/config/used_space
+Date: February 2023
+Contact: Nayna Jain <nayna@linux.ibm.com>
+Description: Current space consumed by the key store, in bytes, represented
+ in ASCII decimal format.
+
+What: /sys/firmware/plpks/config/supported_policies
+Date: February 2023
+Contact: Nayna Jain <nayna@linux.ibm.com>
+Description: Bitmask of supported policy flags by the hypervisor, represented
+ as an 8 byte hexadecimal ASCII string. Consult the hypervisor
+ documentation for what these flags are.
+
+What: /sys/firmware/plpks/config/signed_update_algorithms
+Date: February 2023
+Contact: Nayna Jain <nayna@linux.ibm.com>
+Description: Bitmask of flags indicating which algorithms the hypervisor
+ supports for signed update of objects, represented as a 16 byte
+ hexadecimal ASCII string. Consult the hypervisor documentation
+ for what these flags mean.
diff --git a/Documentation/ABI/testing/sysfs-secvar b/Documentation/ABI/testing/sysfs-secvar
index 1016967a730f..c52a5fd15709 100644
--- a/Documentation/ABI/testing/sysfs-secvar
+++ b/Documentation/ABI/testing/sysfs-secvar
@@ -63,68 +63,3 @@ Contact: Nayna Jain <nayna@linux.ibm.com>
Description: A write-only file that is used to submit the new value for the
variable. The size of the file represents the maximum size of
the variable data that can be written.
-
-What: /sys/firmware/secvar/config
-Date: February 2023
-Contact: Nayna Jain <nayna@linux.ibm.com>
-Description: This optional directory contains read-only config attributes as
- defined by the secure variable implementation. All data is in
- ASCII format. The directory is only created if the backing
- implementation provides variables to populate it, which at
- present is only PLPKS on the pseries platform.
-
-What: /sys/firmware/secvar/config/version
-Date: February 2023
-Contact: Nayna Jain <nayna@linux.ibm.com>
-Description: Config version as reported by the hypervisor in ASCII decimal
- format.
-
- Currently only provided by PLPKS on the pseries platform.
-
-What: /sys/firmware/secvar/config/max_object_size
-Date: February 2023
-Contact: Nayna Jain <nayna@linux.ibm.com>
-Description: Maximum allowed size of objects in the keystore in bytes,
- represented in ASCII decimal format.
-
- This is not necessarily the same as the max size that can be
- written to an update file as writes can contain more than
- object data, you should use the size of the update file for
- that purpose.
-
- Currently only provided by PLPKS on the pseries platform.
-
-What: /sys/firmware/secvar/config/total_size
-Date: February 2023
-Contact: Nayna Jain <nayna@linux.ibm.com>
-Description: Total size of the PLPKS in bytes, represented in ASCII decimal
- format.
-
- Currently only provided by PLPKS on the pseries platform.
-
-What: /sys/firmware/secvar/config/used_space
-Date: February 2023
-Contact: Nayna Jain <nayna@linux.ibm.com>
-Description: Current space consumed by the key store, in bytes, represented
- in ASCII decimal format.
-
- Currently only provided by PLPKS on the pseries platform.
-
-What: /sys/firmware/secvar/config/supported_policies
-Date: February 2023
-Contact: Nayna Jain <nayna@linux.ibm.com>
-Description: Bitmask of supported policy flags by the hypervisor,
- represented as an 8 byte hexadecimal ASCII string. Consult the
- hypervisor documentation for what these flags are.
-
- Currently only provided by PLPKS on the pseries platform.
-
-What: /sys/firmware/secvar/config/signed_update_algorithms
-Date: February 2023
-Contact: Nayna Jain <nayna@linux.ibm.com>
-Description: Bitmask of flags indicating which algorithms the hypervisor
- supports for signed update of objects, represented as a 16 byte
- hexadecimal ASCII string. Consult the hypervisor documentation
- for what these flags mean.
-
- Currently only provided by PLPKS on the pseries platform.
diff --git a/arch/powerpc/include/asm/plpks.h b/arch/powerpc/include/asm/plpks.h
index f303922bf622..8829a13bfda0 100644
--- a/arch/powerpc/include/asm/plpks.h
+++ b/arch/powerpc/include/asm/plpks.h
@@ -13,6 +13,7 @@
#include <linux/types.h>
#include <linux/list.h>
+#include <linux/kobject.h>
// Object policy flags from supported_policies
#define PLPKS_OSSECBOOTAUDIT PPC_BIT32(1) // OS secure boot must be audit/enforce
@@ -107,11 +108,15 @@ u16 plpks_get_passwordlen(void);
void plpks_early_init_devtree(void);
int plpks_populate_fdt(void *fdt);
+
+int plpks_config_create_softlink(struct kobject *from);
#else // CONFIG_PSERIES_PLPKS
static inline bool plpks_is_available(void) { return false; }
static inline u16 plpks_get_passwordlen(void) { BUILD_BUG(); }
static inline void plpks_early_init_devtree(void) { }
static inline int plpks_populate_fdt(void *fdt) { BUILD_BUG(); }
+static inline int plpks_config_create_softlink(struct kobject *from)
+ { return 0; }
#endif // CONFIG_PSERIES_PLPKS
#endif // _ASM_POWERPC_PLPKS_H
diff --git a/arch/powerpc/include/asm/secvar.h b/arch/powerpc/include/asm/secvar.h
index 4828e0ab7e3c..fd5006307f2a 100644
--- a/arch/powerpc/include/asm/secvar.h
+++ b/arch/powerpc/include/asm/secvar.h
@@ -20,7 +20,6 @@ struct secvar_operations {
int (*set)(const char *key, u64 key_len, u8 *data, u64 data_size);
ssize_t (*format)(char *buf, size_t bufsize);
int (*max_size)(u64 *max_size);
- const struct attribute **config_attrs;
// NULL-terminated array of fixed variable names
// Only used if get_next() isn't provided
diff --git a/arch/powerpc/kernel/secvar-sysfs.c b/arch/powerpc/kernel/secvar-sysfs.c
index ec900bce0257..4111b21962eb 100644
--- a/arch/powerpc/kernel/secvar-sysfs.c
+++ b/arch/powerpc/kernel/secvar-sysfs.c
@@ -12,6 +12,7 @@
#include <linux/string.h>
#include <linux/of.h>
#include <asm/secvar.h>
+#include <asm/plpks.h>
#define NAME_MAX_SIZE 1024
@@ -145,19 +146,6 @@ static __init int update_kobj_size(void)
return 0;
}
-static __init int secvar_sysfs_config(struct kobject *kobj)
-{
- struct attribute_group config_group = {
- .name = "config",
- .attrs = (struct attribute **)secvar_ops->config_attrs,
- };
-
- if (secvar_ops->config_attrs)
- return sysfs_create_group(kobj, &config_group);
-
- return 0;
-}
-
static __init int add_var(const char *name)
{
struct kobject *kobj;
@@ -260,12 +248,15 @@ static __init int secvar_sysfs_init(void)
goto err;
}
- rc = secvar_sysfs_config(secvar_kobj);
+ rc = plpks_config_create_softlink(secvar_kobj);
if (rc) {
- pr_err("Failed to create config directory\n");
+ pr_err("Failed to create softlink to PLPKS config directory");
goto err;
}
+ pr_info("/sys/firmware/secvar/config is now deprecated.\n");
+ pr_info("Will be removed in future versions.\n");
+
if (secvar_ops->get_next)
rc = secvar_sysfs_load();
else
diff --git a/arch/powerpc/platforms/pseries/Makefile b/arch/powerpc/platforms/pseries/Makefile
index 931ebaa474c8..3ced289a675b 100644
--- a/arch/powerpc/platforms/pseries/Makefile
+++ b/arch/powerpc/platforms/pseries/Makefile
@@ -30,7 +30,7 @@ obj-$(CONFIG_PAPR_SCM) += papr_scm.o
obj-$(CONFIG_PPC_SPLPAR) += vphn.o
obj-$(CONFIG_PPC_SVM) += svm.o
obj-$(CONFIG_FA_DUMP) += rtas-fadump.o
-obj-$(CONFIG_PSERIES_PLPKS) += plpks.o
+obj-$(CONFIG_PSERIES_PLPKS) += plpks.o plpks-sysfs.o
obj-$(CONFIG_PPC_SECURE_BOOT) += plpks-secvar.o
obj-$(CONFIG_PSERIES_PLPKS_SED) += plpks_sed_ops.o
obj-$(CONFIG_SUSPEND) += suspend.o
diff --git a/arch/powerpc/platforms/pseries/plpks-secvar.c b/arch/powerpc/platforms/pseries/plpks-secvar.c
index f9e9cc40c9d0..a50ff6943d80 100644
--- a/arch/powerpc/platforms/pseries/plpks-secvar.c
+++ b/arch/powerpc/platforms/pseries/plpks-secvar.c
@@ -20,33 +20,6 @@
#include <asm/secvar.h>
#include <asm/plpks.h>
-// Config attributes for sysfs
-#define PLPKS_CONFIG_ATTR(name, fmt, func) \
- static ssize_t name##_show(struct kobject *kobj, \
- struct kobj_attribute *attr, \
- char *buf) \
- { \
- return sysfs_emit(buf, fmt, func()); \
- } \
- static struct kobj_attribute attr_##name = __ATTR_RO(name)
-
-PLPKS_CONFIG_ATTR(version, "%u\n", plpks_get_version);
-PLPKS_CONFIG_ATTR(max_object_size, "%u\n", plpks_get_maxobjectsize);
-PLPKS_CONFIG_ATTR(total_size, "%u\n", plpks_get_totalsize);
-PLPKS_CONFIG_ATTR(used_space, "%u\n", plpks_get_usedspace);
-PLPKS_CONFIG_ATTR(supported_policies, "%08x\n", plpks_get_supportedpolicies);
-PLPKS_CONFIG_ATTR(signed_update_algorithms, "%016llx\n", plpks_get_signedupdatealgorithms);
-
-static const struct attribute *config_attrs[] = {
- &attr_version.attr,
- &attr_max_object_size.attr,
- &attr_total_size.attr,
- &attr_used_space.attr,
- &attr_supported_policies.attr,
- &attr_signed_update_algorithms.attr,
- NULL,
-};
-
static u32 get_policy(const char *name)
{
if ((strcmp(name, "db") == 0) ||
@@ -225,7 +198,6 @@ static const struct secvar_operations plpks_secvar_ops_static = {
.set = plpks_set_variable,
.format = plpks_secvar_format,
.max_size = plpks_max_size,
- .config_attrs = config_attrs,
.var_names = plpks_var_names_static,
};
@@ -234,7 +206,6 @@ static const struct secvar_operations plpks_secvar_ops_dynamic = {
.set = plpks_set_variable,
.format = plpks_secvar_format,
.max_size = plpks_max_size,
- .config_attrs = config_attrs,
.var_names = plpks_var_names_dynamic,
};
diff --git a/arch/powerpc/platforms/pseries/plpks-sysfs.c b/arch/powerpc/platforms/pseries/plpks-sysfs.c
new file mode 100644
index 000000000000..01d526185783
--- /dev/null
+++ b/arch/powerpc/platforms/pseries/plpks-sysfs.c
@@ -0,0 +1,94 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2025 IBM Corporation, Srish Srinivasan <ssrish@linux.ibm.com>
+ *
+ * This code exposes PLPKS config to user via sysfs
+ */
+
+#define pr_fmt(fmt) "plpks-sysfs: "fmt
+
+#include <linux/init.h>
+#include <linux/printk.h>
+#include <linux/types.h>
+#include <asm/machdep.h>
+#include <asm/plpks.h>
+
+/* config attributes for sysfs */
+#define PLPKS_CONFIG_ATTR(name, fmt, func) \
+ static ssize_t name##_show(struct kobject *kobj, \
+ struct kobj_attribute *attr, \
+ char *buf) \
+ { \
+ return sysfs_emit(buf, fmt, func()); \
+ } \
+ static struct kobj_attribute attr_##name = __ATTR_RO(name)
+
+PLPKS_CONFIG_ATTR(version, "%u\n", plpks_get_version);
+PLPKS_CONFIG_ATTR(max_object_size, "%u\n", plpks_get_maxobjectsize);
+PLPKS_CONFIG_ATTR(total_size, "%u\n", plpks_get_totalsize);
+PLPKS_CONFIG_ATTR(used_space, "%u\n", plpks_get_usedspace);
+PLPKS_CONFIG_ATTR(supported_policies, "%08x\n", plpks_get_supportedpolicies);
+PLPKS_CONFIG_ATTR(signed_update_algorithms, "%016llx\n",
+ plpks_get_signedupdatealgorithms);
+
+static const struct attribute *config_attrs[] = {
+ &attr_version.attr,
+ &attr_max_object_size.attr,
+ &attr_total_size.attr,
+ &attr_used_space.attr,
+ &attr_supported_policies.attr,
+ &attr_signed_update_algorithms.attr,
+ NULL,
+};
+
+static struct kobject *plpks_kobj, *plpks_config_kobj;
+
+int plpks_config_create_softlink(struct kobject *from)
+{
+ if (!plpks_config_kobj)
+ return -EINVAL;
+ return sysfs_create_link(from, plpks_config_kobj, "config");
+}
+
+static __init int plpks_sysfs_config(struct kobject *kobj)
+{
+ struct attribute_group config_group = {
+ .name = NULL,
+ .attrs = (struct attribute **)config_attrs,
+ };
+
+ return sysfs_create_group(kobj, &config_group);
+}
+
+static __init int plpks_sysfs_init(void)
+{
+ int rc;
+
+ if (!plpks_is_available())
+ return -ENODEV;
+
+ plpks_kobj = kobject_create_and_add("plpks", firmware_kobj);
+ if (!plpks_kobj) {
+ pr_err("Failed to create plpks kobj\n");
+ return -ENOMEM;
+ }
+
+ plpks_config_kobj = kobject_create_and_add("config", plpks_kobj);
+ if (!plpks_config_kobj) {
+ pr_err("Failed to create plpks config kobj\n");
+ kobject_put(plpks_kobj);
+ return -ENOMEM;
+ }
+
+ rc = plpks_sysfs_config(plpks_config_kobj);
+ if (rc) {
+ pr_err("Failed to create attribute group for plpks config\n");
+ kobject_put(plpks_config_kobj);
+ kobject_put(plpks_kobj);
+ return rc;
+ }
+
+ return 0;
+}
+
+machine_subsys_initcall(pseries, plpks_sysfs_init);
--
2.47.3
^ permalink raw reply related
* [PATCH v6 5/6] keys/trusted_keys: establish PKWM as a trusted source
From: Srish Srinivasan @ 2026-02-01 13:59 UTC (permalink / raw)
To: linux-integrity, keyrings, linuxppc-dev
Cc: maddy, mpe, npiggin, christophe.leroy, James.Bottomley, jarkko,
zohar, nayna, rnsastry, linux-kernel, linux-security-module,
ssrish
In-Reply-To: <20260201135930.898721-1-ssrish@linux.ibm.com>
The wrapping key does not exist by default and is generated by the
hypervisor as a part of PKWM initialization. This key is then persisted by
the hypervisor and is used to wrap trusted keys. These are variable length
symmetric keys, which in the case of PowerVM Key Wrapping Module (PKWM) are
generated using the kernel RNG. PKWM can be used as a trust source through
the following example keyctl commands:
keyctl add trusted my_trusted_key "new 32" @u
Use the wrap_flags command option to set the secure boot requirement for
the wrapping request through the following keyctl commands
case1: no secure boot requirement. (default)
keyctl usage: keyctl add trusted my_trusted_key "new 32" @u
OR
keyctl add trusted my_trusted_key "new 32 wrap_flags=0x00" @u
case2: secure boot required to in either audit or enforce mode. set bit 0
keyctl usage: keyctl add trusted my_trusted_key "new 32 wrap_flags=0x01" @u
case3: secure boot required to be in enforce mode. set bit 1
keyctl usage: keyctl add trusted my_trusted_key "new 32 wrap_flags=0x02" @u
NOTE:
-> Setting the secure boot requirement is NOT a must.
-> Only either of the secure boot requirement options should be set. Not
both.
-> All the other bits are required to be not set.
-> Set the kernel parameter trusted.source=pkwm to choose PKWM as the
backend for trusted keys implementation.
-> CONFIG_PSERIES_PLPKS must be enabled to build PKWM.
Add PKWM, which is a combination of IBM PowerVM and Power LPAR Platform
KeyStore, as a new trust source for trusted keys.
Signed-off-by: Srish Srinivasan <ssrish@linux.ibm.com>
Reviewed-by: Mimi Zohar <zohar@linux.ibm.com>
Reviewed-by: Nayna Jain <nayna@linux.ibm.com>
Reviewed-by: Jarkko Sakkinen <jarkko@kernel.org>
Tested-by: Nayna Jain <nayna@linux.ibm.com>
---
MAINTAINERS | 9 +
include/keys/trusted-type.h | 7 +-
include/keys/trusted_pkwm.h | 33 ++++
security/keys/trusted-keys/Kconfig | 8 +
security/keys/trusted-keys/Makefile | 2 +
security/keys/trusted-keys/trusted_core.c | 6 +-
security/keys/trusted-keys/trusted_pkwm.c | 190 ++++++++++++++++++++++
7 files changed, 253 insertions(+), 2 deletions(-)
create mode 100644 include/keys/trusted_pkwm.h
create mode 100644 security/keys/trusted-keys/trusted_pkwm.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 0efa8cc6775b..b037648e66fc 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -14013,6 +14013,15 @@ S: Supported
F: include/keys/trusted_dcp.h
F: security/keys/trusted-keys/trusted_dcp.c
+KEYS-TRUSTED-PLPKS
+M: Srish Srinivasan <ssrish@linux.ibm.com>
+M: Nayna Jain <nayna@linux.ibm.com>
+L: linux-integrity@vger.kernel.org
+L: keyrings@vger.kernel.org
+S: Supported
+F: include/keys/trusted_pkwm.h
+F: security/keys/trusted-keys/trusted_pkwm.c
+
KEYS-TRUSTED-TEE
M: Sumit Garg <sumit.garg@kernel.org>
L: linux-integrity@vger.kernel.org
diff --git a/include/keys/trusted-type.h b/include/keys/trusted-type.h
index 4eb64548a74f..03527162613f 100644
--- a/include/keys/trusted-type.h
+++ b/include/keys/trusted-type.h
@@ -19,7 +19,11 @@
#define MIN_KEY_SIZE 32
#define MAX_KEY_SIZE 128
-#define MAX_BLOB_SIZE 512
+#if IS_ENABLED(CONFIG_TRUSTED_KEYS_PKWM)
+#define MAX_BLOB_SIZE 1152
+#else
+#define MAX_BLOB_SIZE 512
+#endif
#define MAX_PCRINFO_SIZE 64
#define MAX_DIGEST_SIZE 64
@@ -46,6 +50,7 @@ struct trusted_key_options {
uint32_t policydigest_len;
unsigned char policydigest[MAX_DIGEST_SIZE];
uint32_t policyhandle;
+ void *private;
};
struct trusted_key_ops {
diff --git a/include/keys/trusted_pkwm.h b/include/keys/trusted_pkwm.h
new file mode 100644
index 000000000000..4035b9776394
--- /dev/null
+++ b/include/keys/trusted_pkwm.h
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __PKWM_TRUSTED_KEY_H
+#define __PKWM_TRUSTED_KEY_H
+
+#include <keys/trusted-type.h>
+#include <linux/bitops.h>
+#include <linux/printk.h>
+
+extern struct trusted_key_ops pkwm_trusted_key_ops;
+
+struct trusted_pkwm_options {
+ u16 wrap_flags;
+};
+
+static inline void dump_options(struct trusted_key_options *o)
+{
+ const struct trusted_pkwm_options *pkwm;
+ bool sb_audit_or_enforce_bit;
+ bool sb_enforce_bit;
+
+ pkwm = o->private;
+ sb_audit_or_enforce_bit = pkwm->wrap_flags & BIT(0);
+ sb_enforce_bit = pkwm->wrap_flags & BIT(1);
+
+ if (sb_audit_or_enforce_bit)
+ pr_debug("secure boot mode required: audit or enforce");
+ else if (sb_enforce_bit)
+ pr_debug("secure boot mode required: enforce");
+ else
+ pr_debug("secure boot mode required: disabled");
+}
+
+#endif
diff --git a/security/keys/trusted-keys/Kconfig b/security/keys/trusted-keys/Kconfig
index 204a68c1429d..9e00482d886a 100644
--- a/security/keys/trusted-keys/Kconfig
+++ b/security/keys/trusted-keys/Kconfig
@@ -46,6 +46,14 @@ config TRUSTED_KEYS_DCP
help
Enable use of NXP's DCP (Data Co-Processor) as trusted key backend.
+config TRUSTED_KEYS_PKWM
+ bool "PKWM-based trusted keys"
+ depends on PSERIES_PLPKS >= TRUSTED_KEYS
+ default y
+ select HAVE_TRUSTED_KEYS
+ help
+ Enable use of IBM PowerVM Key Wrapping Module (PKWM) as a trusted key backend.
+
if !HAVE_TRUSTED_KEYS
comment "No trust source selected!"
endif
diff --git a/security/keys/trusted-keys/Makefile b/security/keys/trusted-keys/Makefile
index f0f3b27f688b..5fc053a21dad 100644
--- a/security/keys/trusted-keys/Makefile
+++ b/security/keys/trusted-keys/Makefile
@@ -16,3 +16,5 @@ trusted-$(CONFIG_TRUSTED_KEYS_TEE) += trusted_tee.o
trusted-$(CONFIG_TRUSTED_KEYS_CAAM) += trusted_caam.o
trusted-$(CONFIG_TRUSTED_KEYS_DCP) += trusted_dcp.o
+
+trusted-$(CONFIG_TRUSTED_KEYS_PKWM) += trusted_pkwm.o
diff --git a/security/keys/trusted-keys/trusted_core.c b/security/keys/trusted-keys/trusted_core.c
index b1680ee53f86..2d328de170e8 100644
--- a/security/keys/trusted-keys/trusted_core.c
+++ b/security/keys/trusted-keys/trusted_core.c
@@ -12,6 +12,7 @@
#include <keys/trusted_caam.h>
#include <keys/trusted_dcp.h>
#include <keys/trusted_tpm.h>
+#include <keys/trusted_pkwm.h>
#include <linux/capability.h>
#include <linux/err.h>
#include <linux/init.h>
@@ -31,7 +32,7 @@ MODULE_PARM_DESC(rng, "Select trusted key RNG");
static char *trusted_key_source;
module_param_named(source, trusted_key_source, charp, 0);
-MODULE_PARM_DESC(source, "Select trusted keys source (tpm, tee, caam or dcp)");
+MODULE_PARM_DESC(source, "Select trusted keys source (tpm, tee, caam, dcp or pkwm)");
static const struct trusted_key_source trusted_key_sources[] = {
#if defined(CONFIG_TRUSTED_KEYS_TPM)
@@ -46,6 +47,9 @@ static const struct trusted_key_source trusted_key_sources[] = {
#if defined(CONFIG_TRUSTED_KEYS_DCP)
{ "dcp", &dcp_trusted_key_ops },
#endif
+#if defined(CONFIG_TRUSTED_KEYS_PKWM)
+ { "pkwm", &pkwm_trusted_key_ops },
+#endif
};
DEFINE_STATIC_CALL_NULL(trusted_key_seal, *trusted_key_sources[0].ops->seal);
diff --git a/security/keys/trusted-keys/trusted_pkwm.c b/security/keys/trusted-keys/trusted_pkwm.c
new file mode 100644
index 000000000000..4f391b77a907
--- /dev/null
+++ b/security/keys/trusted-keys/trusted_pkwm.c
@@ -0,0 +1,190 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2025 IBM Corporation, Srish Srinivasan <ssrish@linux.ibm.com>
+ */
+
+#include <keys/trusted_pkwm.h>
+#include <keys/trusted-type.h>
+#include <linux/build_bug.h>
+#include <linux/key-type.h>
+#include <linux/parser.h>
+#include <asm/plpks.h>
+
+enum {
+ Opt_err,
+ Opt_wrap_flags,
+};
+
+static const match_table_t key_tokens = {
+ {Opt_wrap_flags, "wrap_flags=%s"},
+ {Opt_err, NULL}
+};
+
+static int getoptions(char *datablob, struct trusted_key_options *opt)
+{
+ substring_t args[MAX_OPT_ARGS];
+ char *p = datablob;
+ int token;
+ int res;
+ u16 wrap_flags;
+ unsigned long token_mask = 0;
+ struct trusted_pkwm_options *pkwm;
+
+ if (!datablob)
+ return 0;
+
+ pkwm = opt->private;
+
+ while ((p = strsep(&datablob, " \t"))) {
+ if (*p == '\0' || *p == ' ' || *p == '\t')
+ continue;
+
+ token = match_token(p, key_tokens, args);
+ if (test_and_set_bit(token, &token_mask))
+ return -EINVAL;
+
+ switch (token) {
+ case Opt_wrap_flags:
+ res = kstrtou16(args[0].from, 16, &wrap_flags);
+ if (res < 0 || wrap_flags > 2)
+ return -EINVAL;
+ pkwm->wrap_flags = wrap_flags;
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+ return 0;
+}
+
+static struct trusted_key_options *trusted_options_alloc(void)
+{
+ struct trusted_key_options *options;
+ struct trusted_pkwm_options *pkwm;
+
+ options = kzalloc(sizeof(*options), GFP_KERNEL);
+
+ if (options) {
+ pkwm = kzalloc(sizeof(*pkwm), GFP_KERNEL);
+
+ if (!pkwm) {
+ kfree_sensitive(options);
+ options = NULL;
+ } else {
+ options->private = pkwm;
+ }
+ }
+
+ return options;
+}
+
+static int trusted_pkwm_seal(struct trusted_key_payload *p, char *datablob)
+{
+ struct trusted_key_options *options = NULL;
+ struct trusted_pkwm_options *pkwm = NULL;
+ u8 *input_buf, *output_buf;
+ u32 output_len, input_len;
+ int rc;
+
+ options = trusted_options_alloc();
+
+ if (!options)
+ return -ENOMEM;
+
+ rc = getoptions(datablob, options);
+ if (rc < 0)
+ goto out;
+ dump_options(options);
+
+ input_len = p->key_len;
+ input_buf = kmalloc(ALIGN(input_len, 4096), GFP_KERNEL);
+ if (!input_buf) {
+ pr_err("Input buffer allocation failed. Returning -ENOMEM.");
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ memcpy(input_buf, p->key, p->key_len);
+
+ pkwm = options->private;
+
+ rc = plpks_wrap_object(&input_buf, input_len, pkwm->wrap_flags,
+ &output_buf, &output_len);
+ if (!rc) {
+ memcpy(p->blob, output_buf, output_len);
+ p->blob_len = output_len;
+ dump_payload(p);
+ } else {
+ pr_err("Wrapping of payload key failed: %d\n", rc);
+ }
+
+ kfree(input_buf);
+ kfree(output_buf);
+
+out:
+ kfree_sensitive(options->private);
+ kfree_sensitive(options);
+ return rc;
+}
+
+static int trusted_pkwm_unseal(struct trusted_key_payload *p, char *datablob)
+{
+ u8 *input_buf, *output_buf;
+ u32 input_len, output_len;
+ int rc;
+
+ input_len = p->blob_len;
+ input_buf = kmalloc(ALIGN(input_len, 4096), GFP_KERNEL);
+ if (!input_buf) {
+ pr_err("Input buffer allocation failed. Returning -ENOMEM.");
+ return -ENOMEM;
+ }
+
+ memcpy(input_buf, p->blob, p->blob_len);
+
+ rc = plpks_unwrap_object(&input_buf, input_len, &output_buf,
+ &output_len);
+ if (!rc) {
+ memcpy(p->key, output_buf, output_len);
+ p->key_len = output_len;
+ dump_payload(p);
+ } else {
+ pr_err("Unwrapping of payload failed: %d\n", rc);
+ }
+
+ kfree(input_buf);
+ kfree(output_buf);
+
+ return rc;
+}
+
+static int trusted_pkwm_init(void)
+{
+ int ret;
+
+ if (!plpks_wrapping_is_supported()) {
+ pr_err("H_PKS_WRAP_OBJECT interface not supported\n");
+ return -ENODEV;
+ }
+
+ ret = plpks_gen_wrapping_key();
+ if (ret) {
+ pr_err("Failed to generate default wrapping key\n");
+ return -EINVAL;
+ }
+
+ return register_key_type(&key_type_trusted);
+}
+
+static void trusted_pkwm_exit(void)
+{
+ unregister_key_type(&key_type_trusted);
+}
+
+struct trusted_key_ops pkwm_trusted_key_ops = {
+ .migratable = 0, /* non-migratable */
+ .init = trusted_pkwm_init,
+ .seal = trusted_pkwm_seal,
+ .unseal = trusted_pkwm_unseal,
+ .exit = trusted_pkwm_exit,
+};
--
2.47.3
^ permalink raw reply related
* [PATCH v6 1/6] pseries/plpks: fix kernel-doc comment inconsistencies
From: Srish Srinivasan @ 2026-02-01 13:59 UTC (permalink / raw)
To: linux-integrity, keyrings, linuxppc-dev
Cc: maddy, mpe, npiggin, christophe.leroy, James.Bottomley, jarkko,
zohar, nayna, rnsastry, linux-kernel, linux-security-module,
ssrish
In-Reply-To: <20260201135930.898721-1-ssrish@linux.ibm.com>
Fix issues with comments for all the applicable functions to be
consistent with kernel-doc format. Move them before the function
definition as opposed to the function prototype.
Signed-off-by: Srish Srinivasan <ssrish@linux.ibm.com>
Reviewed-by: Nayna Jain <nayna@linux.ibm.com>
Tested-by: Nayna Jain <nayna@linux.ibm.com>
---
arch/powerpc/include/asm/plpks.h | 77 ------
arch/powerpc/platforms/pseries/plpks.c | 328 ++++++++++++++++++++++++-
2 files changed, 318 insertions(+), 87 deletions(-)
diff --git a/arch/powerpc/include/asm/plpks.h b/arch/powerpc/include/asm/plpks.h
index 7a84069759b0..f303922bf622 100644
--- a/arch/powerpc/include/asm/plpks.h
+++ b/arch/powerpc/include/asm/plpks.h
@@ -67,122 +67,45 @@ struct plpks_var_name_list {
struct plpks_var_name varlist[];
};
-/**
- * Updates the authenticated variable. It expects NULL as the component.
- */
int plpks_signed_update_var(struct plpks_var *var, u64 flags);
-/**
- * Writes the specified var and its data to PKS.
- * Any caller of PKS driver should present a valid component type for
- * their variable.
- */
int plpks_write_var(struct plpks_var var);
-/**
- * Removes the specified var and its data from PKS.
- */
int plpks_remove_var(char *component, u8 varos,
struct plpks_var_name vname);
-/**
- * Returns the data for the specified os variable.
- *
- * Caller must allocate a buffer in var->data with length in var->datalen.
- * If no buffer is provided, var->datalen will be populated with the object's
- * size.
- */
int plpks_read_os_var(struct plpks_var *var);
-/**
- * Returns the data for the specified firmware variable.
- *
- * Caller must allocate a buffer in var->data with length in var->datalen.
- * If no buffer is provided, var->datalen will be populated with the object's
- * size.
- */
int plpks_read_fw_var(struct plpks_var *var);
-/**
- * Returns the data for the specified bootloader variable.
- *
- * Caller must allocate a buffer in var->data with length in var->datalen.
- * If no buffer is provided, var->datalen will be populated with the object's
- * size.
- */
int plpks_read_bootloader_var(struct plpks_var *var);
-/**
- * Returns if PKS is available on this LPAR.
- */
bool plpks_is_available(void);
-/**
- * Returns version of the Platform KeyStore.
- */
u8 plpks_get_version(void);
-/**
- * Returns hypervisor storage overhead per object, not including the size of
- * the object or label. Only valid for config version >= 2
- */
u16 plpks_get_objoverhead(void);
-/**
- * Returns maximum password size. Must be >= 32 bytes
- */
u16 plpks_get_maxpwsize(void);
-/**
- * Returns maximum object size supported by Platform KeyStore.
- */
u16 plpks_get_maxobjectsize(void);
-/**
- * Returns maximum object label size supported by Platform KeyStore.
- */
u16 plpks_get_maxobjectlabelsize(void);
-/**
- * Returns total size of the configured Platform KeyStore.
- */
u32 plpks_get_totalsize(void);
-/**
- * Returns used space from the total size of the Platform KeyStore.
- */
u32 plpks_get_usedspace(void);
-/**
- * Returns bitmask of policies supported by the hypervisor.
- */
u32 plpks_get_supportedpolicies(void);
-/**
- * Returns maximum byte size of a single object supported by the hypervisor.
- * Only valid for config version >= 3
- */
u32 plpks_get_maxlargeobjectsize(void);
-/**
- * Returns bitmask of signature algorithms supported for signed updates.
- * Only valid for config version >= 3
- */
u64 plpks_get_signedupdatealgorithms(void);
-/**
- * Returns the length of the PLPKS password in bytes.
- */
u16 plpks_get_passwordlen(void);
-/**
- * Called in early init to retrieve and clear the PLPKS password from the DT.
- */
void plpks_early_init_devtree(void);
-/**
- * Populates the FDT with the PLPKS password to prepare for kexec.
- */
int plpks_populate_fdt(void *fdt);
#else // CONFIG_PSERIES_PLPKS
static inline bool plpks_is_available(void) { return false; }
diff --git a/arch/powerpc/platforms/pseries/plpks.c b/arch/powerpc/platforms/pseries/plpks.c
index b1667ed05f98..03722fabf9c3 100644
--- a/arch/powerpc/platforms/pseries/plpks.c
+++ b/arch/powerpc/platforms/pseries/plpks.c
@@ -312,40 +312,107 @@ static int _plpks_get_config(void)
return rc;
}
+/**
+ * plpks_get_version() - Get the version of the PLPKS config structure.
+ *
+ * Successful execution of the H_PKS_GET_CONFIG HCALL during initialization
+ * reads the PLPKS config structure version and saves it in a file local static
+ * version variable.
+ *
+ * Returns: On success the saved PLPKS config structure version is returned, 0
+ * if not.
+ */
u8 plpks_get_version(void)
{
return version;
}
+/**
+ * plpks_get_objoverhead() - Get the hypervisor storage overhead per object.
+ *
+ * Successful execution of the H_PKS_GET_CONFIG HCALL during initialization
+ * reads the per object hypervisor storage overhead in bytes into the local
+ * static objoverhead variable, excluding the size of the object or the label.
+ * This value can be treated as valid only when the PLPKS config structure
+ * version >= 2.
+ *
+ * Returns: If PLPKS config structure version >= 2 then the storage overhead is
+ * returned, 0 otherwise.
+ */
u16 plpks_get_objoverhead(void)
{
return objoverhead;
}
+/**
+ * plpks_get_maxpwsize() - Get the maximum password size.
+ *
+ * Successful execution of the H_PKS_GET_CONFIG HCALL during initialization
+ * reads the maximum password size and checks if it is 32 bytes at the least
+ * before storing it in the local static maxpwsize variable.
+ *
+ * Returns: On success the maximum password size is returned, 0 if not.
+ */
u16 plpks_get_maxpwsize(void)
{
return maxpwsize;
}
+/**
+ * plpks_get_maxobjectsize() - Get the maximum object size supported by the
+ * PLPKS.
+ *
+ * Successful execution of the H_PKS_GET_CONFIG HCALL during initialization
+ * reads the maximum object size into the file local static maxobjsize variable.
+ *
+ * Returns: On success the maximum object size is returned, 0 if not.
+ */
u16 plpks_get_maxobjectsize(void)
{
return maxobjsize;
}
+/**
+ * plpks_get_maxobjectlabelsize() - Get the maximum object label size supported
+ * by the PLPKS.
+ *
+ * Successful execution of the H_PKS_GET_CONFIG HCALL during initialization
+ * reads the maximum object label size into the local static maxobjlabelsize
+ * variable.
+ *
+ * Returns: On success the maximum object label size is returned, 0 if not.
+ */
u16 plpks_get_maxobjectlabelsize(void)
{
return maxobjlabelsize;
}
+/**
+ * plpks_get_totalsize() - Get the total size of the PLPKS that is configured.
+ *
+ * Successful execution of the H_PKS_GET_CONFIG HCALL during initialization
+ * reads the total size of the PLPKS that is configured for the LPAR into the
+ * file local static totalsize variable.
+ *
+ * Returns: On success the total size of the PLPKS configured is returned, 0 if
+ * not.
+ */
u32 plpks_get_totalsize(void)
{
return totalsize;
}
+/**
+ * plpks_get_usedspace() - Get the used space from the total size of the PLPKS.
+ *
+ * Invoke the H_PKS_GET_CONFIG HCALL to refresh the latest value for the used
+ * space as this keeps changing with the creation and removal of objects in the
+ * PLPKS.
+ *
+ * Returns: On success the used space is returned, 0 if not.
+ */
u32 plpks_get_usedspace(void)
{
- // Unlike other config values, usedspace regularly changes as objects
- // are updated, so we need to refresh.
int rc = _plpks_get_config();
if (rc) {
pr_err("Couldn't get config, rc: %d\n", rc);
@@ -354,26 +421,84 @@ u32 plpks_get_usedspace(void)
return usedspace;
}
+/**
+ * plpks_get_supportedpolicies() - Get a bitmask of the policies supported by
+ * the hypervisor.
+ *
+ * Successful execution of the H_PKS_GET_CONFIG HCALL during initialization
+ * reads a bitmask of the policies supported by the hypervisor into the file
+ * local static supportedpolicies variable.
+ *
+ * Returns: On success the bitmask of the policies supported by the hypervisor
+ * are returned, 0 if not.
+ */
u32 plpks_get_supportedpolicies(void)
{
return supportedpolicies;
}
+/**
+ * plpks_get_maxlargeobjectsize() - Get the maximum object size supported for
+ * PLPKS config structure version >= 3
+ *
+ * Successful execution of the H_PKS_GET_CONFIG HCALL during initialization
+ * reads the maximum object size into the local static maxlargeobjectsize
+ * variable for PLPKS config structure version >= 3. This was introduced
+ * starting with PLPKS config structure version 3 to allow for objects of
+ * size >= 64K.
+ *
+ * Returns: If PLPKS config structure version >= 3 then the new maximum object
+ * size is returned, 0 if not.
+ */
u32 plpks_get_maxlargeobjectsize(void)
{
return maxlargeobjectsize;
}
+/**
+ * plpks_get_signedupdatealgorithms() - Get a bitmask of the signature
+ * algorithms supported for signed updates.
+ *
+ * Successful execution of the H_PKS_GET_CONFIG HCALL during initialization
+ * reads a bitmask of the signature algorithms supported for signed updates into
+ * the file local static signedupdatealgorithms variable. This is valid only
+ * when the PLPKS config structure version >= 3.
+ *
+ * Returns: On success the bitmask of the signature algorithms supported for
+ * signed updates is returned, 0 if not.
+ */
u64 plpks_get_signedupdatealgorithms(void)
{
return signedupdatealgorithms;
}
+/**
+ * plpks_get_passwordlen() - Get the length of the PLPKS password in bytes.
+ *
+ * The H_PKS_GEN_PASSWORD HCALL makes the hypervisor generate a random password
+ * for the specified consumer, apply that password to the PLPKS and return it to
+ * the caller. In this process, the password length for the OS consumer is
+ * stored in the local static ospasswordlength variable.
+ *
+ * Returns: On success the password length for the OS consumer in bytes is
+ * returned, 0 if not.
+ */
u16 plpks_get_passwordlen(void)
{
return ospasswordlength;
}
+/**
+ * plpks_is_available() - Get the PLPKS availability status for the LPAR.
+ *
+ * The availability of PLPKS is inferred based upon the successful execution of
+ * the H_PKS_GET_CONFIG HCALL provided the firmware supports this feature. The
+ * H_PKS_GET_CONFIG HCALL reads the configuration and status information related
+ * to the PLPKS. The configuration structure provides a version number to inform
+ * the caller of the supported features.
+ *
+ * Returns: true is returned if PLPKS is available, false if not.
+ */
bool plpks_is_available(void)
{
int rc;
@@ -425,6 +550,35 @@ static int plpks_confirm_object_flushed(struct label *label,
return pseries_status_to_err(rc);
}
+/**
+ * plpks_signed_update_var() - Update the specified authenticated variable.
+ * @var: authenticated variable to be updated
+ * @flags: signed update request operation flags
+ *
+ * The H_PKS_SIGNED_UPDATE HCALL performs a signed update to an object in the
+ * PLPKS. The object must have the signed update policy flag set.
+ *
+ * Possible reasons for the returned errno values:
+ *
+ * -ENXIO if PLPKS is not supported
+ * -EIO if PLPKS access is blocked due to the LPAR's state
+ * if PLPKS modification is blocked due to the LPAR's state
+ * if an error occurred while processing the request
+ * -EINVAL if invalid authorization parameter
+ * if invalid object label parameter
+ * if invalid object label len parameter
+ * if invalid or unsupported policy declaration
+ * if invalid signed update flags
+ * if invalid input data parameter
+ * if invalid input data len parameter
+ * if invalid continue token parameter
+ * -EPERM if access is denied
+ * -ENOMEM if there is inadequate memory to perform the operation
+ * -EBUSY if unable to handle the request or long running operation
+ * initiated, retry later
+ *
+ * Returns: On success 0 is returned, a negative errno if not.
+ */
int plpks_signed_update_var(struct plpks_var *var, u64 flags)
{
unsigned long retbuf[PLPAR_HCALL9_BUFSIZE] = {0};
@@ -481,6 +635,33 @@ int plpks_signed_update_var(struct plpks_var *var, u64 flags)
return rc;
}
+/**
+ * plpks_write_var() - Write the specified variable and its data to PLPKS.
+ * @var: variable to be written into the PLPKS
+ *
+ * The H_PKS_WRITE_OBJECT HCALL writes an object into the PLPKS. The caller must
+ * provide a valid component type for the variable, and the signed update policy
+ * flag must not be set.
+ *
+ * Possible reasons for the returned errno values:
+ *
+ * -ENXIO if PLPKS is not supported
+ * -EIO if PLPKS access is blocked due to the LPAR's state
+ * if PLPKS modification is blocked due to the LPAR's state
+ * if an error occurred while processing the request
+ * -EINVAL if invalid authorization parameter
+ * if invalid object label parameter
+ * if invalid object label len parameter
+ * if invalid or unsupported policy declaration
+ * if invalid input data parameter
+ * if invalid input data len parameter
+ * -EPERM if access is denied
+ * -ENOMEM if unable to store the requested object in the space available
+ * -EBUSY if unable to handle the request
+ * -EEXIST if the object label already exists
+ *
+ * Returns: On success 0 is returned, a negative errno if not.
+ */
int plpks_write_var(struct plpks_var var)
{
unsigned long retbuf[PLPAR_HCALL_BUFSIZE] = { 0 };
@@ -520,6 +701,30 @@ int plpks_write_var(struct plpks_var var)
return rc;
}
+/**
+ * plpks_remove_var() - Remove the specified variable and its data from PLPKS.
+ * @component: metadata prefix in the object label metadata structure
+ * @varos: metadata OS flags in the object label metadata structure
+ * @vname: object label for the object that needs to be removed
+ *
+ * The H_PKS_REMOVE_OBJECT HCALL removes an object from the PLPKS. The removal
+ * is independent of the policy bits that are set.
+ *
+ * Possible reasons for the returned errno values:
+ *
+ * -ENXIO if PLPKS is not supported
+ * -EIO if PLPKS access is blocked due to the LPAR's state
+ * if PLPKS modification is blocked due to the LPAR's state
+ * if an error occurred while processing the request
+ * -EINVAL if invalid authorization parameter
+ * if invalid object label parameter
+ * if invalid object label len parameter
+ * -EPERM if access is denied
+ * -ENOENT if the requested object was not found
+ * -EBUSY if unable to handle the request
+ *
+ * Returns: On success 0 is returned, a negative errno if not.
+ */
int plpks_remove_var(char *component, u8 varos, struct plpks_var_name vname)
{
unsigned long retbuf[PLPAR_HCALL_BUFSIZE] = { 0 };
@@ -619,21 +824,119 @@ static int plpks_read_var(u8 consumer, struct plpks_var *var)
return rc;
}
+/**
+ * plpks_read_os_var() - Fetch the data for the specified variable that is
+ * owned by the OS consumer.
+ * @var: variable to be read from the PLPKS
+ *
+ * The consumer or the owner of the object is the os kernel. The
+ * H_PKS_READ_OBJECT HCALL reads an object from the PLPKS. The caller must
+ * allocate the buffer var->data and specify the length for this buffer in
+ * var->datalen. If no buffer is provided, var->datalen will be populated with
+ * the requested object's size.
+ *
+ * Possible reasons for the returned errno values:
+ *
+ * -ENXIO if PLPKS is not supported
+ * -EIO if PLPKS access is blocked due to the LPAR's state
+ * if an error occurred while processing the request
+ * -EINVAL if invalid authorization parameter
+ * if invalid object label parameter
+ * if invalid object label len parameter
+ * if invalid output data parameter
+ * if invalid output data len parameter
+ * -EPERM if access is denied
+ * -ENOENT if the requested object was not found
+ * -EFBIG if the requested object couldn't be
+ * stored in the buffer provided
+ * -EBUSY if unable to handle the request
+ *
+ * Returns: On success 0 is returned, a negative errno if not.
+ */
int plpks_read_os_var(struct plpks_var *var)
{
return plpks_read_var(PLPKS_OS_OWNER, var);
}
+/**
+ * plpks_read_fw_var() - Fetch the data for the specified variable that is
+ * owned by the firmware consumer.
+ * @var: variable to be read from the PLPKS
+ *
+ * The consumer or the owner of the object is the firmware. The
+ * H_PKS_READ_OBJECT HCALL reads an object from the PLPKS. The caller must
+ * allocate the buffer var->data and specify the length for this buffer in
+ * var->datalen. If no buffer is provided, var->datalen will be populated with
+ * the requested object's size.
+ *
+ * Possible reasons for the returned errno values:
+ *
+ * -ENXIO if PLPKS is not supported
+ * -EIO if PLPKS access is blocked due to the LPAR's state
+ * if an error occurred while processing the request
+ * -EINVAL if invalid authorization parameter
+ * if invalid object label parameter
+ * if invalid object label len parameter
+ * if invalid output data parameter
+ * if invalid output data len parameter
+ * -EPERM if access is denied
+ * -ENOENT if the requested object was not found
+ * -EFBIG if the requested object couldn't be
+ * stored in the buffer provided
+ * -EBUSY if unable to handle the request
+ *
+ * Returns: On success 0 is returned, a negative errno if not.
+ */
int plpks_read_fw_var(struct plpks_var *var)
{
return plpks_read_var(PLPKS_FW_OWNER, var);
}
+/**
+ * plpks_read_bootloader_var() - Fetch the data for the specified variable
+ * owned by the bootloader consumer.
+ * @var: variable to be read from the PLPKS
+ *
+ * The consumer or the owner of the object is the bootloader. The
+ * H_PKS_READ_OBJECT HCALL reads an object from the PLPKS. The caller must
+ * allocate the buffer var->data and specify the length for this buffer in
+ * var->datalen. If no buffer is provided, var->datalen will be populated with
+ * the requested object's size.
+ *
+ * Possible reasons for the returned errno values:
+ *
+ * -ENXIO if PLPKS is not supported
+ * -EIO if PLPKS access is blocked due to the LPAR's state
+ * if an error occurred while processing the request
+ * -EINVAL if invalid authorization parameter
+ * if invalid object label parameter
+ * if invalid object label len parameter
+ * if invalid output data parameter
+ * if invalid output data len parameter
+ * -EPERM if access is denied
+ * -ENOENT if the requested object was not found
+ * -EFBIG if the requested object couldn't be
+ * stored in the buffer provided
+ * -EBUSY if unable to handle the request
+ *
+ * Returns: On success 0 is returned, a negative errno if not.
+ */
int plpks_read_bootloader_var(struct plpks_var *var)
{
return plpks_read_var(PLPKS_BOOTLOADER_OWNER, var);
}
+/**
+ * plpks_populate_fdt(): Populates the FDT with the PLPKS password to prepare
+ * for kexec.
+ * @fdt: pointer to the device tree blob
+ *
+ * Upon confirming the existence of the chosen node, invoke fdt_setprop to
+ * populate the device tree with the PLPKS password in order to prepare for
+ * kexec.
+ *
+ * Returns: On success 0 is returned, a negative value if not.
+ */
int plpks_populate_fdt(void *fdt)
{
int chosen_offset = fdt_path_offset(fdt, "/chosen");
@@ -647,14 +950,19 @@ int plpks_populate_fdt(void *fdt)
return fdt_setprop(fdt, chosen_offset, "ibm,plpks-pw", ospassword, ospasswordlength);
}
-// Once a password is registered with the hypervisor it cannot be cleared without
-// rebooting the LPAR, so to keep using the PLPKS across kexec boots we need to
-// recover the previous password from the FDT.
-//
-// There are a few challenges here. We don't want the password to be visible to
-// users, so we need to clear it from the FDT. This has to be done in early boot.
-// Clearing it from the FDT would make the FDT's checksum invalid, so we have to
-// manually cause the checksum to be recalculated.
+/**
+ * plpks_early_init_devtree() - Retrieves and clears the PLPKS password from the
+ * DT in early init.
+ *
+ * Once a password is registered with the hypervisor it cannot be cleared
+ * without rebooting the LPAR, so to keep using the PLPKS across kexec boots we
+ * need to recover the previous password from the FDT.
+ *
+ * There are a few challenges here. We don't want the password to be visible to
+ * users, so we need to clear it from the FDT. This has to be done in early
+ * boot. Clearing it from the FDT would make the FDT's checksum invalid, so we
+ * have to manually cause the checksum to be recalculated.
+ */
void __init plpks_early_init_devtree(void)
{
void *fdt = initial_boot_params;
--
2.47.3
^ permalink raw reply related
* [PATCH v6 3/6] pseries/plpks: expose PowerVM wrapping features via the sysfs
From: Srish Srinivasan @ 2026-02-01 13:59 UTC (permalink / raw)
To: linux-integrity, keyrings, linuxppc-dev
Cc: maddy, mpe, npiggin, christophe.leroy, James.Bottomley, jarkko,
zohar, nayna, rnsastry, linux-kernel, linux-security-module,
ssrish
In-Reply-To: <20260201135930.898721-1-ssrish@linux.ibm.com>
Starting with Power11, PowerVM supports a new feature called "Key Wrapping"
that protects user secrets by wrapping them using a hypervisor generated
wrapping key. The status of this feature can be read by the
H_PKS_GET_CONFIG HCALL.
Expose the Power LPAR Platform KeyStore (PLPKS) wrapping features config
via the sysfs file /sys/firmware/plpks/config/wrapping_features.
Signed-off-by: Srish Srinivasan <ssrish@linux.ibm.com>
Reviewed-by: Nayna Jain <nayna@linux.ibm.com>
Tested-by: Nayna Jain <nayna@linux.ibm.com>
---
.../ABI/testing/sysfs-firmware-plpks | 8 ++++++++
arch/powerpc/include/asm/hvcall.h | 4 +++-
arch/powerpc/include/asm/plpks.h | 3 +++
arch/powerpc/platforms/pseries/plpks-sysfs.c | 2 ++
arch/powerpc/platforms/pseries/plpks.c | 20 +++++++++++++++++++
5 files changed, 36 insertions(+), 1 deletion(-)
diff --git a/Documentation/ABI/testing/sysfs-firmware-plpks b/Documentation/ABI/testing/sysfs-firmware-plpks
index af0353f34115..cba061e4eee2 100644
--- a/Documentation/ABI/testing/sysfs-firmware-plpks
+++ b/Documentation/ABI/testing/sysfs-firmware-plpks
@@ -48,3 +48,11 @@ Description: Bitmask of flags indicating which algorithms the hypervisor
supports for signed update of objects, represented as a 16 byte
hexadecimal ASCII string. Consult the hypervisor documentation
for what these flags mean.
+
+What: /sys/firmware/plpks/config/wrapping_features
+Date: November 2025
+Contact: Srish Srinivasan <ssrish@linux.ibm.com>
+Description: Bitmask of the wrapping features indicating the wrapping
+ algorithms that are supported for the H_PKS_WRAP_OBJECT requests
+ , represented as a 8 byte hexadecimal ASCII string. Consult the
+ hypervisor documentation for what these flags mean.
diff --git a/arch/powerpc/include/asm/hvcall.h b/arch/powerpc/include/asm/hvcall.h
index 9aef16149d92..dff90a7d7f70 100644
--- a/arch/powerpc/include/asm/hvcall.h
+++ b/arch/powerpc/include/asm/hvcall.h
@@ -360,7 +360,9 @@
#define H_GUEST_RUN_VCPU 0x480
#define H_GUEST_COPY_MEMORY 0x484
#define H_GUEST_DELETE 0x488
-#define MAX_HCALL_OPCODE H_GUEST_DELETE
+#define H_PKS_WRAP_OBJECT 0x490
+#define H_PKS_UNWRAP_OBJECT 0x494
+#define MAX_HCALL_OPCODE H_PKS_UNWRAP_OBJECT
/* Scope args for H_SCM_UNBIND_ALL */
#define H_UNBIND_SCOPE_ALL (0x1)
diff --git a/arch/powerpc/include/asm/plpks.h b/arch/powerpc/include/asm/plpks.h
index 8829a13bfda0..8f034588fdf7 100644
--- a/arch/powerpc/include/asm/plpks.h
+++ b/arch/powerpc/include/asm/plpks.h
@@ -23,6 +23,7 @@
#define PLPKS_IMMUTABLE PPC_BIT32(5) // Once written, object cannot be removed
#define PLPKS_TRANSIENT PPC_BIT32(6) // Object does not persist through reboot
#define PLPKS_SIGNEDUPDATE PPC_BIT32(7) // Object can only be modified by signed updates
+#define PLPKS_WRAPPINGKEY PPC_BIT32(8) // Object contains a wrapping key
#define PLPKS_HVPROVISIONED PPC_BIT32(28) // Hypervisor has provisioned this object
// Signature algorithm flags from signed_update_algorithms
@@ -103,6 +104,8 @@ u32 plpks_get_maxlargeobjectsize(void);
u64 plpks_get_signedupdatealgorithms(void);
+u64 plpks_get_wrappingfeatures(void);
+
u16 plpks_get_passwordlen(void);
void plpks_early_init_devtree(void);
diff --git a/arch/powerpc/platforms/pseries/plpks-sysfs.c b/arch/powerpc/platforms/pseries/plpks-sysfs.c
index 01d526185783..c2ebcbb41ae3 100644
--- a/arch/powerpc/platforms/pseries/plpks-sysfs.c
+++ b/arch/powerpc/platforms/pseries/plpks-sysfs.c
@@ -30,6 +30,7 @@ PLPKS_CONFIG_ATTR(used_space, "%u\n", plpks_get_usedspace);
PLPKS_CONFIG_ATTR(supported_policies, "%08x\n", plpks_get_supportedpolicies);
PLPKS_CONFIG_ATTR(signed_update_algorithms, "%016llx\n",
plpks_get_signedupdatealgorithms);
+PLPKS_CONFIG_ATTR(wrapping_features, "%016llx\n", plpks_get_wrappingfeatures);
static const struct attribute *config_attrs[] = {
&attr_version.attr,
@@ -38,6 +39,7 @@ static const struct attribute *config_attrs[] = {
&attr_used_space.attr,
&attr_supported_policies.attr,
&attr_signed_update_algorithms.attr,
+ &attr_wrapping_features.attr,
NULL,
};
diff --git a/arch/powerpc/platforms/pseries/plpks.c b/arch/powerpc/platforms/pseries/plpks.c
index 03722fabf9c3..4a08f51537c8 100644
--- a/arch/powerpc/platforms/pseries/plpks.c
+++ b/arch/powerpc/platforms/pseries/plpks.c
@@ -38,6 +38,7 @@ static u32 usedspace;
static u32 supportedpolicies;
static u32 maxlargeobjectsize;
static u64 signedupdatealgorithms;
+static u64 wrappingfeatures;
struct plpks_auth {
u8 version;
@@ -248,6 +249,7 @@ static int _plpks_get_config(void)
__be32 supportedpolicies;
__be32 maxlargeobjectsize;
__be64 signedupdatealgorithms;
+ __be64 wrappingfeatures;
u8 rsvd1[476];
} __packed * config;
size_t size;
@@ -280,6 +282,7 @@ static int _plpks_get_config(void)
supportedpolicies = be32_to_cpu(config->supportedpolicies);
maxlargeobjectsize = be32_to_cpu(config->maxlargeobjectsize);
signedupdatealgorithms = be64_to_cpu(config->signedupdatealgorithms);
+ wrappingfeatures = be64_to_cpu(config->wrappingfeatures);
// Validate that the numbers we get back match the requirements of the spec
if (maxpwsize < 32) {
@@ -472,6 +475,23 @@ u64 plpks_get_signedupdatealgorithms(void)
return signedupdatealgorithms;
}
+/**
+ * plpks_get_wrappingfeatures() - Returns a bitmask of the wrapping features
+ * supported by the hypervisor.
+ *
+ * Successful execution of the H_PKS_GET_CONFIG HCALL during initialization
+ * reads a bitmask of the wrapping features supported by the hypervisor into the
+ * file local static wrappingfeatures variable. This is valid only when the
+ * PLPKS config structure version >= 3.
+ *
+ * Return:
+ * bitmask of the wrapping features supported by the hypervisor
+ */
+u64 plpks_get_wrappingfeatures(void)
+{
+ return wrappingfeatures;
+}
+
/**
* plpks_get_passwordlen() - Get the length of the PLPKS password in bytes.
*
--
2.47.3
^ permalink raw reply related
* [PATCH v6 0/6] Extend "trusted" keys to support a new trust source named the PowerVM Key Wrapping Module (PKWM)
From: Srish Srinivasan @ 2026-02-01 13:59 UTC (permalink / raw)
To: linux-integrity, keyrings, linuxppc-dev
Cc: maddy, mpe, npiggin, christophe.leroy, James.Bottomley, jarkko,
zohar, nayna, rnsastry, linux-kernel, linux-security-module,
ssrish
Power11 has introduced a feature called the PowerVM Key Wrapping Module
(PKWM), where PowerVM in combination with Power LPAR Platform KeyStore
(PLPKS) [1] supports a new feature called "Key Wrapping" [2] to protect
user secrets by wrapping them using a hypervisor generated wrapping key.
This wrapping key is an AES-GCM-256 symmetric key that is stored as an
object in the PLPKS. It has policy based protections that prevents it from
being read out or exposed to the user. This wrapping key can then be used
by the OS to wrap or unwrap secrets via hypervisor calls.
This patchset intends to add the PKWM, which is a combination of IBM
PowerVM and PLPKS, as a new trust source for trusted keys. The wrapping key
does not exist by default and its generation is requested by the kernel at
the time of PKWM initialization. This key is then persisted by the PKWM and
is used for wrapping any kernel provided key, and is never exposed to the
user. The kernel is aware of only the label to this wrapping key.
Along with the PKWM implementation, this patchset includes two preparatory
patches: one fixing the kernel-doc inconsistencies in the PLPKS code and
another reorganizing PLPKS config variables in the sysfs.
Changelog:
v6:
* Patch 1 to Patch 3:
- Add Nayna's Tested-by tag
* Patch 4
- Fix build error reported by kernel test robot <lkp@intel.com>
- Add Nayna's Tested-by tag
* Patch 5
- Add Nayna's Tested-by tag
v5:
* Patch 1 to Patch 3:
- Add Nayna's Reviewed-by tag
* Patch 4:
- Fix build error identified by chleroy@kernel.org
- Add Nayna's Reviewed-by tag
* Patch 5:
- Add Reviewed-by tags from Nayna and Jarkko
v4:
* Patch 5:
- Add a per-backend private data pointer in trusted_key_options
to store a pointer to the backend-specific options structure
- Minor clean-up
v3:
* Patch 2:
- Add Mimi's Reviewed-by tag
* Patch 4:
- Minor tweaks to some print statements
- Fix typos
* Patch 5:
- Fix typos
- Add Mimi's Reviewed-by tag
* Patch 6:
- Add Mimi's Reviewed-by tag
v2:
* Patch 2:
- Fix build warning detected by the kernel test bot
* Patch 5:
- Use pr_debug inside dump_options
- Replace policyhande with wrap_flags inside dump_options
- Provide meaningful error messages with error codes
Nayna Jain (1):
docs: trusted-encryped: add PKWM as a new trust source
Srish Srinivasan (5):
pseries/plpks: fix kernel-doc comment inconsistencies
powerpc/pseries: move the PLPKS config inside its own sysfs directory
pseries/plpks: expose PowerVM wrapping features via the sysfs
pseries/plpks: add HCALLs for PowerVM Key Wrapping Module
keys/trusted_keys: establish PKWM as a trusted source
.../ABI/testing/sysfs-firmware-plpks | 58 ++
Documentation/ABI/testing/sysfs-secvar | 65 --
.../admin-guide/kernel-parameters.txt | 1 +
Documentation/arch/powerpc/papr_hcalls.rst | 43 ++
.../security/keys/trusted-encrypted.rst | 50 ++
MAINTAINERS | 9 +
arch/powerpc/include/asm/hvcall.h | 4 +-
arch/powerpc/include/asm/plpks.h | 95 +--
arch/powerpc/include/asm/secvar.h | 1 -
arch/powerpc/kernel/secvar-sysfs.c | 21 +-
arch/powerpc/platforms/pseries/Makefile | 2 +-
arch/powerpc/platforms/pseries/plpks-secvar.c | 29 -
arch/powerpc/platforms/pseries/plpks-sysfs.c | 96 +++
arch/powerpc/platforms/pseries/plpks.c | 688 +++++++++++++++++-
include/keys/trusted-type.h | 7 +-
include/keys/trusted_pkwm.h | 33 +
security/keys/trusted-keys/Kconfig | 8 +
security/keys/trusted-keys/Makefile | 2 +
security/keys/trusted-keys/trusted_core.c | 6 +-
security/keys/trusted-keys/trusted_pkwm.c | 190 +++++
20 files changed, 1207 insertions(+), 201 deletions(-)
create mode 100644 Documentation/ABI/testing/sysfs-firmware-plpks
create mode 100644 arch/powerpc/platforms/pseries/plpks-sysfs.c
create mode 100644 include/keys/trusted_pkwm.h
create mode 100644 security/keys/trusted-keys/trusted_pkwm.c
--
2.47.3
^ permalink raw reply
* Re: [PATCH RESEND] apparmor: Replace deprecated strcpy with memcpy in gen_symlink_name
From: Thorsten Blum @ 2026-02-01 12:42 UTC (permalink / raw)
To: John Johansen
Cc: Paul Moore, James Morris, Serge E. Hallyn, apparmor,
linux-security-module, linux-kernel
In-Reply-To: <D73FFEAA-F246-4777-8CA8-32807F378423@linux.dev>
Hi John,
On 27. Nov 2025, at 11:18, Thorsten Blum wrote:
> On 27. Nov 2025, at 02:32, John Johansen wrote:
>> hey Thorsten,
>>
>> sorry I have actually pulled these in, and tested them. I didn't send out
>> the acks yet because I have another patch that I was waiting on a proper
>> signed-off-by: on.
>>
>> I am going to have to pull that one so I can push. I'll add acks now but
>> the push isn't going to happen for a few hours.
>>
>> Acked-by: John Johansen <john.johansen@canonical.com>
>
> Ah sorry for resending then, didn't know you looked at them already.
Did you ever push the commits? I can't find them anywhere.
Thanks,
Thorsten
^ permalink raw reply
* Re: [PATCH v2 3/3] landlock: transpose the layer masks data structure
From: Günther Noack @ 2026-02-01 12:24 UTC (permalink / raw)
To: Mickaël Salaün
Cc: linux-security-module, Tingmao Wang, Justin Suess,
Samasth Norway Ananda, Matthieu Buffet, Mikhail Ivanov,
konstantin.meskhidze, Randy Dunlap
In-Reply-To: <20260129.oozohZah1Uth@digikod.net>
On Thu, Jan 29, 2026 at 09:28:08PM +0100, Mickaël Salaün wrote:
> On Sun, Jan 25, 2026 at 08:58:53PM +0100, Günther Noack wrote:
> > At the moment, for the filesystem access rights, the data structure
> > has the same size as before, but once we introduce the 17th filesystem
> > access right, it will double in size (from 32 to 64 bytes), as
> > access_mask_t grows from 16 to 32 bit. See the link below for
> > measurements.
> >
> > Link: https://lore.kernel.org/all/20260120.haeCh4li9Vae@digikod.net/
>
> When adding extra links, please add a [1] reference at the end and use
> this reference in the commit message.
Ack, done.
–Günther
^ permalink raw reply
* Re: [PATCH v3 1/3] landlock: add backwards compatibility for restrict flags
From: Günther Noack @ 2026-02-01 12:24 UTC (permalink / raw)
To: Samasth Norway Ananda; +Cc: gnoack, mic, linux-security-module, linux-kernel
In-Reply-To: <20260128031814.2945394-2-samasth.norway.ananda@oracle.com>
On Tue, Jan 27, 2026 at 07:18:10PM -0800, Samasth Norway Ananda wrote:
> Add backwards compatibility handling for the restrict flags introduced
> in ABI version 7. This is shown as a separate code block (similar to
> the ruleset_attr handling in the switch statement) because restrict flags
> are passed to landlock_restrict_self() rather than being part of the
> ruleset attributes.
>
> Also fix misleading description of the /usr rule which incorrectly
> stated it "only allow[s] reading" when the code actually allows both
> reading and executing (LANDLOCK_ACCESS_FS_EXECUTE is included in
> allowed_access).
>
> Signed-off-by: Samasth Norway Ananda <samasth.norway.ananda@oracle.com>
> ---
> Documentation/userspace-api/landlock.rst | 30 +++++++++++++++++-------
> 1 file changed, 22 insertions(+), 8 deletions(-)
>
> diff --git a/Documentation/userspace-api/landlock.rst b/Documentation/userspace-api/landlock.rst
> index 1ed25af0499f..c8ef1392a0c7 100644
> --- a/Documentation/userspace-api/landlock.rst
> +++ b/Documentation/userspace-api/landlock.rst
> @@ -157,11 +157,11 @@ This enables the creation of an inclusive ruleset that will contain our rules.
> }
>
> We can now add a new rule to this ruleset thanks to the returned file
> -descriptor referring to this ruleset. The rule will only allow reading the
> -file hierarchy ``/usr``. Without another rule, write actions would then be
> -denied by the ruleset. To add ``/usr`` to the ruleset, we open it with the
> -``O_PATH`` flag and fill the &struct landlock_path_beneath_attr with this file
> -descriptor.
> +descriptor referring to this ruleset. The rule will allow reading and
> +executing the file hierarchy ``/usr``. Without another rule, write actions
> +would then be denied by the ruleset. To add ``/usr`` to the ruleset, we open
> +it with the ``O_PATH`` flag and fill the &struct landlock_path_beneath_attr with
> +this file descriptor.
>
> .. code-block:: c
>
> @@ -233,10 +233,24 @@ to effectively block sending UDP datagrams to arbitrary ports.
> err = landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_PORT,
> &net_port, 0);
>
> +When passing a non-zero ``flags`` argument to ``landlock_restrict_self()``, a
> +similar backwards compatibility check is needed for the restrict flags
> +(see sys_landlock_restrict_self() documentation for available flags):
> +
> +.. code-block:: c
> +
> + __u32 restrict_flags = LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON;
> + if (abi < 7) {
> + /* Clear logging flags unsupported before ABI 7. */
> + restrict_flags &= ~(LANDLOCK_RESTRICT_SELF_LOG_SAME_EXEC_OFF |
> + LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON |
> + LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF);
> + }
> +
> The next step is to restrict the current thread from gaining more privileges
> (e.g. through a SUID binary). We now have a ruleset with the first rule
> -allowing read access to ``/usr`` while denying all other handled accesses for
> -the filesystem, and two more rules allowing DNS queries.
> +allowing read and execute access to ``/usr`` while denying all other handled
> +accesses for the filesystem, and two more rules allowing DNS queries.
>
> .. code-block:: c
>
> @@ -250,7 +264,7 @@ The current thread is now ready to sandbox itself with the ruleset.
>
> .. code-block:: c
>
> - if (landlock_restrict_self(ruleset_fd, 0)) {
> + if (landlock_restrict_self(ruleset_fd, restrict_flags)) {
> perror("Failed to enforce ruleset");
> close(ruleset_fd);
> return 1;
> --
> 2.50.1
>
Reviewed-by: Günther Noack <gnoack3000@gmail.com>
Thanks!
–Günther
^ permalink raw reply
* Re: [PATCH v3 2/3] landlock: add errata documentation section
From: Günther Noack @ 2026-02-01 12:23 UTC (permalink / raw)
To: Samasth Norway Ananda; +Cc: gnoack, mic, linux-security-module, linux-kernel
In-Reply-To: <20260128031814.2945394-3-samasth.norway.ananda@oracle.com>
On Tue, Jan 27, 2026 at 07:18:11PM -0800, Samasth Norway Ananda wrote:
> Add errata section with code examples for querying errata and a warning
> that most applications should not check errata. Use kernel-doc directives
> to include errata descriptions from the header files instead of manual
> links.
>
> Also enhance existing DOC sections in security/landlock/errata/abi-*.h
> files with Impact sections, and update the code comment in syscalls.c
> to remind developers to update errata documentation when applicable.
>
> This addresses the gap where the kernel implements errata tracking
> but provides no user-facing documentation on how to use it, while
> improving the existing technical documentation in-place rather than
> duplicating it.
>
> Signed-off-by: Samasth Norway Ananda <samasth.norway.ananda@oracle.com>
> ---
> Documentation/userspace-api/landlock.rst | 67 ++++++++++++++++++++++--
> security/landlock/errata/abi-1.h | 8 +++
> security/landlock/errata/abi-4.h | 7 +++
> security/landlock/errata/abi-6.h | 10 ++++
> security/landlock/syscalls.c | 4 +-
> 5 files changed, 91 insertions(+), 5 deletions(-)
>
> diff --git a/Documentation/userspace-api/landlock.rst b/Documentation/userspace-api/landlock.rst
> index c8ef1392a0c7..405b2d73e699 100644
> --- a/Documentation/userspace-api/landlock.rst
> +++ b/Documentation/userspace-api/landlock.rst
> @@ -8,7 +8,7 @@ Landlock: unprivileged access control
> =====================================
>
> :Author: Mickaël Salaün
> -:Date: December 2025
> +:Date: January 2026
>
> The goal of Landlock is to enable restriction of ambient rights (e.g. global
> filesystem or network access) for a set of processes. Because Landlock
> @@ -492,9 +492,68 @@ system call:
> printf("Landlock supports LANDLOCK_ACCESS_FS_REFER.\n");
> }
>
> -The following kernel interfaces are implicitly supported by the first ABI
> -version. Features only supported from a specific version are explicitly marked
> -as such.
> +All Landlock kernel interfaces are supported by the first ABI version unless
> +explicitly noted in their documentation.
> +
> +Landlock errata
> +---------------
> +
> +In addition to ABI versions, Landlock provides an errata mechanism to track
> +fixes for issues that may affect backwards compatibility or require userspace
> +awareness. The errata bitmask can be queried using:
> +
> +.. code-block:: c
> +
> + int errata;
> +
> + errata = landlock_create_ruleset(NULL, 0, LANDLOCK_CREATE_RULESET_ERRATA);
> + if (errata < 0) {
> + /* Landlock not available or disabled */
> + return 0;
> + }
> +
> +The returned value is a bitmask where each bit represents a specific erratum.
> +If bit N is set (``errata & (1 << (N - 1))``), then erratum N has been fixed
> +in the running kernel.
> +
> +.. warning::
> +
> + **Most applications should NOT check errata.** In 99.9% of cases, checking
> + errata is unnecessary, increases code complexity, and can potentially
> + decrease protection if misused. For example, disabling the sandbox when an
> + erratum is not fixed could leave the system less secure than using
> + Landlock's best-effort protection. When in doubt, ignore errata.
> +
> +.. kernel-doc:: security/landlock/errata/abi-4.h
> + :doc: erratum_1
> +
> +.. kernel-doc:: security/landlock/errata/abi-6.h
> + :doc: erratum_2
> +
> +.. kernel-doc:: security/landlock/errata/abi-1.h
> + :doc: erratum_3
> +
> +How to check for errata
> +~~~~~~~~~~~~~~~~~~~~~~~
> +
> +If you determine that your application needs to check for specific errata,
> +use this pattern:
> +
> +.. code-block:: c
> +
> + int errata = landlock_create_ruleset(NULL, 0, LANDLOCK_CREATE_RULESET_ERRATA);
> + if (errata >= 0) {
> + /* Check for specific erratum (1-indexed) */
> + if (errata & (1 << (erratum_number - 1))) {
> + /* Erratum N is fixed in this kernel */
> + } else {
> + /* Erratum N is NOT fixed - consider implications for your use case */
> + }
> + }
> +
> +**Important:** Only check errata if your application specifically relies on
> +behavior that changed due to the fix. The fixes generally make Landlock less
> +restrictive or more correct, not more restrictive.
>
> Kernel interface
> ================
> diff --git a/security/landlock/errata/abi-1.h b/security/landlock/errata/abi-1.h
> index e8a2bff2e5b6..3f099555f059 100644
> --- a/security/landlock/errata/abi-1.h
> +++ b/security/landlock/errata/abi-1.h
> @@ -12,5 +12,13 @@
> * hierarchy down to its filesystem root and those from the related mount point
> * hierarchy. This prevents access right widening through rename or link
> * actions.
> + *
> + * Impact:
> + *
> + * Without this fix, it was possible to widen access rights through rename or
> + * link actions involving disconnected directories, potentially bypassing
> + * ``LANDLOCK_ACCESS_FS_REFER`` restrictions. This could allow privilege
> + * escalation in complex mount scenarios where directories become disconnected
> + * from their original mount points.
> */
> LANDLOCK_ERRATUM(3)
> diff --git a/security/landlock/errata/abi-4.h b/security/landlock/errata/abi-4.h
> index c052ee54f89f..fe11ec7d7ddf 100644
> --- a/security/landlock/errata/abi-4.h
> +++ b/security/landlock/errata/abi-4.h
> @@ -11,5 +11,12 @@
> * :manpage:`bind(2)` and :manpage:`connect(2)` operations. This change ensures
> * that only TCP sockets are subject to TCP access rights, allowing other
> * protocols to operate without unnecessary restrictions.
> + *
> + * Impact:
> + *
> + * In kernels without this fix, using ``LANDLOCK_ACCESS_NET_BIND_TCP`` or
> + * ``LANDLOCK_ACCESS_NET_CONNECT_TCP`` would incorrectly restrict non-TCP
> + * stream protocols (SMC, MPTCP, SCTP), potentially breaking applications
> + * that rely on these protocols while using Landlock network restrictions.
> */
> LANDLOCK_ERRATUM(1)
> diff --git a/security/landlock/errata/abi-6.h b/security/landlock/errata/abi-6.h
> index 5113a829f87e..5cb1475c7ea8 100644
> --- a/security/landlock/errata/abi-6.h
> +++ b/security/landlock/errata/abi-6.h
> @@ -15,5 +15,15 @@
> * interaction between threads of the same process should always be allowed.
> * This change ensures that any thread is allowed to send signals to any other
> * thread within the same process, regardless of their domain.
> + *
> + * Impact:
> + *
> + * This problem only manifests when the userspace process is itself using
> + * :manpage:`libpsx(3)` or an equivalent mechanism to enforce a Landlock policy
> + * on multiple already-running threads at once. Programs which enforce a
> + * Landlock policy at startup time and only then become multithreaded are not
> + * affected. Without this fix, signal scoping could break multi-threaded
> + * applications that expect threads within the same process to freely signal
> + * each other.
> */
> LANDLOCK_ERRATUM(2)
> diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c
> index 8eaec8d35c44..9b7a7f39f26c 100644
> --- a/security/landlock/syscalls.c
> +++ b/security/landlock/syscalls.c
> @@ -158,9 +158,11 @@ static const struct file_operations ruleset_fops = {
> /*
> * The Landlock ABI version should be incremented for each new Landlock-related
> * user space visible change (e.g. Landlock syscalls). This version should
> - * only be incremented once per Linux release, and the date in
> + * only be incremented once per Linux release. When incrementing, the date in
> * Documentation/userspace-api/landlock.rst should be updated to reflect the
> * UAPI change.
> + * If the change involves a fix that requires userspace awareness, also update
> + * the errata documentation in Documentation/userspace-api/landlock.rst.
> */
> const int landlock_abi_version = 9;
>
> --
> 2.50.1
>
Reviewed-by: Günther Noack <gnoack3000@gmail.com>
Thanks!
–Günther
^ permalink raw reply
* Re: [PATCH v2 3/6] samples/landlock: Support LANDLOCK_SCOPE_PATHNAME_UNIX_SOCKET
From: Tingmao Wang @ 2026-01-31 17:48 UTC (permalink / raw)
To: Mickaël Salaün
Cc: Günther Noack, Demi Marie Obenour, Alyssa Ross, Jann Horn,
Tahera Fahimi, Justin Suess, linux-security-module
In-Reply-To: <20260129.Hizei3ea8eew@digikod.net>
On 1/29/26 21:27, Mickaël Salaün wrote:
> We should have a (potentially small) description of what this patch
> does, even if it's a bit redundant with the subject.
>
>
> On Tue, Dec 30, 2025 at 05:20:21PM +0000, Tingmao Wang wrote:
>> Signed-off-by: Tingmao Wang <m@maowtm.org>
>> ---
>>
>> I've decided to use "u" as the character to control this scope bit since
>> it stands for (normal) Unix sockets. Imo using "p" or "n" would make it less
>> clear / memorable. Open to suggestions.
>
> Looks good to me.
>
>>
>> Also, open to suggestion whether socket scoping (pathname and abstract)
>> should be enabled by default, if LL_SCOPED is not set. This would break
>> backward compatibility, but maybe we shouldn't guarentee backward
>> compatibility of this sandboxer in the first place, and almost all cases
>> of Landlock usage would want socket scoping.
>
> I agree that this example could have better defaults, but this should be
> done with a standalone patch series. An important point to keep in mind
> is that this example is used by developers (e.g. potential copy/paste),
> so we need to be careful to not encourage them to create code which is
> backward incompatible. I think the best way to do it is to request a
> default behavior for a specific Landlock ABI version (e.g. with a new
> parameter).
Just to make sure we're on the same page, I was only talking about whether
we keep the behavior of the sandboxer "backward compatible" (i.e. if
someone ran a program that relied on accessing UNIX sockets of more
privileged programs, if we make the sandboxer start enforcing socket
scoping by default, their program would stop working under this
sandboxer), I was not suggesting that we do something which will cause the
sandboxer itself to no longer work on older kernels.
But on second thought, the sandboxer is already not designed to be relied
upon to always behave the same way after an update, since the user don't
get to choose which handled access rights are added to the ruleset. With
new bits added to either ACCESS_FS_ROUGHLY_READ or
ACCESS_FS_ROUGHLY_WRITE, the policy effectively gets more restrictive
automatically. For example, once Günther's patch [1] that adds
LANDLOCK_ACCESS_FS_RESOLVE_UNIX is merged, the sandboxer will effectively
starts restricting pathname UNIX sockets "by default" anyway (under any
dirs not listed in LL_FS_RW). So maybe we don't need to think too hard
about this.
[1]: https://lore.kernel.org/all/20260119203457.97676-6-gnoack3000@gmail.com/
^ permalink raw reply
* Re: [PATCH v2 0/6] Landlock: Implement scope control for pathname Unix sockets
From: Tingmao Wang @ 2026-01-31 17:41 UTC (permalink / raw)
To: Mickaël Salaün, Günther Noack, Demi Marie Obenour
Cc: Günther Noack, Alyssa Ross, Jann Horn, Tahera Fahimi,
Justin Suess, linux-security-module
In-Reply-To: <20260109.Ino1ahfef1iu@digikod.net>
On 1/9/26 12:01, Mickaël Salaün wrote:
> On Wed, Dec 31, 2025 at 11:54:27AM -0500, Demi Marie Obenour wrote:
>> On 12/30/25 18:16, Günther Noack wrote:
>>> [...]
>>> On Tue, Dec 30, 2025 at 05:20:18PM +0000, Tingmao Wang wrote:
>>>> [...]
>>>
>>> What is unclear to me from the examples and the description is: Why is
>>> the boundary between allowed and denied connection targets drawn at
>>> the border of the Landlock domain (the "scope") and why don't we solve
>>> this with the file-system-based approach described in [1]?
>>>
>>> **Do we have existing use cases where a service is both offered and
>>> connected to all from within the same Landlock domain, and where the
>>> process enforcing the policy does not control the child process enough
>>> so that it would be possible to allow-list it with a
>>> LANDLOCK_ACCESS_FS_CONNECT_UNIX rule?**
>
> Yes, with the sandboxer use case. It's similar to a container that
> doesn't know all programs that could be run in it. We should be able to
> create sandboxes that don't assume (nor restrict) internal IPCs (or any
> kind of direct process I/O).
>
> Landlock should make it possible to scope any kind of IPC. It's a
> mental model which is easy to understand and that should be enforced by
> default. Of course, we need some ways to add exceptions to this
> deny-by-default policy, and that's why we also need the FS-based control
> mechanism.
>
>>>
>>> If we do not have such a use case, it seems that the planned FS-based
>>> control mechanism from [1] would do the same job? Long term, we might
>>> be better off if we only have only one control -- as we have discussed
>>> in [2], having two of these might mean that they interact in
>>> unconventional and possibly confusing ways.
>>
>> I agree with this.
>
> Both approaches are complementary/orthogonal and make sense long term
> too. This might be the first time we can restrict the same operations,
> but I'm pretty sure it will not be the last, and it's OK. Handled FS
> access and scoped restrictions are two ways to describe a part of the
> security policy. Having different ways makes the interface much simpler
> than a generic one that would have to take into account all potential
> future access controls.
>
> One thing to keep in mind is that UAPI doesn't have to map 1:1 to the
> kernel implementation, but the UAPI is stable and future proof, whereas
> the kernel implementation can change a lot.
>
> The ruleset's handled fields serve two purposes: define what should be
> denied by default, and define which type of rules are valid
> (compatibility). The ruleset's scoped field serve similar purposes but
> it also implies implicit rules (i.e. communications with processes
> inside the sandbox are allowed). Without the soped field, we would have
> to create dedicated handled field per type (i.e. scope's bit) and
> dedicated rule type for the related handled field, which would make the
> interface more generic but also more complex, for something which is not
> needed.
>
> In a nutshell, in the case of the FS-based and scope-based unix socket
> control, we should see one kind of restrictions (e.g. connect to unix
> socket), which can accept two types of rules: (explicit) file path, or
> (implicit) peer's scope. Access should be granted as long as a rule
> matches, whatever its type.
>
> This rationale should be explained in a commit message.
>
>>
>>> Apart from that, there are some other weaker hints that make me
>>> slightly critical of this patch set:
>>>
>>> * We discussed the idea that a FS-based path_beneath rule would act
>>> implicitly also as an exception for
>>> LANDLOCK_SCOPE_PATHNAME_UNIX_SOCKET, in [2] - one possible way to
>>> interpret this is that the gravity of the system's logic pulls us
>>> back towards a FS-based control, and we would have to swim less
>>> against the stream if we integrated the Unix connect() control in
>>> that way?
While I do think being able to control UNIX socket access via domain
scoping is useful (after all, it is an additional bit of information /
relationship that "normal" filesystem objects doesn't have, and
applications using Landlock might find it useful to control access based
on this extra information, either to simplify the policy, or because a
generic sandboxer can't come up with a pre-determined list of UNIX socket
fs rules, e.g. for applications which places socket in hard-coded "/tmp"),
I do share the worry about how this might get confusing from an API
perspective, as discussed in my GitHub comment [1].
Another way to put it is that, if FS-based and scope-based controls
interacts in the above proposed way, both mechanisms feel like "poking
holes" in the other. But as Mickaël said, one can think of the two
mechanisms not as independent controls, but rather as two interfaces for
the same control. The socket access control is "enabled" if either the
LANDLOCK_ACCESS_FS_RESOLVE_UNIX access is handled, or the scope bit
proposed in this patch is enabled.
With that said, I can think of some alternative ways that might make this
API look "better" (from a subjective point of view, feedback welcome),
however it does mean more delays, and specifically, these will depend on
LANDLOCK_ACCESS_FS_RESOLVE_UNIX:
One possibility is to simply always allow a Landlock domain to connect to
its own sockets (in the case where LANDLOCK_ACCESS_FS_RESOLVE_UNIX is
handled, otherwise all sockets are allowed). This might be reasonable, as
one can only connect to a socket it creates if it has the permission to
create it in the first place, which is already controlled by
LANDLOCK_ACCESS_FS_MAKE_SOCK, so we don't really lose any policy
flexibility here - if for some reason the sandboxer don't want to allow
access to any (pathname) sockets, even the sandboxed app's own ones, it
can just not allow LANDLOCK_ACCESS_FS_MAKE_SOCK anywhere.
Another possibility is to have this scope control as part of the
LANDLOCK_ACCESS_FS_RESOLVE_UNIX rule itself, rather than as a separate
scope bit, via introducing a "rule flag" (which is a new mechanism
proposed in [2]) which I will tentatively call
(LANDLOCK_ADD_RULE_)SAME_SCOPE_ONLY. So for example:
- If the sandboxer wants to allow all socket access, don't handle
LANDLOCK_ACCESS_FS_RESOLVE_UNIX at all, or handle it but have an allow
rule at / with no flags.
- If it wants to allow access to only the sandboxed app's own sockets,
handle LANDLOCK_ACCESS_FS_RESOLVE_UNIX, then place one allow rule on /
with the rule flag SAME_SCOPE_ONLY. This means that the allow rule
"UNIX socket under /" only allows connecting to sockets in the same
domain scope. Additional rules without this rule flag can be placed on
more specific places to add "exceptions" to allow access to more
privileged sockets.
- To allow access to specific sockets only, not even the sandboxed app's
own sockets, just use LANDLOCK_ACCESS_FS_RESOLVE_UNIX without this new
rule flag.
This is slightly more flexible than just the LANDLOCK_SCOPE_PATHNAME_UNIX_SOCKET
scope bit plus LANDLOCK_ACCESS_FS_RESOLVE_UNIX, as it allows the sandboxer
to specify where an application can connect, even with sockets in its own
domain, and it also nicely avoids the "what feels like two independent
controls poking holes in each other" problem. But it does mean more
complexity (although hopefully not too much), as we now need to introduce
the rule flags concept, but that is required for some other proposed stuff
anyway - quiet flags [3] and no_inherit [4] rules, and the rule flags
design is in a good state I think.
What do folks think?
(btw, when I say "sandboxed app" this can of course also mean multiple
applications in the same sandbox, e.g. like a container runtime as Mickaël
pointed out, which raises the probability that just having
LANDLOCK_ACCESS_FS_RESOLVE_UNIX would be insufficient, e.g. when bind
mounts are involved, or other cases which I haven't thought of right now)
[1]: https://github.com/landlock-lsm/linux/issues/36#issuecomment-3693123942
[2]: https://lore.kernel.org/all/f238931bc813fc50fc8e11a007a8ad2136024df3.1766330134.git.m@maowtm.org/
[3]: https://lore.kernel.org/all/cover.1766330134.git.m@maowtm.org/
[4]: https://lore.kernel.org/all/20251221194301.247484-1-utilityemal77@gmail.com/
^ permalink raw reply
* Re: [PATCH net-next v2 0/4] net: uapi: Provide an UAPI definition of 'struct sockaddr'
From: Jakub Kicinski @ 2026-01-31 17:25 UTC (permalink / raw)
To: Thomas Weißschuh
Cc: Eric Dumazet, Kuniyuki Iwashima, Paolo Abeni, Willem de Bruijn,
David S. Miller, Simon Horman, Shuah Khan, Matthieu Baerts,
Mat Martineau, Geliang Tang, Mickaël Salaün,
Günther Noack, Alexei Starovoitov, Daniel Borkmann,
Jesper Dangaard Brouer, John Fastabend, Stanislav Fomichev,
Andrii Nakryiko, Martin KaFai Lau, Eduard Zingerman, Song Liu,
Yonghong Song, KP Singh, Hao Luo, Jiri Olsa, netdev, linux-kernel,
linux-api, Arnd Bergmann, linux-kselftest, mptcp,
linux-security-module, bpf, libc-alpha, Carlos O'Donell,
Adhemerval Zanella, Rich Felker, klibc, Florian Weimer
In-Reply-To: <3c46fc49-b41b-44a2-b42a-669cc7e6bb02@linutronix.de>
On Sat, 31 Jan 2026 11:26:32 +0100 Thomas Weißschuh wrote:
> Jan 30, 2026 17:17:46 Jakub Kicinski <kuba@kernel.org>:
>
> > On Fri, 30 Jan 2026 11:34:15 +0100 Thomas Weißschuh wrote:
> >> Some of them get broken by the new 'struct sockaddr', but some others are
> >> already broken just by the new transitive inclusion of libc-compat.h.
> >> So any header starting to use the compatibility machinery may trigger breakage
> >> in code including UAPI headers before libc header, even for completely new type
> >> definitions which themselves would not conflict with libc.
> >
> > Let's split the uAPI header changes from any selftest changes.
> > If you're saying the the selftests no longer build after the uAPI
> > header changes then of course we can't apply the patches.
>
> Yes, the selftests don't build anymore after the uAPI changes.
>
> "can't apply" as in
> * "can't apply separately"
> * "are unacceptable in general"
this one
> * "are too late for this cycle"
> ?
>
> None of this is urgent.
> We can do the selftests in one cycle and the uAPI in another one.
> Feel free to pick up the patches as you see fit.
> (The mptcp changes already go through their tree, so need to be dropped here)
> I can also resubmit the patches differently if preferred.
The selftests are just a canary in the coalmine. If we break a bunch of
selftests chances are we'll also break compilation of real applications
for people. Subjective, but I don't see a sufficient upside here to do
that.
FWIW the typelimits change broke compilation of ethtool, we'll see if
anyone "outside kernel community itself" complains.
^ permalink raw reply
* Re: [PATCH net-next v2 0/4] net: uapi: Provide an UAPI definition of 'struct sockaddr'
From: Thomas Weißschuh @ 2026-01-31 10:26 UTC (permalink / raw)
To: Jakub Kicinski
Cc: Eric Dumazet, Kuniyuki Iwashima, Paolo Abeni, Willem de Bruijn,
David S. Miller, Simon Horman, Shuah Khan, Matthieu Baerts,
Mat Martineau, Geliang Tang, Mickaël Salaün,
Günther Noack, Alexei Starovoitov, Daniel Borkmann,
Jesper Dangaard Brouer, John Fastabend, Stanislav Fomichev,
Andrii Nakryiko, Martin KaFai Lau, Eduard Zingerman, Song Liu,
Yonghong Song, KP Singh, Hao Luo, Jiri Olsa, netdev, linux-kernel,
linux-api, Arnd Bergmann, linux-kselftest, mptcp,
linux-security-module, bpf, libc-alpha, Carlos O'Donell,
Adhemerval Zanella, Rich Felker, klibc, Florian Weimer
In-Reply-To: <20260130081741.425a92e0@kernel.org>
Jan 30, 2026 17:17:46 Jakub Kicinski <kuba@kernel.org>:
> On Fri, 30 Jan 2026 11:34:15 +0100 Thomas Weißschuh wrote:
>>> Are all those selftests / samples getting broken by this patch set?
>>
>> Yes.
>>
>> Some of them get broken by the new 'struct sockaddr', but some others are
>> already broken just by the new transitive inclusion of libc-compat.h.
>> So any header starting to use the compatibility machinery may trigger breakage
>> in code including UAPI headers before libc header, even for completely new type
>> definitions which themselves would not conflict with libc.
>
> Let's split the uAPI header changes from any selftest changes.
> If you're saying the the selftests no longer build after the uAPI
> header changes then of course we can't apply the patches.
Yes, the selftests don't build anymore after the uAPI changes.
"can't apply" as in
* "can't apply separately"
* "are unacceptable in general"
* "are too late for this cycle"
?
None of this is urgent.
We can do the selftests in one cycle and the uAPI in another one.
Feel free to pick up the patches as you see fit.
(The mptcp changes already go through their tree, so need to be dropped here)
I can also resubmit the patches differently if preferred.
Thomas
^ permalink raw reply
* Re: [PATCH] ipe: document AT_EXECVE_CHECK TOCTOU issue on OverlayFS
From: Amir Goldstein @ 2026-01-31 10:22 UTC (permalink / raw)
To: Fan Wu
Cc: linux-security-module, linux-doc, linux-kernel, corbet, mic,
miklos, linux-unionfs, linux-fsdevel
In-Reply-To: <CAKtyLkG1_evj7=yrB6nH-8fJuP3DsWyxwrhv7O_Mk=Fy8FOvVw@mail.gmail.com>
[+CC fsdevel]
On Fri, Jan 30, 2026 at 8:21 PM Fan Wu <wufan@kernel.org> wrote:
>
> On Fri, Jan 30, 2026 at 3:06 AM Amir Goldstein <amir73il@gmail.com> wrote:
> >
> > On Fri, Jan 30, 2026 at 1:14 AM <wufan@kernel.org> wrote:
> > >
> > > From: Fan Wu <wufan@kernel.org>
> > >
> > > Document a known TOCTOU (time-of-check to time-of-use) issue when using
> > > AT_EXECVE_CHECK with read() on OverlayFS.
> >
> > Hi Fan Wu,
> >
> > TBH, I don't like the way that this problem is being framed.
> > IIUC, the problem is using IPE on a non-read-only fs.
> > Is that correct?
> >
> > That fact that IPE metadata is usually coupled with read-only fs
> > is interesting for the understanding of the use case, but unless
> > IPE feature mandates read-only fs, this is a generic problem.
> >
> > OverlayFS is just one private case, which happens to be common
> > in Android or containers? IDK, you did not mention this.
> >
> > Please describe the problem as a generic problem and give
> > overlayfs as an example, preferable with references to the
> > real world use cases.
> >
> > If I misunderstood, please explain why this problem is exclusive
> > to overlayfs.
> >
> > Thanks,
> > Amir.
> >
>
> Hi Amir,
>
> Thanks for the review. That's exactly why we CC'd you and the
> overlayfs folks, we wanted to get your perspective before documenting
> this.
>
> Let me give some background. IPE enforces execution policy based on
> file integrity properties, primarily dm-verity and fs-verity. These
> are the trust anchors, and files without these protections won't be
> trusted by IPE. Since dm-verity and fs-verity are inherently
> read-only, in typical deployments the TOCTOU issue doesn't exist. To
> support overlayfs, IPE uses d_real_inode() to look through the overlay
> and get the real inode from the lower layer.
>
> Recently a new feature AT_EXECVE_CHECK was introduced to allow script
> interpreters to request LSM checks on script files before execution.
> The idea is: interpreter opens the script, calls execveat() with
> AT_EXECVE_CHECK to verify the file passes security policy, then reads
> and executes the content.
>
> What we found is that on overlayfs with a dm-verity lower layer and
> writable upper layer, when a script file only exists in the lower
> layer, AT_EXECVE_CHECK passes because IPE sees it's dm-verity
> protected. But if another process writes to the same path after
> execveat() returns, copy-up happens and subsequent read() from the
> original fd returns content from the upper layer. We verified this
> through testing.
I don't understand how this is different from any AT_EXECVE_CHECK
TOCTOU race on a writable filesystem, regardless of IPE.
It seems to me that it is the user calling AT_EXECVE_CHECK who
is responsible for verifying after reading that file has not changed and if
it has changed, then the AT_EXECVE_CHECK could be invalidated
(depending on policy).
Maybe multi-grain ctime could provide a safe cache invalidating check?
As long as the filesystem is trusted to report true MG ctime.
See below regarding overlayfs...
>
> Overlayfs is popular in container environments, so we want to document
> this for IPE users.
>
> We noticed the overlayfs documentation
> (https://docs.kernel.org/filesystems/overlayfs.html#non-standard-behavior)
> states that if a lower layer file is opened and memory mapped,
> subsequent changes are not reflected in the memory mapping. We also
> verified this: mmap keeps the original lower layer content after
> copy-up. One reason we CC'd you is to ask: is relying on mmap to keep
> the original lower file reference a reasonable choice? Or would you
> recommend against depending on this behavior?
I recommend against depending on this behavior.
Please do not document this as a solution.
It sounds like you are documenting a recipe for how to write a safe
interpreter?
The advice to mount overlayfs read only seems impractical to 90%
of the container users which use overlayfs specifically to add writability
over a read only image.
If the generic ctime check is not considered reliable enough for checking
if a file was modified and copied up, what about checking the
STATX_ATTR_VERITY flag of the file?
ovl_getattr() reports the STATX_ flags from the "upper most" inode
and then merges some specific fields from lower layers.
I think that would mean that in the use case to describe, a copy up
would result in STATX_ATTR_VERITY going away when doing
fstat() after a copy up.
If this works, better document it as a good solution.
>
> The narrative in the patch can definitely be adjusted. Would something
> like this work better:
>
> "When using AT_EXECVE_CHECK on overlayfs, if the lower layer is
> integrity-protected but the upper layer is writable, a copy-up between
> the check and read() may cause the interpreter to read unverified
> content."
Sounds fine to me, as long as it is clear that overlayfs is just
a private case of the AT_EXECVE_CHECK TOCOU race.
Thanks,
Amir.
^ permalink raw reply
* Re: [PATCH v4 00/17] module: Introduce hash-based integrity checking
From: Mihai-Drosi Câju @ 2026-01-31 7:36 UTC (permalink / raw)
To: linux
Cc: arnd, arnout, atomlin, bigeasy, chleroy, christian, corbet, coxu,
da.gomez, da.gomez, dmitry.kasatkin, eric.snowberg,
f.gruenbichler, jmorris, kpcyrd, linux-arch, linux-doc,
linux-integrity, linux-kbuild, linux-kernel, linux-modules,
linux-security-module, linuxppc-dev, lkp, maddy, mattia, mcaju95,
mcgrof, mpe, nathan, naveen, nicolas.bouchinet, nicolas.schier,
npiggin, nsc, paul, petr.pavlu, roberto.sassu, samitolvanen,
serge, xiujianfeng, zohar
In-Reply-To: <20260113-module-hashes-v4-0-0b932db9b56b@weissschuh.net>
> The current signature-based module integrity checking has some drawbacks
in combination with reproducible builds. Either the module signing key
is generated at build time, which makes the build unreproducible, or a
static signing key is used, which precludes rebuilds by third parties
and makes the whole build and packaging process much more complicated.
I think there is a middle ground where the module signing key is generated
using a key derivation function that has as an input a deterministic value
on the build host, such as /etc/machine-id . The problem with this approach
is that only hosts knowing the value will be able to reproduce the build.
Maybe this is a solution to NixOS secret management? Introduce minimal
impurity as a cryptographic seed and derive the rest of the secrets using
something like Argon2(seed, key_uuid).
There might be another approach to code integrity rather than step-by-step
reproducibility. One may exploit the very cryptographic primitives that make
reproducibility hard to ensure that reproducibility is most likely valid.
For example, the module signing issue, the build host publishes four artifacts:
* The source-code
* The compiled and signed binary
* The build environment
* Its public key
Now, we don't need to sign with the private key to know that building the source
code using the specific build environment and signing the result with the private
key will result in the claimed binary. We can just compile and verify with the
public key.
So a traditional workflow would be:
compiled_module + module_signature == module
In this case we build the module, sign it with whatever key, distribute the
builds and the private key to whoever wants to reproduce the build. Or we build
locally and the key stays with the end-user.
While the cryptographic approach would be:
verify(compiled_code, module.signature) is True
In this case we distribute the builds, source code and the public key. While
everyone can ensure that the compiled code is the result of the build
environment and source code. The signature is verified using cryptographic
means.
As long as no one cracks RSA or an algorithm of our choosing/has an absurd
amount of luck, the cryptographic approach would be just as good as the traditional
approach at ensuring that a program has stopped with a certain output.
^ permalink raw reply
* Re: [PATCH] xfrm: kill xfrm_dev_{state,policy}_flush_secctx_check()
From: Tetsuo Handa @ 2026-01-31 6:00 UTC (permalink / raw)
To: Paul Moore, SELinux, linux-security-module
Cc: Steffen Klassert, Herbert Xu, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman, Network Development
In-Reply-To: <CAHC9VhQEKfxXzFgYShojESpQn10LES5zL6Ua0YV9b8seEKFqyA@mail.gmail.com>
On 2026/01/31 6:56, Paul Moore wrote:
> On Wed, Jan 28, 2026 at 5:28 AM Tetsuo Handa
> <penguin-kernel@i-love.sakura.ne.jp> wrote:
>> On 2026/01/28 6:59, Paul Moore wrote:
>>> It sounds like we either need to confirm that
>>> security_xfrm_{policy,state}_delete() is already present in all code
>>> paths that result in SPD/SAD deletions (in a place that can safely
>>> fail and return an error),
>>
>> Yes.
>
> To clarify, do you mean "yes, I agree", or "yes, I've already checked
> this and can confirm that the LSM hooks are already being called"?
I mean "yes, I agree".
>
>>> or we need to place
>>> xfrm_dev_{policy,state}_flush_secctx_check() in a location that can
>>> safely fail.
>>
>> Did you mean xfrm_{policy,state}_flush_secctx_check() ?
>
> They both call into the security_xfrm_policy_delete() LSM hook which
> is what I care about as that hook is what authorizes the operation.
I can't understand what your authorization is.
No authorization can be placed during must-not-fail operation.
For example, please consider the following sequence.
mkdir /mnt/tmpfs
unshare -m
mount -t tmpfs none /mnt/tmpfs
touch /mnt/tmpfs/file1
mkdir /mnt/tmpfs/dir1
exit
Although there are LSM hooks for deleting a file/directory and LSM hook for
unmounting, no LSM hook is called before deleting file1, deleting dir1,
and unmounting /mnt/tmpfs because operations that happen during tear-down of
a namespace must not fail.
What "[PATCH] xfrm: kill xfrm_dev_{state,policy}_flush_secctx_check()" is doing
is the same thing for a network device. NETDEV_UNREGISTER is a must-not-fail
operation that happens during tear-down of a network device. LSM hooks are not
allowed to veto must-not-fail operations.
>
>> Regarding xfrm_policy_flush() as an example, we can observe that we are
>> calling LSM hooks for must-not-fail callers ...
>
> We need to make sure the LSM hooks are being called to authorize the
> removal of SPD and SAD entries. If you are going to remove LSM hooks
> from the existing code, please document how that code path you are
> changing is still subject to authorization by the LSM hooks or explain
> in great detail how that authorization is not necessary.
Again, LSM hooks are not allowed to veto operations that happen during
NETDEV_UNREGISTER event. Current XFRM code for NETDEV_UNREGISTER event is broken
because the behavior is
while (security_xfrm_state_delete() != 0) {
schedule_timeout_uninterruptible(10 * HZ);
pr_emerg("unregister_netdevice: waiting for %s to become free. Usage count = %d\n",
dev->name, netdev_refcnt_read(dev));
}
while (security_xfrm_policy_delete() != 0) {
schedule_timeout_uninterruptible(10 * HZ);
pr_emerg("unregister_netdevice: waiting for %s to become free. Usage count = %d\n",
dev->name, netdev_refcnt_read(dev));
}
which is a denial-of-service kernel bug.
No room for debating "authorize the removal of SPD and SAD entries".
This path is a must-not-fail operation. Removal of SPD and SAD entries has to be done
without authorization when NETDEV_UNREGISTER event fired.
Though, you could argue whether removal of SPD and SAD entries is really what they want
when NETDEV_DOWN event fired. I don't know what the authors of commit d77e38e612a0
("xfrm: Add an IPsec hardware offloading API") are expecting because deleted entries
are not automatically revived when NETDEV_UP event fires
( https://lkml.kernel.org/r/cea4b855-fe94-4b4e-9c2d-3cef7aac1be3@I-love.SAKURA.ne.jp ).
Hmm, does
https://elixir.bootlin.com/linux/v6.19-rc5/source/include/linux/netdevice.h#L3142 mean that
LSM hooks are as well not allowed to veto operations that happen during
NETDEV_UP/NETDEV_DOWN events?
^ permalink raw reply
* [PATCH v5 3/3] ima: Use kstat.ctime as a fallback change detection for stacked fs
From: Frederick Lawler @ 2026-01-30 22:39 UTC (permalink / raw)
To: Mimi Zohar, Roberto Sassu, Dmitry Kasatkin, Eric Snowberg,
Paul Moore, James Morris, Serge E. Hallyn, Darrick J. Wong,
Christian Brauner, Josef Bacik, Jeff Layton
Cc: linux-kernel, linux-integrity, linux-security-module, kernel-team,
Frederick Lawler
In-Reply-To: <20260130-xfs-ima-fixup-v5-0-57e84ea91712@cloudflare.com>
IMA performs unnecessary measurements on files in stacked file systems
that do not set kstat.change_cookie to an inode's i_version.
For example: TMPFS (upper) is stacked onto XFS (lower).
Actions files result in re-measurement because commit 1cf7e834a6fb
("xfs: switch to multigrain timestamps") introduced multigrain
timestamps into XFS which changed the kstat.change_cookie semantics
to no longer supply an i_version to compare against in
integrity_inode_attributes_changed(). Once the inode is in TMPFS,
the change detection behavior operates as normal because TMPFS updates
kstat.change_cookie to the i_version.
Instead, fall back onto a ctime comparison. This also gives file systems
that do not support i_version an opportunity avoid the same behavior,
as they're more likely to have ctime support.
timespec64_to_ns() is chosen to avoid adding extra storage to
integrity_inode_attributes by leveraging the existing version field.
Link: https://lore.kernel.org/all/aTspr4_h9IU4EyrR@CMGLRV3
Fixes: 1cf7e834a6fb ("xfs: switch to multigrain timestamps")
Suggested-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Frederick Lawler <fred@cloudflare.com>
---
include/linux/integrity.h | 6 +++++-
security/integrity/ima/ima_api.c | 11 ++++++++---
security/integrity/ima/ima_main.c | 2 +-
3 files changed, 14 insertions(+), 5 deletions(-)
diff --git a/include/linux/integrity.h b/include/linux/integrity.h
index 382c783f0fa3ae4a938cdf9559291ba1903a378e..ec2c94907f417c4a71ecce29ac79edac9bc2c6f8 100644
--- a/include/linux/integrity.h
+++ b/include/linux/integrity.h
@@ -10,6 +10,7 @@
#include <linux/fs.h>
#include <linux/iversion.h>
#include <linux/kernel.h>
+#include <linux/time64.h>
enum integrity_status {
INTEGRITY_PASS = 0,
@@ -58,6 +59,9 @@ integrity_inode_attrs_stat_changed
if (stat->result_mask & STATX_CHANGE_COOKIE)
return stat->change_cookie != attrs->version;
+ if (stat->result_mask & STATX_CTIME)
+ return timespec64_to_ns(&stat->ctime) != (s64)attrs->version;
+
return true;
}
@@ -84,7 +88,7 @@ integrity_inode_attrs_changed(const struct integrity_inode_attributes *attrs,
* only for IMA if vfs_getattr_nosec() fails.
*/
if (!file || vfs_getattr_nosec(&file->f_path, &stat,
- STATX_CHANGE_COOKIE,
+ STATX_CHANGE_COOKIE | STATX_CTIME,
AT_STATX_SYNC_AS_STAT))
return !IS_I_VERSION(inode) ||
!inode_eq_iversion(inode, attrs->version);
diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c
index c35ea613c9f8d404ba4886e3b736c3bab29d1668..e47d6281febc15a0ac1bd2ea1d28fea4d0cd5c58 100644
--- a/security/integrity/ima/ima_api.c
+++ b/security/integrity/ima/ima_api.c
@@ -272,10 +272,15 @@ int ima_collect_measurement(struct ima_iint_cache *iint, struct file *file,
* to an initial measurement/appraisal/audit, but was modified to
* assume the file changed.
*/
- result = vfs_getattr_nosec(&file->f_path, &stat, STATX_CHANGE_COOKIE,
+ result = vfs_getattr_nosec(&file->f_path, &stat,
+ STATX_CHANGE_COOKIE | STATX_CTIME,
AT_STATX_SYNC_AS_STAT);
- if (!result && (stat.result_mask & STATX_CHANGE_COOKIE))
- i_version = stat.change_cookie;
+ if (!result) {
+ if (stat.result_mask & STATX_CHANGE_COOKIE)
+ i_version = stat.change_cookie;
+ else if (stat.result_mask & STATX_CTIME)
+ i_version = timespec64_to_ns(&stat.ctime);
+ }
hash.hdr.algo = algo;
hash.hdr.length = hash_digest_size[algo];
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index 8cb17c9d446caaa5a98f5ec8f027c17ba7babca8..776db158b0bd8a0d053729ac0cc15af8b6020a98 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -199,7 +199,7 @@ static void ima_check_last_writer(struct ima_iint_cache *iint,
&iint->atomic_flags);
if ((iint->flags & IMA_NEW_FILE) ||
vfs_getattr_nosec(&file->f_path, &stat,
- STATX_CHANGE_COOKIE,
+ STATX_CHANGE_COOKIE | STATX_CTIME,
AT_STATX_SYNC_AS_STAT) ||
integrity_inode_attrs_stat_changed(&iint->real_inode,
&stat)) {
--
2.43.0
^ permalink raw reply related
* [PATCH v5 2/3] ima: Make integrity_inode_attrs_changed() call into vfs
From: Frederick Lawler @ 2026-01-30 22:39 UTC (permalink / raw)
To: Mimi Zohar, Roberto Sassu, Dmitry Kasatkin, Eric Snowberg,
Paul Moore, James Morris, Serge E. Hallyn, Darrick J. Wong,
Christian Brauner, Josef Bacik, Jeff Layton
Cc: linux-kernel, linux-integrity, linux-security-module, kernel-team,
Frederick Lawler
In-Reply-To: <20260130-xfs-ima-fixup-v5-0-57e84ea91712@cloudflare.com>
Align integrity_inode_attrs_changed() to ima_check_last_writer()'s
semantics when detecting changes.
For IMA, stacked file systems that do not set kstat.change_cookie,
integrity_inode_attrs_changed() will compare zero to zero, thus no
change detected. This is not dissimilar to what
ima_check_last_writer() does.
No logical change intended for EVM.
Signed-off-by: Frederick Lawler <fred@cloudflare.com>
---
include/linux/integrity.h | 28 ++++++++++++++++++++++++----
security/integrity/evm/evm_main.c | 5 ++---
security/integrity/ima/ima_main.c | 5 ++---
3 files changed, 28 insertions(+), 10 deletions(-)
diff --git a/include/linux/integrity.h b/include/linux/integrity.h
index beb9ab19fa6257e79266b58bcb5f55b0c5445828..382c783f0fa3ae4a938cdf9559291ba1903a378e 100644
--- a/include/linux/integrity.h
+++ b/include/linux/integrity.h
@@ -9,6 +9,7 @@
#include <linux/fs.h>
#include <linux/iversion.h>
+#include <linux/kernel.h>
enum integrity_status {
INTEGRITY_PASS = 0,
@@ -62,14 +63,33 @@ integrity_inode_attrs_stat_changed
/*
* On stacked filesystems detect whether the inode or its content has changed.
+ *
+ * Must be called in process context.
*/
static inline bool
integrity_inode_attrs_changed(const struct integrity_inode_attributes *attrs,
- const struct inode *inode)
+ struct file *file, struct inode *inode)
{
- return (inode->i_sb->s_dev != attrs->dev ||
- inode->i_ino != attrs->ino ||
- !inode_eq_iversion(inode, attrs->version));
+ struct kstat stat;
+
+ might_sleep();
+
+ if (inode->i_sb->s_dev != attrs->dev || inode->i_ino != attrs->ino)
+ return true;
+
+ /*
+ * EVM currently relies on backing inode i_version. While IS_I_VERSION
+ * is not a good indicator of i_version support, this still retains
+ * the logic such that a re-evaluation should still occur for EVM, and
+ * only for IMA if vfs_getattr_nosec() fails.
+ */
+ if (!file || vfs_getattr_nosec(&file->f_path, &stat,
+ STATX_CHANGE_COOKIE,
+ AT_STATX_SYNC_AS_STAT))
+ return !IS_I_VERSION(inode) ||
+ !inode_eq_iversion(inode, attrs->version);
+
+ return integrity_inode_attrs_stat_changed(attrs, &stat);
}
diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c
index 73d500a375cb37a54f295b0e1e93fd6e5d9ecddc..6a4e0e246005246d5700b1db590c1759242b9cb6 100644
--- a/security/integrity/evm/evm_main.c
+++ b/security/integrity/evm/evm_main.c
@@ -752,9 +752,8 @@ bool evm_metadata_changed(struct inode *inode, struct inode *metadata_inode)
bool ret = false;
if (iint) {
- ret = (!IS_I_VERSION(metadata_inode) ||
- integrity_inode_attrs_changed(&iint->metadata_inode,
- metadata_inode));
+ ret = integrity_inode_attrs_changed(&iint->metadata_inode,
+ NULL, metadata_inode);
if (ret)
iint->evm_status = INTEGRITY_UNKNOWN;
}
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index 6570ad10887b9ea1172c78274cf62482350e87ff..8cb17c9d446caaa5a98f5ec8f027c17ba7babca8 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -328,9 +328,8 @@ static int process_measurement(struct file *file, const struct cred *cred,
real_inode = d_real_inode(file_dentry(file));
if (real_inode != inode &&
(action & IMA_DO_MASK) && (iint->flags & IMA_DONE_MASK)) {
- if (!IS_I_VERSION(real_inode) ||
- integrity_inode_attrs_changed(&iint->real_inode,
- real_inode)) {
+ if (integrity_inode_attrs_changed(&iint->real_inode,
+ file, real_inode)) {
iint->flags &= ~IMA_DONE_MASK;
iint->measured_pcrs = 0;
}
--
2.43.0
^ permalink raw reply related
* [PATCH v5 1/3] ima: Unify vfs_getattr_nosec() stat comparisons under helper function
From: Frederick Lawler @ 2026-01-30 22:39 UTC (permalink / raw)
To: Mimi Zohar, Roberto Sassu, Dmitry Kasatkin, Eric Snowberg,
Paul Moore, James Morris, Serge E. Hallyn, Darrick J. Wong,
Christian Brauner, Josef Bacik, Jeff Layton
Cc: linux-kernel, linux-integrity, linux-security-module, kernel-team,
Frederick Lawler
In-Reply-To: <20260130-xfs-ima-fixup-v5-0-57e84ea91712@cloudflare.com>
The logic for comparing kstat.change_cookie against IMA version is
hard to read. Abstract comparison logic into a new function
integrity_inode_attrs_stat_changed().
No functional change intended.
Signed-off-by: Frederick Lawler <fred@cloudflare.com>
---
include/linux/integrity.h | 11 +++++++++++
security/integrity/ima/ima_main.c | 4 ++--
2 files changed, 13 insertions(+), 2 deletions(-)
diff --git a/include/linux/integrity.h b/include/linux/integrity.h
index f5842372359be5341b6870a43b92e695e8fc78af..beb9ab19fa6257e79266b58bcb5f55b0c5445828 100644
--- a/include/linux/integrity.h
+++ b/include/linux/integrity.h
@@ -49,6 +49,17 @@ integrity_inode_attrs_store(struct integrity_inode_attributes *attrs,
attrs->ino = inode->i_ino;
}
+/* Compares stat attributes for change detection. */
+static inline bool
+integrity_inode_attrs_stat_changed
+(const struct integrity_inode_attributes *attrs, const struct kstat *stat)
+{
+ if (stat->result_mask & STATX_CHANGE_COOKIE)
+ return stat->change_cookie != attrs->version;
+
+ return true;
+}
+
/*
* On stacked filesystems detect whether the inode or its content has changed.
*/
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index 5770cf691912aa912fc65280c59f5baac35dd725..6570ad10887b9ea1172c78274cf62482350e87ff 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -201,8 +201,8 @@ static void ima_check_last_writer(struct ima_iint_cache *iint,
vfs_getattr_nosec(&file->f_path, &stat,
STATX_CHANGE_COOKIE,
AT_STATX_SYNC_AS_STAT) ||
- !(stat.result_mask & STATX_CHANGE_COOKIE) ||
- stat.change_cookie != iint->real_inode.version) {
+ integrity_inode_attrs_stat_changed(&iint->real_inode,
+ &stat)) {
iint->flags &= ~(IMA_DONE_MASK | IMA_NEW_FILE);
iint->measured_pcrs = 0;
if (update)
--
2.43.0
^ permalink raw reply related
* [PATCH v5 0/3] ima: Detect changes to files via kstat changes rather than i_version
From: Frederick Lawler @ 2026-01-30 22:39 UTC (permalink / raw)
To: Mimi Zohar, Roberto Sassu, Dmitry Kasatkin, Eric Snowberg,
Paul Moore, James Morris, Serge E. Hallyn, Darrick J. Wong,
Christian Brauner, Josef Bacik, Jeff Layton
Cc: linux-kernel, linux-integrity, linux-security-module, kernel-team,
Frederick Lawler
We uncovered a case in kernels >= 6.13 where XFS is no longer updating
struct kstat.change_cookie on i_op getattr() access calls. Instead, XFS is
using multigrain ctime (as well as other file systems) for
change detection in commit 1cf7e834a6fb ("xfs: switch to
multigrain timestamps").
Because file systems may implement i_version as they see fit, IMA
unnecessarily measures files in stacked file systems. This is due
to the LOWER or UPPER FS not updating kstat.change_cookie to the
recent i_version on request. Thus, for XFS, zero is being compared
against the inode's i_version directly, and is always behind.
We're proposing to compare against the kstat.change_cookie
directly to the cached version, and fall back to a ctime comparison,
if STATX_CHANGE_COOKIE is not supplied in the result mask.
EVM is largely left alone since there's no trivial way to query a file
directly in the LSM call paths to obtain kstat.change_cookie &
kstat.ctime to cache. Thus retains accessing i_version directly.
Regression tests will be added to the Linux Test Project instead of
selftest to help catch future file system changes that may impact
future evaluation of IMA.
I'd like this to be backported to at least 6.18 if possible.
Patches 1 & 2 are preparation patches. Ideally patch 2 is squashed into
3, though not strictly necessary.
Below is a simplified test that demonstrates the issue such that
there are multiple unnecessary measurements occurring for actions on
a file in a stacked TMPFS on XFS, prior to the file moved over to TMPFS:
_fragment.config_
CONFIG_XFS_FS=y
CONFIG_OVERLAY_FS=y
CONFIG_IMA=y
CONFIG_IMA_WRITE_POLICY=y
CONFIG_IMA_READ_POLICY=y
_./test.sh_
IMA_POLICY="/sys/kernel/security/ima/policy"
TEST_BIN="/bin/date"
MNT_BASE="/tmp/ima_test_root"
mkdir -p "$MNT_BASE"
mount -t tmpfs tmpfs "$MNT_BASE"
mkdir -p "$MNT_BASE"/{xfs_disk,upper,work,ovl}
dd if=/dev/zero of="$MNT_BASE/xfs.img" bs=1M count=300
mkfs.xfs -q "$MNT_BASE/xfs.img"
mount "$MNT_BASE/xfs.img" "$MNT_BASE/xfs_disk"
cp "$TEST_BIN" "$MNT_BASE/xfs_disk/test_prog"
mount -t overlay overlay -o \
"lowerdir=$MNT_BASE/xfs_disk,upperdir=$MNT_BASE/upper,workdir=$MNT_BASE/work" \
"$MNT_BASE/ovl"
echo "audit func=BPRM_CHECK uid=$(id -u nobody)" > "$IMA_POLICY"
target_prog="$MNT_BASE/ovl/test_prog"
setpriv --reuid nobody "$target_prog"
setpriv --reuid nobody "$target_prog"
setpriv --reuid nobody "$target_prog"
audit_count=$(dmesg | grep -c "file=\"$target_prog\"")
if [[ "$audit_count" -eq 1 ]]; then
echo "PASS: Found exactly 1 audit event."
else
echo "FAIL: Expected 1 audit event, but found $audit_count."
exit 1
fi
Signed-off-by: Frederick Lawler <fred@cloudflare.com>
---
Changes in v5:
- Split into patch series. [Mimi]
- Link to v4: https://lore.kernel.org/r/20260129-xfs-ima-fixup-v4-1-6bb89df7b6a3@cloudflare.com
Changes in v4:
- No functional changes.
- Add Reviewed-by & Fixes tags.
- Link to v3: https://lore.kernel.org/r/20260122-xfs-ima-fixup-v3-1-20335a8aa836@cloudflare.com
Changes in v3:
- Prefer timespec64_to_ns() to leverage attr.version. [Roberto]
- s/TPMFS/TMPFS/ in description.
- Link to v2: https://lore.kernel.org/r/20260120-xfs-ima-fixup-v2-1-f332ead8b043@cloudflare.com
Changes in v2:
- Updated commit description + message to clarify the problem.
- compare struct timespec64 to avoid collision possibility [Roberto].
- Don't check inode_attr_changed() in ima_check_last_writer()
- Link to v1: https://lore.kernel.org/r/20260112-xfs-ima-fixup-v1-1-8d13b6001312@cloudflare.com
Changes since RFC:
- Remove calls to I_IS_VERSION()
- Function documentation/comments
- Abide IMA/EVM change detection fallback invariants
- Combined ctime guard into version for attributes struct
- Link to RFC: https://lore.kernel.org/r/20251229-xfs-ima-fixup-v1-1-6a717c939f7c@cloudflare.com
---
Frederick Lawler (3):
ima: Unify vfs_getattr_nosec() stat comparisons under helper function
ima: Make integrity_inode_attrs_changed() call into vfs
ima: Use kstat.ctime as a fallback change detection for stacked fs
include/linux/integrity.h | 43 +++++++++++++++++++++++++++++++++++----
security/integrity/evm/evm_main.c | 5 ++---
security/integrity/ima/ima_api.c | 11 +++++++---
security/integrity/ima/ima_main.c | 11 +++++-----
4 files changed, 54 insertions(+), 16 deletions(-)
---
base-commit: 8f0b4cce4481fb22653697cced8d0d04027cb1e8
change-id: 20251212-xfs-ima-fixup-931780a62c2c
Best regards,
--
Frederick Lawler <fred@cloudflare.com>
^ permalink raw reply
* Re: [PATCH] xfrm: kill xfrm_dev_{state,policy}_flush_secctx_check()
From: Paul Moore @ 2026-01-30 21:56 UTC (permalink / raw)
To: Tetsuo Handa
Cc: SELinux, linux-security-module, Steffen Klassert, Herbert Xu,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Network Development
In-Reply-To: <1bb453af-3ef2-4ab6-a909-0705bd07c136@I-love.SAKURA.ne.jp>
On Wed, Jan 28, 2026 at 5:28 AM Tetsuo Handa
<penguin-kernel@i-love.sakura.ne.jp> wrote:
> On 2026/01/28 6:59, Paul Moore wrote:
> > It sounds like we either need to confirm that
> > security_xfrm_{policy,state}_delete() is already present in all code
> > paths that result in SPD/SAD deletions (in a place that can safely
> > fail and return an error),
>
> Yes.
To clarify, do you mean "yes, I agree", or "yes, I've already checked
this and can confirm that the LSM hooks are already being called"?
> > or we need to place
> > xfrm_dev_{policy,state}_flush_secctx_check() in a location that can
> > safely fail.
>
> Did you mean xfrm_{policy,state}_flush_secctx_check() ?
They both call into the security_xfrm_policy_delete() LSM hook which
is what I care about as that hook is what authorizes the operation.
> Regarding xfrm_policy_flush() as an example, we can observe that we are
> calling LSM hooks for must-not-fail callers ...
We need to make sure the LSM hooks are being called to authorize the
removal of SPD and SAD entries. If you are going to remove LSM hooks
from the existing code, please document how that code path you are
changing is still subject to authorization by the LSM hooks or explain
in great detail how that authorization is not necessary.
--
paul-moore.com
^ permalink raw reply
* Re: [PATCH v4 04/17] module: Make mod_verify_sig() static
From: Aaron Tomlin @ 2026-01-30 20:53 UTC (permalink / raw)
To: Thomas Weißschuh
Cc: Nathan Chancellor, Arnd Bergmann, Luis Chamberlain, Petr Pavlu,
Sami Tolvanen, Daniel Gomez, Paul Moore, James Morris,
Serge E. Hallyn, Jonathan Corbet, Madhavan Srinivasan,
Michael Ellerman, Nicholas Piggin, Naveen N Rao, Mimi Zohar,
Roberto Sassu, Dmitry Kasatkin, Eric Snowberg, Nicolas Schier,
Daniel Gomez, Christophe Leroy (CS GROUP), Nicolas Schier,
Nicolas Bouchinet, Xiu Jianfeng, Fabian Grünbichler,
Arnout Engelen, Mattia Rizzolo, kpcyrd, Christian Heusel,
Câju Mihai-Drosi, Sebastian Andrzej Siewior, linux-kbuild,
linux-kernel, linux-arch, linux-modules, linux-security-module,
linux-doc, linuxppc-dev, linux-integrity
In-Reply-To: <20260113-module-hashes-v4-4-0b932db9b56b@weissschuh.net>
[-- Attachment #1: Type: text/plain, Size: 1444 bytes --]
On Tue, Jan 13, 2026 at 01:28:48PM +0100, Thomas Weißschuh wrote:
> It is not used outside of signing.c.
>
> Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
> ---
> kernel/module/internal.h | 1 -
> kernel/module/signing.c | 2 +-
> 2 files changed, 1 insertion(+), 2 deletions(-)
>
> diff --git a/kernel/module/internal.h b/kernel/module/internal.h
> index 618202578b42..e68fbcd60c35 100644
> --- a/kernel/module/internal.h
> +++ b/kernel/module/internal.h
> @@ -119,7 +119,6 @@ struct module_use {
> struct module *source, *target;
> };
>
> -int mod_verify_sig(const void *mod, struct load_info *info);
> int try_to_force_load(struct module *mod, const char *reason);
> bool find_symbol(struct find_symbol_arg *fsa);
> struct module *find_module_all(const char *name, size_t len, bool even_unformed);
> diff --git a/kernel/module/signing.c b/kernel/module/signing.c
> index a2ff4242e623..fe3f51ac6199 100644
> --- a/kernel/module/signing.c
> +++ b/kernel/module/signing.c
> @@ -40,7 +40,7 @@ void set_module_sig_enforced(void)
> /*
> * Verify the signature on a module.
> */
> -int mod_verify_sig(const void *mod, struct load_info *info)
> +static int mod_verify_sig(const void *mod, struct load_info *info)
> {
> struct module_signature ms;
> size_t sig_len, modlen = info->len;
>
> --
> 2.52.0
>
Reviewed-by: Aaron Tomlin <atomlin@atomlin.com>
--
Aaron Tomlin
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply
* Re: [PATCH v4 03/17] ima: efi: Drop unnecessary check for CONFIG_MODULE_SIG/CONFIG_KEXEC_SIG
From: Aaron Tomlin @ 2026-01-30 20:49 UTC (permalink / raw)
To: Thomas Weißschuh
Cc: Nathan Chancellor, Arnd Bergmann, Luis Chamberlain, Petr Pavlu,
Sami Tolvanen, Daniel Gomez, Paul Moore, James Morris,
Serge E. Hallyn, Jonathan Corbet, Madhavan Srinivasan,
Michael Ellerman, Nicholas Piggin, Naveen N Rao, Mimi Zohar,
Roberto Sassu, Dmitry Kasatkin, Eric Snowberg, Nicolas Schier,
Daniel Gomez, Christophe Leroy (CS GROUP), Nicolas Schier,
Nicolas Bouchinet, Xiu Jianfeng, Fabian Grünbichler,
Arnout Engelen, Mattia Rizzolo, kpcyrd, Christian Heusel,
Câju Mihai-Drosi, Sebastian Andrzej Siewior, linux-kbuild,
linux-kernel, linux-arch, linux-modules, linux-security-module,
linux-doc, linuxppc-dev, linux-integrity
In-Reply-To: <20260113-module-hashes-v4-3-0b932db9b56b@weissschuh.net>
[-- Attachment #1: Type: text/plain, Size: 1280 bytes --]
On Tue, Jan 13, 2026 at 01:28:47PM +0100, Thomas Weißschuh wrote:
> When configuration settings are disabled the guarded functions are
> defined as empty stubs, so the check is unnecessary.
> The specific configuration option for set_module_sig_enforced() is
> about to change and removing the checks avoids some later churn.
>
> Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
> ---
> security/integrity/ima/ima_efi.c | 6 ++----
> 1 file changed, 2 insertions(+), 4 deletions(-)
>
> diff --git a/security/integrity/ima/ima_efi.c b/security/integrity/ima/ima_efi.c
> index 138029bfcce1..a35dd166ad47 100644
> --- a/security/integrity/ima/ima_efi.c
> +++ b/security/integrity/ima/ima_efi.c
> @@ -68,10 +68,8 @@ static const char * const sb_arch_rules[] = {
> const char * const *arch_get_ima_policy(void)
> {
> if (IS_ENABLED(CONFIG_IMA_ARCH_POLICY) && arch_ima_get_secureboot()) {
> - if (IS_ENABLED(CONFIG_MODULE_SIG))
> - set_module_sig_enforced();
> - if (IS_ENABLED(CONFIG_KEXEC_SIG))
> - set_kexec_sig_enforced();
> + set_module_sig_enforced();
> + set_kexec_sig_enforced();
> return sb_arch_rules;
> }
> return NULL;
>
> --
> 2.52.0
>
Reviewed-by: Aaron Tomlin <atomlin@atomlin.com>
--
Aaron Tomlin
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply
* Re: [PATCH v4 02/17] powerpc/ima: Drop unnecessary check for CONFIG_MODULE_SIG
From: Aaron Tomlin @ 2026-01-30 20:43 UTC (permalink / raw)
To: Thomas Weißschuh
Cc: Nathan Chancellor, Arnd Bergmann, Luis Chamberlain, Petr Pavlu,
Sami Tolvanen, Daniel Gomez, Paul Moore, James Morris,
Serge E. Hallyn, Jonathan Corbet, Madhavan Srinivasan,
Michael Ellerman, Nicholas Piggin, Naveen N Rao, Mimi Zohar,
Roberto Sassu, Dmitry Kasatkin, Eric Snowberg, Nicolas Schier,
Daniel Gomez, Christophe Leroy (CS GROUP), Nicolas Schier,
Nicolas Bouchinet, Xiu Jianfeng, Fabian Grünbichler,
Arnout Engelen, Mattia Rizzolo, kpcyrd, Christian Heusel,
Câju Mihai-Drosi, Sebastian Andrzej Siewior, linux-kbuild,
linux-kernel, linux-arch, linux-modules, linux-security-module,
linux-doc, linuxppc-dev, linux-integrity
In-Reply-To: <20260113-module-hashes-v4-2-0b932db9b56b@weissschuh.net>
[-- Attachment #1: Type: text/plain, Size: 1163 bytes --]
On Tue, Jan 13, 2026 at 01:28:46PM +0100, Thomas Weißschuh wrote:
> When CONFIG_MODULE_SIG is disabled set_module_sig_enforced() is defined
> as an empty stub, so the check is unnecessary.
> The specific configuration option for set_module_sig_enforced() is
> about to change and removing the check avoids some later churn.
>
> Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
> ---
> arch/powerpc/kernel/ima_arch.c | 3 +--
> 1 file changed, 1 insertion(+), 2 deletions(-)
>
> diff --git a/arch/powerpc/kernel/ima_arch.c b/arch/powerpc/kernel/ima_arch.c
> index b7029beed847..690263bf4265 100644
> --- a/arch/powerpc/kernel/ima_arch.c
> +++ b/arch/powerpc/kernel/ima_arch.c
> @@ -63,8 +63,7 @@ static const char *const secure_and_trusted_rules[] = {
> const char *const *arch_get_ima_policy(void)
> {
> if (is_ppc_secureboot_enabled()) {
> - if (IS_ENABLED(CONFIG_MODULE_SIG))
> - set_module_sig_enforced();
> + set_module_sig_enforced();
>
> if (is_ppc_trustedboot_enabled())
> return secure_and_trusted_rules;
>
> --
> 2.52.0
>
Reviewed-by: Aaron Tomlin <atomlin@atomlin.com>
--
Aaron Tomlin
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply
* Re: [PATCH] ipe: document AT_EXECVE_CHECK TOCTOU issue on OverlayFS
From: Fan Wu @ 2026-01-30 19:21 UTC (permalink / raw)
To: Amir Goldstein
Cc: wufan, linux-security-module, linux-doc, linux-kernel, corbet,
mic, miklos, linux-unionfs
In-Reply-To: <CAOQ4uxhf6EQKcoN055xzmi-RW2GPxRzz_ExsQawGQBSmoX2WYg@mail.gmail.com>
On Fri, Jan 30, 2026 at 3:06 AM Amir Goldstein <amir73il@gmail.com> wrote:
>
> On Fri, Jan 30, 2026 at 1:14 AM <wufan@kernel.org> wrote:
> >
> > From: Fan Wu <wufan@kernel.org>
> >
> > Document a known TOCTOU (time-of-check to time-of-use) issue when using
> > AT_EXECVE_CHECK with read() on OverlayFS.
>
> Hi Fan Wu,
>
> TBH, I don't like the way that this problem is being framed.
> IIUC, the problem is using IPE on a non-read-only fs.
> Is that correct?
>
> That fact that IPE metadata is usually coupled with read-only fs
> is interesting for the understanding of the use case, but unless
> IPE feature mandates read-only fs, this is a generic problem.
>
> OverlayFS is just one private case, which happens to be common
> in Android or containers? IDK, you did not mention this.
>
> Please describe the problem as a generic problem and give
> overlayfs as an example, preferable with references to the
> real world use cases.
>
> If I misunderstood, please explain why this problem is exclusive
> to overlayfs.
>
> Thanks,
> Amir.
>
Hi Amir,
Thanks for the review. That's exactly why we CC'd you and the
overlayfs folks, we wanted to get your perspective before documenting
this.
Let me give some background. IPE enforces execution policy based on
file integrity properties, primarily dm-verity and fs-verity. These
are the trust anchors, and files without these protections won't be
trusted by IPE. Since dm-verity and fs-verity are inherently
read-only, in typical deployments the TOCTOU issue doesn't exist. To
support overlayfs, IPE uses d_real_inode() to look through the overlay
and get the real inode from the lower layer.
Recently a new feature AT_EXECVE_CHECK was introduced to allow script
interpreters to request LSM checks on script files before execution.
The idea is: interpreter opens the script, calls execveat() with
AT_EXECVE_CHECK to verify the file passes security policy, then reads
and executes the content.
What we found is that on overlayfs with a dm-verity lower layer and
writable upper layer, when a script file only exists in the lower
layer, AT_EXECVE_CHECK passes because IPE sees it's dm-verity
protected. But if another process writes to the same path after
execveat() returns, copy-up happens and subsequent read() from the
original fd returns content from the upper layer. We verified this
through testing.
Overlayfs is popular in container environments, so we want to document
this for IPE users.
We noticed the overlayfs documentation
(https://docs.kernel.org/filesystems/overlayfs.html#non-standard-behavior)
states that if a lower layer file is opened and memory mapped,
subsequent changes are not reflected in the memory mapping. We also
verified this: mmap keeps the original lower layer content after
copy-up. One reason we CC'd you is to ask: is relying on mmap to keep
the original lower file reference a reasonable choice? Or would you
recommend against depending on this behavior?
The narrative in the patch can definitely be adjusted. Would something
like this work better:
"When using AT_EXECVE_CHECK on overlayfs, if the lower layer is
integrity-protected but the upper layer is writable, a copy-up between
the check and read() may cause the interpreter to read unverified
content."
Let us know what you think.
-Fan
^ 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