* [PATCH v5 7/9] tpm-buf: check for corruption in tpm_buf_append_handle()
From: Jarkko Sakkinen @ 2025-09-30 23:19 UTC (permalink / raw)
To: linux-integrity
Cc: dpsmith, ross.philipson, Jarkko Sakkinen, Jonathan McDowell,
Peter Huewe, Jarkko Sakkinen, Jason Gunthorpe, David Howells,
Paul Moore, James Morris, Serge E. Hallyn, James Bottomley,
Mimi Zohar, open list, open list:KEYS/KEYRINGS,
open list:SECURITY SUBSYSTEM
In-Reply-To: <20250930231958.1437783-1-jarkko@kernel.org>
From: Jarkko Sakkinen <jarkko.sakkinen@opinsys.com>
Unify TPM_BUF_BOUNDARY_ERROR and TPM_BUF_OVERFLOW into TPM_BUF_INVALID
flag because semantically they are identical.
Test and set TPM_BUF_INVALID in tpm_buf_append_handle() following the
pattern from other functions in tpm-buf.c.
Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@opinsys.com>
Reviewed-by: Jonathan McDowell <noodles@meta.com>
---
v4:
- No changes.
v3:
- No changes.
v2:
- A new patch.
---
drivers/char/tpm/tpm-buf.c | 14 ++++++++------
include/linux/tpm.h | 8 +++-----
security/keys/trusted-keys/trusted_tpm2.c | 6 +++---
3 files changed, 14 insertions(+), 14 deletions(-)
diff --git a/drivers/char/tpm/tpm-buf.c b/drivers/char/tpm/tpm-buf.c
index dc882fc9fa9e..69ee77400539 100644
--- a/drivers/char/tpm/tpm-buf.c
+++ b/drivers/char/tpm/tpm-buf.c
@@ -104,13 +104,12 @@ EXPORT_SYMBOL_GPL(tpm_buf_length);
*/
void tpm_buf_append(struct tpm_buf *buf, const u8 *new_data, u16 new_length)
{
- /* Return silently if overflow has already happened. */
- if (buf->flags & TPM_BUF_OVERFLOW)
+ if (buf->flags & TPM_BUF_INVALID)
return;
if ((buf->length + new_length) > PAGE_SIZE) {
WARN(1, "tpm_buf: write overflow\n");
- buf->flags |= TPM_BUF_OVERFLOW;
+ buf->flags |= TPM_BUF_INVALID;
return;
}
@@ -157,8 +156,12 @@ EXPORT_SYMBOL_GPL(tpm_buf_append_u32);
*/
void tpm_buf_append_handle(struct tpm_chip *chip, struct tpm_buf *buf, u32 handle)
{
+ if (buf->flags & TPM_BUF_INVALID)
+ return;
+
if (buf->flags & TPM_BUF_TPM2B) {
dev_err(&chip->dev, "Invalid buffer type (TPM2B)\n");
+ buf->flags |= TPM_BUF_INVALID;
return;
}
@@ -177,14 +180,13 @@ static void tpm_buf_read(struct tpm_buf *buf, off_t *offset, size_t count, void
{
off_t next_offset;
- /* Return silently if overflow has already happened. */
- if (buf->flags & TPM_BUF_BOUNDARY_ERROR)
+ if (buf->flags & TPM_BUF_INVALID)
return;
next_offset = *offset + count;
if (next_offset > buf->length) {
WARN(1, "tpm_buf: read out of boundary\n");
- buf->flags |= TPM_BUF_BOUNDARY_ERROR;
+ buf->flags |= TPM_BUF_INVALID;
return;
}
diff --git a/include/linux/tpm.h b/include/linux/tpm.h
index e3c65d33e041..4613216e9d95 100644
--- a/include/linux/tpm.h
+++ b/include/linux/tpm.h
@@ -370,12 +370,10 @@ struct tpm_header {
} __packed;
enum tpm_buf_flags {
- /* the capacity exceeded: */
- TPM_BUF_OVERFLOW = BIT(0),
/* TPM2B format: */
- TPM_BUF_TPM2B = BIT(1),
- /* read out of boundary: */
- TPM_BUF_BOUNDARY_ERROR = BIT(2),
+ TPM_BUF_TPM2B = BIT(0),
+ /* The buffer is in invalid and unusable state: */
+ TPM_BUF_INVALID = BIT(1),
};
/*
diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
index 8e3b283a59b2..119d5152c0db 100644
--- a/security/keys/trusted-keys/trusted_tpm2.c
+++ b/security/keys/trusted-keys/trusted_tpm2.c
@@ -295,7 +295,7 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
/* creation PCR */
tpm_buf_append_u32(&buf, 0);
- if (buf.flags & TPM_BUF_OVERFLOW) {
+ if (buf.flags & TPM_BUF_INVALID) {
rc = -E2BIG;
tpm2_end_auth_session(chip);
goto out;
@@ -308,7 +308,7 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
goto out;
blob_len = tpm_buf_read_u32(&buf, &offset);
- if (blob_len > MAX_BLOB_SIZE || buf.flags & TPM_BUF_BOUNDARY_ERROR) {
+ if (blob_len > MAX_BLOB_SIZE || buf.flags & TPM_BUF_INVALID) {
rc = -E2BIG;
goto out;
}
@@ -414,7 +414,7 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
tpm_buf_append(&buf, blob, blob_len);
- if (buf.flags & TPM_BUF_OVERFLOW) {
+ if (buf.flags & TPM_BUF_INVALID) {
rc = -E2BIG;
tpm2_end_auth_session(chip);
goto out;
--
2.39.5
^ permalink raw reply related
* [PATCH v5 8/9] tpm-buf: Remove chip parameter from tpm_buf_append_handle
From: Jarkko Sakkinen @ 2025-09-30 23:19 UTC (permalink / raw)
To: linux-integrity
Cc: dpsmith, ross.philipson, Jarkko Sakkinen, Jonathan McDowell,
Peter Huewe, Jarkko Sakkinen, Jason Gunthorpe, David Howells,
Paul Moore, James Morris, Serge E. Hallyn, open list,
open list:KEYS/KEYRINGS, open list:SECURITY SUBSYSTEM
In-Reply-To: <20250930231958.1437783-1-jarkko@kernel.org>
From: Jarkko Sakkinen <jarkko.sakkinen@opinsys.com>
Remove chip parameter from tpm_buf_append_handle() in order to maintain
decoupled state with tpm-buf. This is mandatory change in order to re-use
the module in early boot code of Trenchboot, and the binding itself brings
no benefit. Use WARN like in other functions, as the error condition can
happen only as a net effect of a trivial programming mistake.
Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@opinsys.com>
Reviewed-by: Jonathan McDowell <noodles@meta.com>
---
v4:
- No changes.
v3:
- No changes.
v2:
- No changes.
---
drivers/char/tpm/tpm-buf.c | 5 ++---
drivers/char/tpm/tpm2-cmd.c | 2 +-
drivers/char/tpm/tpm2-sessions.c | 2 +-
include/linux/tpm.h | 2 +-
4 files changed, 5 insertions(+), 6 deletions(-)
diff --git a/drivers/char/tpm/tpm-buf.c b/drivers/char/tpm/tpm-buf.c
index 69ee77400539..1b9dee0d0681 100644
--- a/drivers/char/tpm/tpm-buf.c
+++ b/drivers/char/tpm/tpm-buf.c
@@ -147,20 +147,19 @@ EXPORT_SYMBOL_GPL(tpm_buf_append_u32);
/**
* tpm_buf_append_handle() - Add a handle
- * @chip: &tpm_chip instance
* @buf: &tpm_buf instance
* @handle: a TPM object handle
*
* Add a handle to the buffer, and increase the count tracking the number of
* handles in the command buffer. Works only for command buffers.
*/
-void tpm_buf_append_handle(struct tpm_chip *chip, struct tpm_buf *buf, u32 handle)
+void tpm_buf_append_handle(struct tpm_buf *buf, u32 handle)
{
if (buf->flags & TPM_BUF_INVALID)
return;
if (buf->flags & TPM_BUF_TPM2B) {
- dev_err(&chip->dev, "Invalid buffer type (TPM2B)\n");
+ WARN(1, "tpm-buf: invalid type: TPM2B\n");
buf->flags |= TPM_BUF_INVALID;
return;
}
diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c
index 254003e5dd8b..e548b0ac7654 100644
--- a/drivers/char/tpm/tpm2-cmd.c
+++ b/drivers/char/tpm/tpm2-cmd.c
@@ -190,7 +190,7 @@ int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
tpm_buf_append_name(chip, &buf, pcr_idx, NULL);
tpm_buf_append_hmac_session(chip, &buf, 0, NULL, 0);
} else {
- tpm_buf_append_handle(chip, &buf, pcr_idx);
+ tpm_buf_append_handle(&buf, pcr_idx);
tpm_buf_append_auth(chip, &buf, NULL, 0);
}
diff --git a/drivers/char/tpm/tpm2-sessions.c b/drivers/char/tpm/tpm2-sessions.c
index 13f019d1312a..bbc05f0997a8 100644
--- a/drivers/char/tpm/tpm2-sessions.c
+++ b/drivers/char/tpm/tpm2-sessions.c
@@ -232,7 +232,7 @@ void tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
#endif
if (!tpm2_chip_auth(chip)) {
- tpm_buf_append_handle(chip, buf, handle);
+ tpm_buf_append_handle(buf, handle);
return;
}
diff --git a/include/linux/tpm.h b/include/linux/tpm.h
index 4613216e9d95..e86c22b30153 100644
--- a/include/linux/tpm.h
+++ b/include/linux/tpm.h
@@ -427,7 +427,7 @@ void tpm_buf_append_u32(struct tpm_buf *buf, const u32 value);
u8 tpm_buf_read_u8(struct tpm_buf *buf, off_t *offset);
u16 tpm_buf_read_u16(struct tpm_buf *buf, off_t *offset);
u32 tpm_buf_read_u32(struct tpm_buf *buf, off_t *offset);
-void tpm_buf_append_handle(struct tpm_chip *chip, struct tpm_buf *buf, u32 handle);
+void tpm_buf_append_handle(struct tpm_buf *buf, u32 handle);
/*
* Check if TPM device is in the firmware upgrade mode.
--
2.39.5
^ permalink raw reply related
* [PATCH v5 9/9] tpm-buf: Enable managed and stack allocations.
From: Jarkko Sakkinen @ 2025-09-30 23:19 UTC (permalink / raw)
To: linux-integrity
Cc: dpsmith, ross.philipson, Jarkko Sakkinen, Stefan Berger,
Peter Huewe, Jarkko Sakkinen, Jason Gunthorpe, David Howells,
Paul Moore, James Morris, Serge E. Hallyn, James Bottomley,
Mimi Zohar, open list, open list:KEYS/KEYRINGS,
open list:SECURITY SUBSYSTEM
In-Reply-To: <20250930231958.1437783-1-jarkko@kernel.org>
From: Jarkko Sakkinen <jarkko.sakkinen@opinsys.com>
Decouple kzalloc from buffer creation, so that a managed allocation can be
done trivially:
struct tpm_buf *buf __free(kfree) buf = kzalloc(TPM_BUFSIZE,
GFP_KERNEL);
if (!buf)
return -ENOMEM;
tpm_buf_init(buf, TPM_BUFSIZE);
Alternatively, stack allocations are also possible:
u8 buf_data[512];
struct tpm_buf *buf = (struct tpm_buf *)buf_data;
tpm_buf_init(buf, sizeof(buf_data));
Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@opinsys.com>
Reviewed-by: Stefan Berger <stefanb@linux.ibm.com>
---
v5:
- There was a spurious change in tpm2_seal_trusted() error
code handling introduce by this patch.
v4:
- Since every single tpm_transmit_cmd() call site needs to be
changed anyhow, use 'dest' parameter more structured and
actually useful way, and pick the string TCG 1.2 and 2.0
specifications.
- tpm1-cmd: Remove useless rc declarations and repliace them
with trivial "return tpm_transmit_cmd" statement.
- Reverted spurious changes in include/linux/tpm.h.
- Use concisely TPM_BUFSIZE instead of PAGE_SIZE.
v3:
- A new patch from the earlier series with more scoped changes and
less abstract commit message.
---
drivers/char/tpm/tpm-buf.c | 122 +++++----
drivers/char/tpm/tpm-sysfs.c | 21 +-
drivers/char/tpm/tpm.h | 1 -
drivers/char/tpm/tpm1-cmd.c | 162 +++++-------
drivers/char/tpm/tpm2-cmd.c | 299 ++++++++++------------
drivers/char/tpm/tpm2-sessions.c | 122 +++++----
drivers/char/tpm/tpm2-space.c | 44 ++--
drivers/char/tpm/tpm_vtpm_proxy.c | 30 +--
include/linux/tpm.h | 18 +-
security/keys/trusted-keys/trusted_tpm1.c | 34 ++-
security/keys/trusted-keys/trusted_tpm2.c | 172 ++++++-------
11 files changed, 484 insertions(+), 541 deletions(-)
diff --git a/drivers/char/tpm/tpm-buf.c b/drivers/char/tpm/tpm-buf.c
index 1b9dee0d0681..a3bf3c3d0c48 100644
--- a/drivers/char/tpm/tpm-buf.c
+++ b/drivers/char/tpm/tpm-buf.c
@@ -7,82 +7,109 @@
#include <linux/module.h>
#include <linux/tpm.h>
-/**
- * tpm_buf_init() - Allocate and initialize a TPM command
- * @buf: A &tpm_buf
- * @tag: TPM_TAG_RQU_COMMAND, TPM2_ST_NO_SESSIONS or TPM2_ST_SESSIONS
- * @ordinal: A command ordinal
- *
- * Return: 0 or -ENOMEM
- */
-int tpm_buf_init(struct tpm_buf *buf, u16 tag, u32 ordinal)
+static void __tpm_buf_size_invariant(struct tpm_buf *buf, u16 buf_size)
{
- buf->data = (u8 *)__get_free_page(GFP_KERNEL);
- if (!buf->data)
- return -ENOMEM;
-
- tpm_buf_reset(buf, tag, ordinal);
- return 0;
+ u32 buf_size_2 = (u32)buf->capacity + (u32)sizeof(*buf);
+
+ if (!buf->capacity) {
+ if (buf_size > TPM_BUFSIZE) {
+ WARN(1, "%s: size overflow: %u\n", __func__, buf_size);
+ buf->flags |= TPM_BUF_INVALID;
+ }
+ } else {
+ if (buf_size != buf_size_2) {
+ WARN(1, "%s: size mismatch: %u != %u\n", __func__, buf_size,
+ buf_size_2);
+ buf->flags |= TPM_BUF_INVALID;
+ }
+ }
}
-EXPORT_SYMBOL_GPL(tpm_buf_init);
-/**
- * tpm_buf_reset() - Initialize a TPM command
- * @buf: A &tpm_buf
- * @tag: TPM_TAG_RQU_COMMAND, TPM2_ST_NO_SESSIONS or TPM2_ST_SESSIONS
- * @ordinal: A command ordinal
- */
-void tpm_buf_reset(struct tpm_buf *buf, u16 tag, u32 ordinal)
+static void __tpm_buf_reset(struct tpm_buf *buf, u16 buf_size, u16 tag, u32 ordinal)
{
struct tpm_header *head = (struct tpm_header *)buf->data;
+ __tpm_buf_size_invariant(buf, buf_size);
+
+ if (buf->flags & TPM_BUF_INVALID)
+ return;
+
WARN_ON(tag != TPM_TAG_RQU_COMMAND && tag != TPM2_ST_NO_SESSIONS &&
tag != TPM2_ST_SESSIONS && tag != 0);
buf->flags = 0;
buf->length = sizeof(*head);
+ buf->capacity = buf_size - sizeof(*buf);
+ buf->handles = 0;
head->tag = cpu_to_be16(tag);
head->length = cpu_to_be32(sizeof(*head));
head->ordinal = cpu_to_be32(ordinal);
+}
+
+static void __tpm_buf_reset_sized(struct tpm_buf *buf, u16 buf_size)
+{
+ __tpm_buf_size_invariant(buf, buf_size);
+
+ if (buf->flags & TPM_BUF_INVALID)
+ return;
+
+ buf->flags = TPM_BUF_TPM2B;
+ buf->length = 2;
+ buf->capacity = buf_size - sizeof(*buf);
buf->handles = 0;
+ buf->data[0] = 0;
+ buf->data[1] = 0;
}
-EXPORT_SYMBOL_GPL(tpm_buf_reset);
/**
- * tpm_buf_init_sized() - Allocate and initialize a sized (TPM2B) buffer
- * @buf: A @tpm_buf
- *
- * Return: 0 or -ENOMEM
+ * tpm_buf_init() - Initialize a TPM command
+ * @buf: A &tpm_buf
+ * @buf_size: Size of the buffer.
*/
-int tpm_buf_init_sized(struct tpm_buf *buf)
+void tpm_buf_init(struct tpm_buf *buf, u16 buf_size)
{
- buf->data = (u8 *)__get_free_page(GFP_KERNEL);
- if (!buf->data)
- return -ENOMEM;
+ memset(buf, 0, buf_size);
+ __tpm_buf_reset(buf, buf_size, TPM_TAG_RQU_COMMAND, 0);
+}
+EXPORT_SYMBOL_GPL(tpm_buf_init);
- tpm_buf_reset_sized(buf);
- return 0;
+/**
+ * tpm_buf_init_sized() - Initialize a sized buffer
+ * @buf: A &tpm_buf
+ * @buf_size: Size of the buffer.
+ */
+void tpm_buf_init_sized(struct tpm_buf *buf, u16 buf_size)
+{
+ memset(buf, 0, buf_size);
+ __tpm_buf_reset_sized(buf, buf_size);
}
EXPORT_SYMBOL_GPL(tpm_buf_init_sized);
/**
- * tpm_buf_reset_sized() - Initialize a sized buffer
+ * tpm_buf_reset() - Re-initialize a TPM command
* @buf: A &tpm_buf
+ * @tag: TPM_TAG_RQU_COMMAND, TPM2_ST_NO_SESSIONS or TPM2_ST_SESSIONS
+ * @ordinal: A command ordinal
*/
-void tpm_buf_reset_sized(struct tpm_buf *buf)
+void tpm_buf_reset(struct tpm_buf *buf, u16 tag, u32 ordinal)
{
- buf->flags = TPM_BUF_TPM2B;
- buf->length = 2;
- buf->data[0] = 0;
- buf->data[1] = 0;
+ u16 buf_size = buf->capacity + sizeof(*buf);
+
+ __tpm_buf_reset(buf, buf_size, tag, ordinal);
}
-EXPORT_SYMBOL_GPL(tpm_buf_reset_sized);
+EXPORT_SYMBOL_GPL(tpm_buf_reset);
-void tpm_buf_destroy(struct tpm_buf *buf)
+/**
+ * tpm_buf_reset_sized() - Re-initialize a sized buffer
+ * @buf: A &tpm_buf
+ */
+void tpm_buf_reset_sized(struct tpm_buf *buf)
{
- free_page((unsigned long)buf->data);
+ u16 buf_size = buf->capacity + sizeof(*buf);
+
+ __tpm_buf_reset_sized(buf, buf_size);
}
-EXPORT_SYMBOL_GPL(tpm_buf_destroy);
+EXPORT_SYMBOL_GPL(tpm_buf_reset_sized);
/**
* tpm_buf_length() - Return the number of bytes consumed by the data
@@ -92,6 +119,9 @@ EXPORT_SYMBOL_GPL(tpm_buf_destroy);
*/
u32 tpm_buf_length(struct tpm_buf *buf)
{
+ if (buf->flags & TPM_BUF_INVALID)
+ return 0;
+
return buf->length;
}
EXPORT_SYMBOL_GPL(tpm_buf_length);
@@ -104,10 +134,12 @@ EXPORT_SYMBOL_GPL(tpm_buf_length);
*/
void tpm_buf_append(struct tpm_buf *buf, const u8 *new_data, u16 new_length)
{
+ u32 total_length = (u32)buf->length + (u32)new_length;
+
if (buf->flags & TPM_BUF_INVALID)
return;
- if ((buf->length + new_length) > PAGE_SIZE) {
+ if (total_length > (u32)buf->capacity) {
WARN(1, "tpm_buf: write overflow\n");
buf->flags |= TPM_BUF_INVALID;
return;
diff --git a/drivers/char/tpm/tpm-sysfs.c b/drivers/char/tpm/tpm-sysfs.c
index 94231f052ea7..f5dcadb1ab3c 100644
--- a/drivers/char/tpm/tpm-sysfs.c
+++ b/drivers/char/tpm/tpm-sysfs.c
@@ -32,28 +32,29 @@ struct tpm_readpubek_out {
static ssize_t pubek_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
- struct tpm_buf tpm_buf;
struct tpm_readpubek_out *out;
int i;
char *str = buf;
struct tpm_chip *chip = to_tpm_chip(dev);
char anti_replay[20];
+ struct tpm_buf *tpm_buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+ if (!tpm_buf)
+ return -ENOMEM;
+
memset(&anti_replay, 0, sizeof(anti_replay));
if (tpm_try_get_ops(chip))
return 0;
- if (tpm_buf_init(&tpm_buf, TPM_TAG_RQU_COMMAND, TPM_ORD_READPUBEK))
- goto out_ops;
-
- tpm_buf_append(&tpm_buf, anti_replay, sizeof(anti_replay));
+ tpm_buf_init(tpm_buf, TPM_BUFSIZE);
+ tpm_buf_reset(tpm_buf, TPM_TAG_RQU_COMMAND, TPM_ORD_READPUBEK);
+ tpm_buf_append(tpm_buf, anti_replay, sizeof(anti_replay));
- if (tpm_transmit_cmd(chip, &tpm_buf, READ_PUBEK_RESULT_MIN_BODY_SIZE,
- "attempting to read the PUBEK"))
- goto out_buf;
+ if (tpm_transmit_cmd(chip, tpm_buf, READ_PUBEK_RESULT_MIN_BODY_SIZE, "TPM_ReadPubek"))
+ goto out_ops;
- out = (struct tpm_readpubek_out *)&tpm_buf.data[10];
+ out = (struct tpm_readpubek_out *)&tpm_buf->data[10];
str +=
sprintf(str,
"Algorithm: %4ph\n"
@@ -71,8 +72,6 @@ static ssize_t pubek_show(struct device *dev, struct device_attribute *attr,
for (i = 0; i < 256; i += 16)
str += sprintf(str, "%16ph\n", &out->modulus[i]);
-out_buf:
- tpm_buf_destroy(&tpm_buf);
out_ops:
tpm_put_ops(chip);
return str - buf;
diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h
index 769fa6b00c54..eaff2055b541 100644
--- a/drivers/char/tpm/tpm.h
+++ b/drivers/char/tpm/tpm.h
@@ -32,7 +32,6 @@
#endif
#define TPM_MINOR 224 /* officially assigned */
-#define TPM_BUFSIZE 4096
#define TPM_NUM_DEVICES 65536
#define TPM_RETRY 50
diff --git a/drivers/char/tpm/tpm1-cmd.c b/drivers/char/tpm/tpm1-cmd.c
index 5c49bdff33de..10cb5d008c9f 100644
--- a/drivers/char/tpm/tpm1-cmd.c
+++ b/drivers/char/tpm/tpm1-cmd.c
@@ -323,20 +323,14 @@ unsigned long tpm1_calc_ordinal_duration(struct tpm_chip *chip, u32 ordinal)
*/
static int tpm1_startup(struct tpm_chip *chip)
{
- struct tpm_buf buf;
- int rc;
-
- dev_info(&chip->dev, "starting up the TPM manually\n");
-
- rc = tpm_buf_init(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_STARTUP);
- if (rc < 0)
- return rc;
-
- tpm_buf_append_u16(&buf, TPM_ST_CLEAR);
-
- rc = tpm_transmit_cmd(chip, &buf, 0, "attempting to start the TPM");
- tpm_buf_destroy(&buf);
- return rc;
+ struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ tpm_buf_init(buf, TPM_BUFSIZE);
+ tpm_buf_reset(buf, TPM_TAG_RQU_COMMAND, TPM_ORD_STARTUP);
+ tpm_buf_append_u16(buf, TPM_ST_CLEAR);
+ return tpm_transmit_cmd(chip, buf, 0, "TPM_Startup");
}
int tpm1_get_timeouts(struct tpm_chip *chip)
@@ -463,50 +457,45 @@ int tpm1_get_timeouts(struct tpm_chip *chip)
int tpm1_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, const u8 *hash,
const char *log_msg)
{
- struct tpm_buf buf;
- int rc;
-
- rc = tpm_buf_init(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_PCR_EXTEND);
- if (rc)
- return rc;
+ struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
- tpm_buf_append_u32(&buf, pcr_idx);
- tpm_buf_append(&buf, hash, TPM_DIGEST_SIZE);
-
- rc = tpm_transmit_cmd(chip, &buf, TPM_DIGEST_SIZE, log_msg);
- tpm_buf_destroy(&buf);
- return rc;
+ tpm_buf_append_u32(buf, pcr_idx);
+ tpm_buf_append(buf, hash, TPM_DIGEST_SIZE);
+ return tpm_transmit_cmd(chip, buf, TPM_DIGEST_SIZE, log_msg);
}
#define TPM_ORD_GET_CAP 101
ssize_t tpm1_getcap(struct tpm_chip *chip, u32 subcap_id, cap_t *cap,
const char *desc, size_t min_cap_length)
{
- struct tpm_buf buf;
int rc;
- rc = tpm_buf_init(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_GET_CAP);
- if (rc)
- return rc;
+ struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ tpm_buf_init(buf, TPM_BUFSIZE);
+ tpm_buf_reset(buf, TPM_TAG_RQU_COMMAND, TPM_ORD_GET_CAP);
if (subcap_id == TPM_CAP_VERSION_1_1 ||
subcap_id == TPM_CAP_VERSION_1_2) {
- tpm_buf_append_u32(&buf, subcap_id);
- tpm_buf_append_u32(&buf, 0);
+ tpm_buf_append_u32(buf, subcap_id);
+ tpm_buf_append_u32(buf, 0);
} else {
if (subcap_id == TPM_CAP_FLAG_PERM ||
subcap_id == TPM_CAP_FLAG_VOL)
- tpm_buf_append_u32(&buf, TPM_CAP_FLAG);
+ tpm_buf_append_u32(buf, TPM_CAP_FLAG);
else
- tpm_buf_append_u32(&buf, TPM_CAP_PROP);
+ tpm_buf_append_u32(buf, TPM_CAP_PROP);
- tpm_buf_append_u32(&buf, 4);
- tpm_buf_append_u32(&buf, subcap_id);
+ tpm_buf_append_u32(buf, 4);
+ tpm_buf_append_u32(buf, subcap_id);
}
- rc = tpm_transmit_cmd(chip, &buf, min_cap_length, desc);
+ rc = tpm_transmit_cmd(chip, buf, min_cap_length, desc);
if (!rc)
- *cap = *(cap_t *)&buf.data[TPM_HEADER_SIZE + 4];
- tpm_buf_destroy(&buf);
+ *cap = *(cap_t *)&buf->data[TPM_HEADER_SIZE + 4];
return rc;
}
EXPORT_SYMBOL_GPL(tpm1_getcap);
@@ -531,81 +520,69 @@ int tpm1_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
{
struct tpm1_get_random_out *out;
u32 num_bytes = min_t(u32, max, TPM_MAX_RNG_DATA);
- struct tpm_buf buf;
u32 total = 0;
int retries = 5;
u32 recd;
int rc;
- rc = tpm_buf_init(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_GET_RANDOM);
- if (rc)
- return rc;
+ struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+ tpm_buf_init(buf, TPM_BUFSIZE);
do {
- tpm_buf_append_u32(&buf, num_bytes);
+ tpm_buf_reset(buf, TPM_TAG_RQU_COMMAND, TPM_ORD_GET_RANDOM);
+ tpm_buf_append_u32(buf, num_bytes);
- rc = tpm_transmit_cmd(chip, &buf, sizeof(out->rng_data_len),
- "attempting get random");
+ rc = tpm_transmit_cmd(chip, buf, sizeof(out->rng_data_len), "TPM_GetRandom");
if (rc) {
if (rc > 0)
rc = -EIO;
- goto out;
+ return rc;
}
- out = (struct tpm1_get_random_out *)&buf.data[TPM_HEADER_SIZE];
+ out = (struct tpm1_get_random_out *)&buf->data[TPM_HEADER_SIZE];
recd = be32_to_cpu(out->rng_data_len);
- if (recd > num_bytes) {
- rc = -EFAULT;
- goto out;
- }
+ if (recd > num_bytes)
+ return -EFAULT;
+
+ if (buf->length < TPM_HEADER_SIZE +
+ sizeof(out->rng_data_len) + recd)
+ return -EFAULT;
- if (tpm_buf_length(&buf) < TPM_HEADER_SIZE +
- sizeof(out->rng_data_len) + recd) {
- rc = -EFAULT;
- goto out;
- }
memcpy(dest, out->rng_data, recd);
dest += recd;
total += recd;
num_bytes -= recd;
-
- tpm_buf_reset(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_GET_RANDOM);
} while (retries-- && total < max);
rc = total ? (int)total : -EIO;
-out:
- tpm_buf_destroy(&buf);
return rc;
}
#define TPM_ORD_PCRREAD 21
int tpm1_pcr_read(struct tpm_chip *chip, u32 pcr_idx, u8 *res_buf)
{
- struct tpm_buf buf;
int rc;
- rc = tpm_buf_init(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_PCRREAD);
- if (rc)
- return rc;
+ struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
- tpm_buf_append_u32(&buf, pcr_idx);
+ tpm_buf_init(buf, TPM_BUFSIZE);
+ tpm_buf_reset(buf, TPM_TAG_RQU_COMMAND, TPM_ORD_PCRREAD);
+ tpm_buf_append_u32(buf, pcr_idx);
- rc = tpm_transmit_cmd(chip, &buf, TPM_DIGEST_SIZE,
- "attempting to read a pcr value");
+ rc = tpm_transmit_cmd(chip, buf, TPM_DIGEST_SIZE, "TPM_PCRRead");
if (rc)
- goto out;
-
- if (tpm_buf_length(&buf) < TPM_DIGEST_SIZE) {
- rc = -EFAULT;
- goto out;
- }
+ return rc;
- memcpy(res_buf, &buf.data[TPM_HEADER_SIZE], TPM_DIGEST_SIZE);
+ if (buf->length < TPM_DIGEST_SIZE)
+ return -EFAULT;
-out:
- tpm_buf_destroy(&buf);
+ memcpy(res_buf, &buf->data[TPM_HEADER_SIZE], TPM_DIGEST_SIZE);
return rc;
}
@@ -619,16 +596,13 @@ int tpm1_pcr_read(struct tpm_chip *chip, u32 pcr_idx, u8 *res_buf)
*/
static int tpm1_continue_selftest(struct tpm_chip *chip)
{
- struct tpm_buf buf;
- int rc;
-
- rc = tpm_buf_init(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_CONTINUE_SELFTEST);
- if (rc)
- return rc;
+ struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
- rc = tpm_transmit_cmd(chip, &buf, 0, "continue selftest");
- tpm_buf_destroy(&buf);
- return rc;
+ tpm_buf_init(buf, TPM_BUFSIZE);
+ tpm_buf_reset(buf, TPM_TAG_RQU_COMMAND, TPM_ORD_CONTINUE_SELFTEST);
+ return tpm_transmit_cmd(chip, buf, 0, "TPM_ContinueSelfTest");
}
/**
@@ -742,22 +716,24 @@ int tpm1_auto_startup(struct tpm_chip *chip)
int tpm1_pm_suspend(struct tpm_chip *chip, u32 tpm_suspend_pcr)
{
u8 dummy_hash[TPM_DIGEST_SIZE] = { 0 };
- struct tpm_buf buf;
unsigned int try;
int rc;
+ struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
/* for buggy tpm, flush pcrs with extend to selected dummy */
if (tpm_suspend_pcr)
rc = tpm1_pcr_extend(chip, tpm_suspend_pcr, dummy_hash,
"extending dummy pcr before suspend");
- rc = tpm_buf_init(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_SAVESTATE);
- if (rc)
- return rc;
+ tpm_buf_init(buf, TPM_BUFSIZE);
+ tpm_buf_reset(buf, TPM_TAG_RQU_COMMAND, TPM_ORD_SAVESTATE);
+
/* now do the actual savestate */
for (try = 0; try < TPM_RETRY; try++) {
- rc = tpm_transmit_cmd(chip, &buf, 0, NULL);
+ rc = tpm_transmit_cmd(chip, buf, 0, NULL);
/*
* If the TPM indicates that it is too busy to respond to
* this command then retry before giving up. It can take
@@ -772,7 +748,7 @@ int tpm1_pm_suspend(struct tpm_chip *chip, u32 tpm_suspend_pcr)
break;
tpm_msleep(TPM_TIMEOUT_RETRY);
- tpm_buf_reset(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_SAVESTATE);
+ tpm_buf_reset(buf, TPM_TAG_RQU_COMMAND, TPM_ORD_SAVESTATE);
}
if (rc)
@@ -782,7 +758,5 @@ int tpm1_pm_suspend(struct tpm_chip *chip, u32 tpm_suspend_pcr)
dev_warn(&chip->dev, "TPM savestate took %dms\n",
try * TPM_TIMEOUT_RETRY);
- tpm_buf_destroy(&buf);
-
return rc;
}
diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c
index e548b0ac7654..768719bbbf04 100644
--- a/drivers/char/tpm/tpm2-cmd.c
+++ b/drivers/char/tpm/tpm2-cmd.c
@@ -104,12 +104,15 @@ int tpm2_pcr_read(struct tpm_chip *chip, u32 pcr_idx,
{
int i;
int rc;
- struct tpm_buf buf;
struct tpm2_pcr_read_out *out;
u8 pcr_select[TPM2_PCR_SELECT_MIN] = {0};
u16 digest_size;
u16 expected_digest_size = 0;
+ struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
if (pcr_idx >= TPM2_PLATFORM_PCR)
return -EINVAL;
@@ -124,36 +127,31 @@ int tpm2_pcr_read(struct tpm_chip *chip, u32 pcr_idx,
expected_digest_size = chip->allocated_banks[i].digest_size;
}
- rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_PCR_READ);
- if (rc)
- return rc;
+ tpm_buf_init(buf, TPM_BUFSIZE);
+ tpm_buf_reset(buf, TPM2_ST_NO_SESSIONS, TPM2_CC_PCR_READ);
pcr_select[pcr_idx >> 3] = 1 << (pcr_idx & 0x7);
- tpm_buf_append_u32(&buf, 1);
- tpm_buf_append_u16(&buf, digest->alg_id);
- tpm_buf_append_u8(&buf, TPM2_PCR_SELECT_MIN);
- tpm_buf_append(&buf, (const unsigned char *)pcr_select,
+ tpm_buf_append_u32(buf, 1);
+ tpm_buf_append_u16(buf, digest->alg_id);
+ tpm_buf_append_u8(buf, TPM2_PCR_SELECT_MIN);
+ tpm_buf_append(buf, (const unsigned char *)pcr_select,
sizeof(pcr_select));
- rc = tpm_transmit_cmd(chip, &buf, 0, "attempting to read a pcr value");
+ rc = tpm_transmit_cmd(chip, buf, 0, "TPM2_PCR_Read");
if (rc)
- goto out;
+ return rc;
- out = (struct tpm2_pcr_read_out *)&buf.data[TPM_HEADER_SIZE];
+ out = (struct tpm2_pcr_read_out *)&buf->data[TPM_HEADER_SIZE];
digest_size = be16_to_cpu(out->digest_size);
if (digest_size > sizeof(digest->digest) ||
- (!digest_size_ptr && digest_size != expected_digest_size)) {
- rc = -EINVAL;
- goto out;
- }
+ (!digest_size_ptr && digest_size != expected_digest_size))
+ return rc;
if (digest_size_ptr)
*digest_size_ptr = digest_size;
memcpy(digest->digest, out->digest, digest_size);
-out:
- tpm_buf_destroy(&buf);
return rc;
}
@@ -169,46 +167,45 @@ int tpm2_pcr_read(struct tpm_chip *chip, u32 pcr_idx,
int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
struct tpm_digest *digests)
{
- struct tpm_buf buf;
int rc;
int i;
+ struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
if (!disable_pcr_integrity) {
rc = tpm2_start_auth_session(chip);
if (rc)
return rc;
}
- rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_PCR_EXTEND);
- if (rc) {
- if (!disable_pcr_integrity)
- tpm2_end_auth_session(chip);
- return rc;
- }
+ tpm_buf_init(buf, TPM_BUFSIZE);
+ tpm_buf_reset(buf, TPM2_ST_SESSIONS, TPM2_CC_PCR_EXTEND);
if (!disable_pcr_integrity) {
- tpm_buf_append_name(chip, &buf, pcr_idx, NULL);
- tpm_buf_append_hmac_session(chip, &buf, 0, NULL, 0);
+ tpm_buf_append_name(chip, buf, pcr_idx, NULL);
+ tpm_buf_append_hmac_session(chip, buf, 0, NULL, 0);
} else {
- tpm_buf_append_handle(&buf, pcr_idx);
- tpm_buf_append_auth(chip, &buf, NULL, 0);
+ tpm_buf_append_handle(buf, pcr_idx);
+ tpm_buf_append_auth(chip, buf, NULL, 0);
}
- tpm_buf_append_u32(&buf, chip->nr_allocated_banks);
+ tpm_buf_append_u32(buf, chip->nr_allocated_banks);
for (i = 0; i < chip->nr_allocated_banks; i++) {
- tpm_buf_append_u16(&buf, digests[i].alg_id);
- tpm_buf_append(&buf, (const unsigned char *)&digests[i].digest,
+ tpm_buf_append_u16(buf, digests[i].alg_id);
+ tpm_buf_append(buf, (const unsigned char *)&digests[i].digest,
chip->allocated_banks[i].digest_size);
}
+ if (buf->flags & TPM_BUF_INVALID)
+ return -EINVAL;
if (!disable_pcr_integrity)
- tpm_buf_fill_hmac_session(chip, &buf);
- rc = tpm_transmit_cmd(chip, &buf, 0, "attempting extend a PCR value");
+ tpm_buf_fill_hmac_session(chip, buf);
+ rc = tpm_transmit_cmd(chip, buf, 0, "TPM2_PCR_Extend");
if (!disable_pcr_integrity)
- rc = tpm_buf_check_hmac_response(chip, &buf, rc);
-
- tpm_buf_destroy(&buf);
+ rc = tpm_buf_check_hmac_response(chip, buf, rc);
return rc;
}
@@ -233,7 +230,6 @@ int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
{
struct tpm2_get_random_out *out;
struct tpm_header *head;
- struct tpm_buf buf;
u32 recd;
u32 num_bytes = max;
int err;
@@ -245,51 +241,50 @@ int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
if (!num_bytes || max > TPM_MAX_RNG_DATA)
return -EINVAL;
+ struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
err = tpm2_start_auth_session(chip);
if (err)
return err;
- err = tpm_buf_init(&buf, 0, 0);
- if (err) {
- tpm2_end_auth_session(chip);
- return err;
- }
-
+ tpm_buf_init(buf, TPM_BUFSIZE);
do {
- tpm_buf_reset(&buf, TPM2_ST_SESSIONS, TPM2_CC_GET_RANDOM);
+ tpm_buf_reset(buf, TPM2_ST_SESSIONS, TPM2_CC_GET_RANDOM);
if (tpm2_chip_auth(chip)) {
- tpm_buf_append_hmac_session(chip, &buf,
+ tpm_buf_append_hmac_session(chip, buf,
TPM2_SA_ENCRYPT |
TPM2_SA_CONTINUE_SESSION,
NULL, 0);
} else {
- offset = buf.handles * 4 + TPM_HEADER_SIZE;
- head = (struct tpm_header *)buf.data;
- if (tpm_buf_length(&buf) == offset)
+ offset = buf->handles * 4 + TPM_HEADER_SIZE;
+ head = (struct tpm_header *)buf->data;
+ if (tpm_buf_length(buf) == offset)
head->tag = cpu_to_be16(TPM2_ST_NO_SESSIONS);
}
- tpm_buf_append_u16(&buf, num_bytes);
- tpm_buf_fill_hmac_session(chip, &buf);
- err = tpm_transmit_cmd(chip, &buf,
+ tpm_buf_append_u16(buf, num_bytes);
+ tpm_buf_fill_hmac_session(chip, buf);
+ err = tpm_transmit_cmd(chip, buf,
offsetof(struct tpm2_get_random_out,
buffer),
- "attempting get random");
- err = tpm_buf_check_hmac_response(chip, &buf, err);
+ "TPM2_GetRandom");
+ err = tpm_buf_check_hmac_response(chip, buf, err);
if (err) {
if (err > 0)
err = -EIO;
goto out;
}
- head = (struct tpm_header *)buf.data;
+ head = (struct tpm_header *)buf->data;
offset = TPM_HEADER_SIZE;
/* Skip the parameter size field: */
if (be16_to_cpu(head->tag) == TPM2_ST_SESSIONS)
offset += 4;
- out = (struct tpm2_get_random_out *)&buf.data[offset];
+ out = (struct tpm2_get_random_out *)&buf->data[offset];
recd = min_t(u32, be16_to_cpu(out->size), num_bytes);
- if (tpm_buf_length(&buf) <
+ if (tpm_buf_length(buf) <
TPM_HEADER_SIZE +
offsetof(struct tpm2_get_random_out, buffer) +
recd) {
@@ -303,11 +298,9 @@ int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
num_bytes -= recd;
} while (retries-- && total < max);
- tpm_buf_destroy(&buf);
-
return total ? total : -EIO;
+
out:
- tpm_buf_destroy(&buf);
tpm2_end_auth_session(chip);
return err;
}
@@ -319,20 +312,18 @@ int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
*/
void tpm2_flush_context(struct tpm_chip *chip, u32 handle)
{
- struct tpm_buf buf;
- int rc;
-
- rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_FLUSH_CONTEXT);
- if (rc) {
+ struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+ if (!buf) {
dev_warn(&chip->dev, "0x%08x was not flushed, out of memory\n",
handle);
return;
}
- tpm_buf_append_u32(&buf, handle);
+ tpm_buf_init(buf, TPM_BUFSIZE);
+ tpm_buf_reset(buf, TPM2_ST_NO_SESSIONS, TPM2_CC_FLUSH_CONTEXT);
+ tpm_buf_append_u32(buf, handle);
- tpm_transmit_cmd(chip, &buf, 0, "flushing context");
- tpm_buf_destroy(&buf);
+ tpm_transmit_cmd(chip, buf, 0, "TPM2_FlushContext");
}
EXPORT_SYMBOL_GPL(tpm2_flush_context);
@@ -359,19 +350,20 @@ ssize_t tpm2_get_tpm_pt(struct tpm_chip *chip, u32 property_id, u32 *value,
const char *desc)
{
struct tpm2_get_cap_out *out;
- struct tpm_buf buf;
int rc;
- rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_GET_CAPABILITY);
- if (rc)
- return rc;
- tpm_buf_append_u32(&buf, TPM2_CAP_TPM_PROPERTIES);
- tpm_buf_append_u32(&buf, property_id);
- tpm_buf_append_u32(&buf, 1);
- rc = tpm_transmit_cmd(chip, &buf, 0, NULL);
+ struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ tpm_buf_init(buf, TPM_BUFSIZE);
+ tpm_buf_reset(buf, TPM2_ST_NO_SESSIONS, TPM2_CC_GET_CAPABILITY);
+ tpm_buf_append_u32(buf, TPM2_CAP_TPM_PROPERTIES);
+ tpm_buf_append_u32(buf, property_id);
+ tpm_buf_append_u32(buf, 1);
+ rc = tpm_transmit_cmd(chip, buf, 0, NULL);
if (!rc) {
- out = (struct tpm2_get_cap_out *)
- &buf.data[TPM_HEADER_SIZE];
+ out = (struct tpm2_get_cap_out *)&buf->data[TPM_HEADER_SIZE];
/*
* To prevent failing boot up of some systems, Infineon TPM2.0
* returns SUCCESS on TPM2_Startup in field upgrade mode. Also
@@ -383,7 +375,6 @@ ssize_t tpm2_get_tpm_pt(struct tpm_chip *chip, u32 property_id, u32 *value,
else
rc = -ENODATA;
}
- tpm_buf_destroy(&buf);
return rc;
}
EXPORT_SYMBOL_GPL(tpm2_get_tpm_pt);
@@ -400,15 +391,14 @@ EXPORT_SYMBOL_GPL(tpm2_get_tpm_pt);
*/
void tpm2_shutdown(struct tpm_chip *chip, u16 shutdown_type)
{
- struct tpm_buf buf;
- int rc;
-
- rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_SHUTDOWN);
- if (rc)
+ struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+ if (!buf)
return;
- tpm_buf_append_u16(&buf, shutdown_type);
- tpm_transmit_cmd(chip, &buf, 0, "stopping the TPM");
- tpm_buf_destroy(&buf);
+
+ tpm_buf_init(buf, TPM_BUFSIZE);
+ tpm_buf_reset(buf, TPM2_ST_NO_SESSIONS, TPM2_CC_SHUTDOWN);
+ tpm_buf_append_u16(buf, shutdown_type);
+ tpm_transmit_cmd(chip, buf, 0, "TPM2_Shutdown");
}
/**
@@ -426,20 +416,19 @@ void tpm2_shutdown(struct tpm_chip *chip, u16 shutdown_type)
*/
static int tpm2_do_selftest(struct tpm_chip *chip)
{
- struct tpm_buf buf;
int full;
int rc;
- for (full = 0; full < 2; full++) {
- rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_SELF_TEST);
- if (rc)
- return rc;
-
- tpm_buf_append_u8(&buf, full);
- rc = tpm_transmit_cmd(chip, &buf, 0,
- "attempting the self test");
- tpm_buf_destroy(&buf);
+ struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+ tpm_buf_init(buf, TPM_BUFSIZE);
+ tpm_buf_reset(buf, TPM2_ST_NO_SESSIONS, TPM2_CC_SELF_TEST);
+ for (full = 0; full < 2; full++) {
+ tpm_buf_reset(buf, TPM2_ST_NO_SESSIONS, TPM2_CC_SELF_TEST);
+ tpm_buf_append_u8(buf, full);
+ rc = tpm_transmit_cmd(chip, buf, 0, "TPM2_SelfTest");
if (rc == TPM2_RC_TESTING)
rc = TPM2_RC_SUCCESS;
if (rc == TPM2_RC_INITIALIZE || rc == TPM2_RC_SUCCESS)
@@ -464,23 +453,24 @@ static int tpm2_do_selftest(struct tpm_chip *chip)
int tpm2_probe(struct tpm_chip *chip)
{
struct tpm_header *out;
- struct tpm_buf buf;
int rc;
- rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_GET_CAPABILITY);
- if (rc)
- return rc;
- tpm_buf_append_u32(&buf, TPM2_CAP_TPM_PROPERTIES);
- tpm_buf_append_u32(&buf, TPM_PT_TOTAL_COMMANDS);
- tpm_buf_append_u32(&buf, 1);
- rc = tpm_transmit_cmd(chip, &buf, 0, NULL);
+ struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ tpm_buf_init(buf, TPM_BUFSIZE);
+ tpm_buf_reset(buf, TPM2_ST_NO_SESSIONS, TPM2_CC_GET_CAPABILITY);
+ tpm_buf_append_u32(buf, TPM2_CAP_TPM_PROPERTIES);
+ tpm_buf_append_u32(buf, TPM_PT_TOTAL_COMMANDS);
+ tpm_buf_append_u32(buf, 1);
+ rc = tpm_transmit_cmd(chip, buf, 0, NULL);
/* We ignore TPM return codes on purpose. */
if (rc >= 0) {
- out = (struct tpm_header *)buf.data;
+ out = (struct tpm_header *)buf->data;
if (be16_to_cpu(out->tag) == TPM2_ST_NO_SESSIONS)
chip->flags |= TPM_CHIP_FLAG_TPM2;
}
- tpm_buf_destroy(&buf);
return 0;
}
EXPORT_SYMBOL_GPL(tpm2_probe);
@@ -520,7 +510,6 @@ struct tpm2_pcr_selection {
ssize_t tpm2_get_pcr_allocation(struct tpm_chip *chip)
{
struct tpm2_pcr_selection pcr_selection;
- struct tpm_buf buf;
void *marker;
void *end;
void *pcr_select_offset;
@@ -532,39 +521,37 @@ ssize_t tpm2_get_pcr_allocation(struct tpm_chip *chip)
int rc;
int i = 0;
- rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_GET_CAPABILITY);
- if (rc)
- return rc;
+ struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
- tpm_buf_append_u32(&buf, TPM2_CAP_PCRS);
- tpm_buf_append_u32(&buf, 0);
- tpm_buf_append_u32(&buf, 1);
+ tpm_buf_init(buf, TPM_BUFSIZE);
+ tpm_buf_reset(buf, TPM2_ST_NO_SESSIONS, TPM2_CC_GET_CAPABILITY);
+ tpm_buf_append_u32(buf, TPM2_CAP_PCRS);
+ tpm_buf_append_u32(buf, 0);
+ tpm_buf_append_u32(buf, 1);
- rc = tpm_transmit_cmd(chip, &buf, 9, "get tpm pcr allocation");
+ rc = tpm_transmit_cmd(chip, buf, 9, "TPM2_GetCapability(PCRS)");
if (rc)
- goto out;
+ return rc;
- nr_possible_banks = be32_to_cpup(
- (__be32 *)&buf.data[TPM_HEADER_SIZE + 5]);
+ nr_possible_banks = be32_to_cpup((__be32 *)&buf->data[TPM_HEADER_SIZE + 5]);
if (nr_possible_banks > TPM2_MAX_PCR_BANKS) {
pr_err("tpm: unexpected number of banks: %u > %u",
nr_possible_banks, TPM2_MAX_PCR_BANKS);
- rc = -ENOMEM;
- goto out;
+ return -ENOMEM;
}
- marker = &buf.data[TPM_HEADER_SIZE + 9];
+ marker = &buf->data[TPM_HEADER_SIZE + 9];
- rsp_len = be32_to_cpup((__be32 *)&buf.data[2]);
- end = &buf.data[rsp_len];
+ rsp_len = be32_to_cpup((__be32 *)&buf->data[2]);
+ end = &buf->data[rsp_len];
for (i = 0; i < nr_possible_banks; i++) {
pcr_select_offset = marker +
offsetof(struct tpm2_pcr_selection, size_of_select);
- if (pcr_select_offset >= end) {
- rc = -EFAULT;
- break;
- }
+ if (pcr_select_offset >= end)
+ return -EFAULT;
memcpy(&pcr_selection, marker, sizeof(pcr_selection));
hash_alg = be16_to_cpu(pcr_selection.hash_alg);
@@ -576,7 +563,7 @@ ssize_t tpm2_get_pcr_allocation(struct tpm_chip *chip)
rc = tpm2_init_bank_info(chip, nr_alloc_banks);
if (rc < 0)
- break;
+ return rc;
nr_alloc_banks++;
}
@@ -588,21 +575,21 @@ ssize_t tpm2_get_pcr_allocation(struct tpm_chip *chip)
}
chip->nr_allocated_banks = nr_alloc_banks;
-out:
- tpm_buf_destroy(&buf);
-
- return rc;
+ return 0;
}
int tpm2_get_cc_attrs_tbl(struct tpm_chip *chip)
{
- struct tpm_buf buf;
u32 nr_commands;
__be32 *attrs;
u32 cc;
int i;
int rc;
+ struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
rc = tpm2_get_tpm_pt(chip, TPM_PT_TOTAL_COMMANDS, &nr_commands, NULL);
if (rc)
goto out;
@@ -619,30 +606,24 @@ int tpm2_get_cc_attrs_tbl(struct tpm_chip *chip)
goto out;
}
- rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_GET_CAPABILITY);
- if (rc)
- goto out;
-
- tpm_buf_append_u32(&buf, TPM2_CAP_COMMANDS);
- tpm_buf_append_u32(&buf, TPM2_CC_FIRST);
- tpm_buf_append_u32(&buf, nr_commands);
+ tpm_buf_init(buf, TPM_BUFSIZE);
+ tpm_buf_reset(buf, TPM2_ST_NO_SESSIONS, TPM2_CC_GET_CAPABILITY);
+ tpm_buf_append_u32(buf, TPM2_CAP_COMMANDS);
+ tpm_buf_append_u32(buf, TPM2_CC_FIRST);
+ tpm_buf_append_u32(buf, nr_commands);
- rc = tpm_transmit_cmd(chip, &buf, 9 + 4 * nr_commands, NULL);
- if (rc) {
- tpm_buf_destroy(&buf);
+ rc = tpm_transmit_cmd(chip, buf, 9 + 4 * nr_commands, NULL);
+ if (rc)
goto out;
- }
- if (nr_commands !=
- be32_to_cpup((__be32 *)&buf.data[TPM_HEADER_SIZE + 5])) {
+ if (nr_commands != be32_to_cpup((__be32 *)&buf->data[TPM_HEADER_SIZE + 5])) {
rc = -EFAULT;
- tpm_buf_destroy(&buf);
goto out;
}
chip->nr_commands = nr_commands;
- attrs = (__be32 *)&buf.data[TPM_HEADER_SIZE + 9];
+ attrs = (__be32 *)&buf->data[TPM_HEADER_SIZE + 9];
for (i = 0; i < nr_commands; i++, attrs++) {
chip->cc_attrs_tbl[i] = be32_to_cpup(attrs);
cc = chip->cc_attrs_tbl[i] & 0xFFFF;
@@ -654,8 +635,6 @@ int tpm2_get_cc_attrs_tbl(struct tpm_chip *chip)
}
}
- tpm_buf_destroy(&buf);
-
out:
if (rc > 0)
rc = -ENODEV;
@@ -676,20 +655,14 @@ EXPORT_SYMBOL_GPL(tpm2_get_cc_attrs_tbl);
static int tpm2_startup(struct tpm_chip *chip)
{
- struct tpm_buf buf;
- int rc;
-
- dev_info(&chip->dev, "starting up the TPM manually\n");
-
- rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_STARTUP);
- if (rc < 0)
- return rc;
-
- tpm_buf_append_u16(&buf, TPM2_SU_CLEAR);
- rc = tpm_transmit_cmd(chip, &buf, 0, "attempting to start the TPM");
- tpm_buf_destroy(&buf);
-
- return rc;
+ struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ tpm_buf_init(buf, TPM_BUFSIZE);
+ tpm_buf_reset(buf, TPM2_ST_NO_SESSIONS, TPM2_CC_STARTUP);
+ tpm_buf_append_u16(buf, TPM2_SU_CLEAR);
+ return tpm_transmit_cmd(chip, buf, 0, "TPM2_Startup");
}
/**
diff --git a/drivers/char/tpm/tpm2-sessions.c b/drivers/char/tpm/tpm2-sessions.c
index bbc05f0997a8..378b8d21b10c 100644
--- a/drivers/char/tpm/tpm2-sessions.c
+++ b/drivers/char/tpm/tpm2-sessions.c
@@ -182,19 +182,18 @@ static int tpm2_parse_read_public(char *name, struct tpm_buf *buf)
static int tpm2_read_public(struct tpm_chip *chip, u32 handle, char *name)
{
- struct tpm_buf buf;
int rc;
- rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_READ_PUBLIC);
- if (rc)
- return rc;
+ struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
- tpm_buf_append_u32(&buf, handle);
- rc = tpm_transmit_cmd(chip, &buf, 0, "read public");
+ tpm_buf_init(buf, TPM_BUFSIZE);
+ tpm_buf_reset(buf, TPM2_ST_NO_SESSIONS, TPM2_CC_READ_PUBLIC);
+ tpm_buf_append_u32(buf, handle);
+ rc = tpm_transmit_cmd(chip, buf, 0, "TPM2_ReadPublic");
if (rc == TPM2_RC_SUCCESS)
- rc = tpm2_parse_read_public(name, &buf);
-
- tpm_buf_destroy(&buf);
+ rc = tpm2_parse_read_public(name, buf);
return rc;
}
@@ -924,7 +923,6 @@ static int tpm2_load_null(struct tpm_chip *chip, u32 *null_key)
int tpm2_start_auth_session(struct tpm_chip *chip)
{
struct tpm2_auth *auth;
- struct tpm_buf buf;
u32 null_key;
int rc;
@@ -933,6 +931,10 @@ int tpm2_start_auth_session(struct tpm_chip *chip)
return 0;
}
+ struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
auth = kzalloc(sizeof(*auth), GFP_KERNEL);
if (!auth)
return -ENOMEM;
@@ -943,41 +945,37 @@ int tpm2_start_auth_session(struct tpm_chip *chip)
auth->session = TPM_HEADER_SIZE;
- rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_START_AUTH_SESS);
- if (rc)
- goto out;
-
+ tpm_buf_init(buf, TPM_BUFSIZE);
+ tpm_buf_reset(buf, TPM2_ST_NO_SESSIONS, TPM2_CC_START_AUTH_SESS);
/* salt key handle */
- tpm_buf_append_u32(&buf, null_key);
+ tpm_buf_append_u32(buf, null_key);
/* bind key handle */
- tpm_buf_append_u32(&buf, TPM2_RH_NULL);
+ tpm_buf_append_u32(buf, TPM2_RH_NULL);
/* nonce caller */
get_random_bytes(auth->our_nonce, sizeof(auth->our_nonce));
- tpm_buf_append_u16(&buf, sizeof(auth->our_nonce));
- tpm_buf_append(&buf, auth->our_nonce, sizeof(auth->our_nonce));
+ tpm_buf_append_u16(buf, sizeof(auth->our_nonce));
+ tpm_buf_append(buf, auth->our_nonce, sizeof(auth->our_nonce));
/* append encrypted salt and squirrel away unencrypted in auth */
- tpm_buf_append_salt(&buf, chip, auth);
+ tpm_buf_append_salt(buf, chip, auth);
/* session type (HMAC, audit or policy) */
- tpm_buf_append_u8(&buf, TPM2_SE_HMAC);
+ tpm_buf_append_u8(buf, TPM2_SE_HMAC);
/* symmetric encryption parameters */
/* symmetric algorithm */
- tpm_buf_append_u16(&buf, TPM_ALG_AES);
+ tpm_buf_append_u16(buf, TPM_ALG_AES);
/* bits for symmetric algorithm */
- tpm_buf_append_u16(&buf, AES_KEY_BITS);
+ tpm_buf_append_u16(buf, AES_KEY_BITS);
/* symmetric algorithm mode (must be CFB) */
- tpm_buf_append_u16(&buf, TPM_ALG_CFB);
+ tpm_buf_append_u16(buf, TPM_ALG_CFB);
/* hash algorithm for session */
- tpm_buf_append_u16(&buf, TPM_ALG_SHA256);
+ tpm_buf_append_u16(buf, TPM_ALG_SHA256);
- rc = tpm_ret_to_err(tpm_transmit_cmd(chip, &buf, 0, "StartAuthSession"));
+ rc = tpm_ret_to_err(tpm_transmit_cmd(chip, buf, 0, "TPM2_StartAuthSession"));
tpm2_flush_context(chip, null_key);
if (rc == TPM2_RC_SUCCESS)
- rc = tpm2_parse_start_auth_session(auth, &buf);
-
- tpm_buf_destroy(&buf);
+ rc = tpm2_parse_start_auth_session(auth, buf);
if (rc == TPM2_RC_SUCCESS) {
chip->auth = auth;
@@ -1199,18 +1197,18 @@ static int tpm2_create_primary(struct tpm_chip *chip, u32 hierarchy,
u32 *handle, u8 *name)
{
int rc;
- struct tpm_buf buf;
- struct tpm_buf template;
- rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_CREATE_PRIMARY);
- if (rc)
- return rc;
+ struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
- rc = tpm_buf_init_sized(&template);
- if (rc) {
- tpm_buf_destroy(&buf);
- return rc;
- }
+ struct tpm_buf *template __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+ if (!template)
+ return -ENOMEM;
+
+ tpm_buf_init(buf, TPM_BUFSIZE);
+ tpm_buf_reset(buf, TPM2_ST_SESSIONS, TPM2_CC_CREATE_PRIMARY);
+ tpm_buf_init_sized(template, TPM_BUFSIZE);
/*
* create the template. Note: in order for userspace to
@@ -1222,75 +1220,71 @@ static int tpm2_create_primary(struct tpm_chip *chip, u32 hierarchy,
*/
/* key type */
- tpm_buf_append_u16(&template, TPM_ALG_ECC);
+ tpm_buf_append_u16(template, TPM_ALG_ECC);
/* name algorithm */
- tpm_buf_append_u16(&template, TPM_ALG_SHA256);
+ tpm_buf_append_u16(template, TPM_ALG_SHA256);
/* object properties */
- tpm_buf_append_u32(&template, TPM2_OA_NULL_KEY);
+ tpm_buf_append_u32(template, TPM2_OA_NULL_KEY);
/* sauth policy (empty) */
- tpm_buf_append_u16(&template, 0);
+ tpm_buf_append_u16(template, 0);
/* BEGIN parameters: key specific; for ECC*/
/* symmetric algorithm */
- tpm_buf_append_u16(&template, TPM_ALG_AES);
+ tpm_buf_append_u16(template, TPM_ALG_AES);
/* bits for symmetric algorithm */
- tpm_buf_append_u16(&template, AES_KEY_BITS);
+ tpm_buf_append_u16(template, AES_KEY_BITS);
/* algorithm mode (must be CFB) */
- tpm_buf_append_u16(&template, TPM_ALG_CFB);
+ tpm_buf_append_u16(template, TPM_ALG_CFB);
/* scheme (NULL means any scheme) */
- tpm_buf_append_u16(&template, TPM_ALG_NULL);
+ tpm_buf_append_u16(template, TPM_ALG_NULL);
/* ECC Curve ID */
- tpm_buf_append_u16(&template, TPM2_ECC_NIST_P256);
+ tpm_buf_append_u16(template, TPM2_ECC_NIST_P256);
/* KDF Scheme */
- tpm_buf_append_u16(&template, TPM_ALG_NULL);
+ tpm_buf_append_u16(template, TPM_ALG_NULL);
/* unique: key specific; for ECC it is two zero size points */
- tpm_buf_append_u16(&template, 0);
- tpm_buf_append_u16(&template, 0);
+ tpm_buf_append_u16(template, 0);
+ tpm_buf_append_u16(template, 0);
/* END parameters */
/* primary handle */
- tpm_buf_append_u32(&buf, hierarchy);
- tpm_buf_append_empty_auth(&buf, TPM2_RS_PW);
+ tpm_buf_append_u32(buf, hierarchy);
+ tpm_buf_append_empty_auth(buf, TPM2_RS_PW);
/* sensitive create size is 4 for two empty buffers */
- tpm_buf_append_u16(&buf, 4);
+ tpm_buf_append_u16(buf, 4);
/* sensitive create auth data (empty) */
- tpm_buf_append_u16(&buf, 0);
+ tpm_buf_append_u16(buf, 0);
/* sensitive create sensitive data (empty) */
- tpm_buf_append_u16(&buf, 0);
+ tpm_buf_append_u16(buf, 0);
/* the public template */
- tpm_buf_append(&buf, template.data, template.length);
- tpm_buf_destroy(&template);
+ tpm_buf_append(buf, template->data, template->length);
/* outside info (empty) */
- tpm_buf_append_u16(&buf, 0);
+ tpm_buf_append_u16(buf, 0);
/* creation PCR (none) */
- tpm_buf_append_u32(&buf, 0);
+ tpm_buf_append_u32(buf, 0);
- rc = tpm_transmit_cmd(chip, &buf, 0,
- "attempting to create NULL primary");
+ rc = tpm_transmit_cmd(chip, buf, 0, "TPM2_CreatePrimary");
if (rc == TPM2_RC_SUCCESS)
- rc = tpm2_parse_create_primary(chip, &buf, handle, hierarchy,
+ rc = tpm2_parse_create_primary(chip, buf, handle, hierarchy,
name);
- tpm_buf_destroy(&buf);
-
return rc;
}
diff --git a/drivers/char/tpm/tpm2-space.c b/drivers/char/tpm/tpm2-space.c
index 0ad5e18355e0..4229c71940cb 100644
--- a/drivers/char/tpm/tpm2-space.c
+++ b/drivers/char/tpm/tpm2-space.c
@@ -70,24 +70,25 @@ void tpm2_del_space(struct tpm_chip *chip, struct tpm_space *space)
int tpm2_load_context(struct tpm_chip *chip, u8 *buf,
unsigned int *offset, u32 *handle)
{
- struct tpm_buf tbuf;
struct tpm2_context *ctx;
unsigned int body_size;
int rc;
- rc = tpm_buf_init(&tbuf, TPM2_ST_NO_SESSIONS, TPM2_CC_CONTEXT_LOAD);
- if (rc)
- return rc;
+ struct tpm_buf *tbuf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+ if (!tbuf)
+ return -ENOMEM;
+
+ tpm_buf_init(tbuf, TPM_BUFSIZE);
+ tpm_buf_reset(tbuf, TPM2_ST_NO_SESSIONS, TPM2_CC_CONTEXT_LOAD);
ctx = (struct tpm2_context *)&buf[*offset];
body_size = sizeof(*ctx) + be16_to_cpu(ctx->blob_size);
- tpm_buf_append(&tbuf, &buf[*offset], body_size);
+ tpm_buf_append(tbuf, &buf[*offset], body_size);
- rc = tpm_transmit_cmd(chip, &tbuf, 4, NULL);
+ rc = tpm_transmit_cmd(chip, tbuf, 4, NULL);
if (rc < 0) {
dev_warn(&chip->dev, "%s: failed with a system error %d\n",
__func__, rc);
- tpm_buf_destroy(&tbuf);
return -EFAULT;
} else if (tpm2_rc_value(rc) == TPM2_RC_HANDLE ||
rc == TPM2_RC_REFERENCE_H0) {
@@ -102,64 +103,55 @@ int tpm2_load_context(struct tpm_chip *chip, u8 *buf,
* flushed outside the space
*/
*handle = 0;
- tpm_buf_destroy(&tbuf);
return -ENOENT;
} else if (tpm2_rc_value(rc) == TPM2_RC_INTEGRITY) {
- tpm_buf_destroy(&tbuf);
return -EINVAL;
} else if (rc > 0) {
dev_warn(&chip->dev, "%s: failed with a TPM error 0x%04X\n",
__func__, rc);
- tpm_buf_destroy(&tbuf);
return -EFAULT;
}
- *handle = be32_to_cpup((__be32 *)&tbuf.data[TPM_HEADER_SIZE]);
+ *handle = be32_to_cpup((__be32 *)&tbuf->data[TPM_HEADER_SIZE]);
*offset += body_size;
-
- tpm_buf_destroy(&tbuf);
return 0;
}
int tpm2_save_context(struct tpm_chip *chip, u32 handle, u8 *buf,
unsigned int buf_size, unsigned int *offset)
{
- struct tpm_buf tbuf;
unsigned int body_size;
int rc;
- rc = tpm_buf_init(&tbuf, TPM2_ST_NO_SESSIONS, TPM2_CC_CONTEXT_SAVE);
- if (rc)
- return rc;
+ struct tpm_buf *tbuf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+ if (!tbuf)
+ return -ENOMEM;
- tpm_buf_append_u32(&tbuf, handle);
+ tpm_buf_init(tbuf, TPM_BUFSIZE);
+ tpm_buf_reset(tbuf, TPM2_ST_NO_SESSIONS, TPM2_CC_CONTEXT_SAVE);
+ tpm_buf_append_u32(tbuf, handle);
- rc = tpm_transmit_cmd(chip, &tbuf, 0, NULL);
+ rc = tpm_transmit_cmd(chip, tbuf, 0, NULL);
if (rc < 0) {
dev_warn(&chip->dev, "%s: failed with a system error %d\n",
__func__, rc);
- tpm_buf_destroy(&tbuf);
return -EFAULT;
} else if (tpm2_rc_value(rc) == TPM2_RC_REFERENCE_H0) {
- tpm_buf_destroy(&tbuf);
return -ENOENT;
} else if (rc) {
dev_warn(&chip->dev, "%s: failed with a TPM error 0x%04X\n",
__func__, rc);
- tpm_buf_destroy(&tbuf);
return -EFAULT;
}
- body_size = tpm_buf_length(&tbuf) - TPM_HEADER_SIZE;
+ body_size = tpm_buf_length(tbuf) - TPM_HEADER_SIZE;
if ((*offset + body_size) > buf_size) {
dev_warn(&chip->dev, "%s: out of backing storage\n", __func__);
- tpm_buf_destroy(&tbuf);
return -ENOMEM;
}
- memcpy(&buf[*offset], &tbuf.data[TPM_HEADER_SIZE], body_size);
+ memcpy(&buf[*offset], &tbuf->data[TPM_HEADER_SIZE], body_size);
*offset += body_size;
- tpm_buf_destroy(&tbuf);
return 0;
}
diff --git a/drivers/char/tpm/tpm_vtpm_proxy.c b/drivers/char/tpm/tpm_vtpm_proxy.c
index 0818bb517805..682dfc93845d 100644
--- a/drivers/char/tpm/tpm_vtpm_proxy.c
+++ b/drivers/char/tpm/tpm_vtpm_proxy.c
@@ -395,40 +395,36 @@ static bool vtpm_proxy_tpm_req_canceled(struct tpm_chip *chip, u8 status)
static int vtpm_proxy_request_locality(struct tpm_chip *chip, int locality)
{
- struct tpm_buf buf;
int rc;
const struct tpm_header *header;
struct proxy_dev *proxy_dev = dev_get_drvdata(&chip->dev);
+ struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ tpm_buf_init(buf, TPM_BUFSIZE);
if (chip->flags & TPM_CHIP_FLAG_TPM2)
- rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS,
- TPM2_CC_SET_LOCALITY);
+ tpm_buf_reset(buf, TPM2_ST_SESSIONS, TPM2_CC_SET_LOCALITY);
else
- rc = tpm_buf_init(&buf, TPM_TAG_RQU_COMMAND,
- TPM_ORD_SET_LOCALITY);
- if (rc)
- return rc;
- tpm_buf_append_u8(&buf, locality);
+ tpm_buf_reset(buf, TPM_TAG_RQU_COMMAND, TPM_ORD_SET_LOCALITY);
+
+ tpm_buf_append_u8(buf, locality);
proxy_dev->state |= STATE_DRIVER_COMMAND;
- rc = tpm_transmit_cmd(chip, &buf, 0, "attempting to set locality");
+ rc = tpm_transmit_cmd(chip, buf, 0, "attempting to set locality");
proxy_dev->state &= ~STATE_DRIVER_COMMAND;
- if (rc < 0) {
- locality = rc;
- goto out;
- }
+ if (rc < 0)
+ return rc;
- header = (const struct tpm_header *)buf.data;
+ header = (const struct tpm_header *)buf->data;
rc = be32_to_cpu(header->return_code);
if (rc)
locality = -1;
-out:
- tpm_buf_destroy(&buf);
-
return locality;
}
diff --git a/include/linux/tpm.h b/include/linux/tpm.h
index e86c22b30153..2ea181a35786 100644
--- a/include/linux/tpm.h
+++ b/include/linux/tpm.h
@@ -26,7 +26,8 @@
#include <crypto/hash_info.h>
#include <crypto/aes.h>
-#define TPM_DIGEST_SIZE 20 /* Max TPM v1.2 PCR size */
+#define TPM_DIGEST_SIZE 20 /* Max TPM v1.2 PCR size */
+#define TPM_BUFSIZE 4096
#define TPM2_MAX_DIGEST_SIZE SHA512_DIGEST_SIZE
#define TPM2_MAX_PCR_BANKS 8
@@ -377,13 +378,15 @@ enum tpm_buf_flags {
};
/*
- * A string buffer type for constructing TPM commands.
+ * A buffer for constructing and parsing TPM commands, responses and sized
+ * (TPM2B) buffers.
*/
struct tpm_buf {
- u32 flags;
- u32 length;
- u8 *data;
+ u8 flags;
u8 handles;
+ u16 length;
+ u16 capacity;
+ u8 data[];
};
enum tpm2_object_attributes {
@@ -414,11 +417,10 @@ struct tpm2_hash {
unsigned int tpm_id;
};
-int tpm_buf_init(struct tpm_buf *buf, u16 tag, u32 ordinal);
+void tpm_buf_init(struct tpm_buf *buf, u16 buf_size);
+void tpm_buf_init_sized(struct tpm_buf *buf, u16 buf_size);
void tpm_buf_reset(struct tpm_buf *buf, u16 tag, u32 ordinal);
-int tpm_buf_init_sized(struct tpm_buf *buf);
void tpm_buf_reset_sized(struct tpm_buf *buf);
-void tpm_buf_destroy(struct tpm_buf *buf);
u32 tpm_buf_length(struct tpm_buf *buf);
void tpm_buf_append(struct tpm_buf *buf, const u8 *new_data, u16 new_length);
void tpm_buf_append_u8(struct tpm_buf *buf, const u8 value);
diff --git a/security/keys/trusted-keys/trusted_tpm1.c b/security/keys/trusted-keys/trusted_tpm1.c
index 636acb66a4f6..3ac204a902de 100644
--- a/security/keys/trusted-keys/trusted_tpm1.c
+++ b/security/keys/trusted-keys/trusted_tpm1.c
@@ -310,9 +310,8 @@ static int TSS_checkhmac2(unsigned char *buffer,
* For key specific tpm requests, we will generate and send our
* own TPM command packets using the drivers send function.
*/
-static int trusted_tpm_send(unsigned char *cmd, size_t buflen)
+static int trusted_tpm_send(void *cmd, size_t buflen)
{
- struct tpm_buf buf;
int rc;
if (!chip)
@@ -322,15 +321,12 @@ static int trusted_tpm_send(unsigned char *cmd, size_t buflen)
if (rc)
return rc;
- buf.flags = 0;
- buf.length = buflen;
- buf.data = cmd;
dump_tpm_buf(cmd);
- rc = tpm_transmit_cmd(chip, &buf, 4, "sending data");
+ rc = tpm_transmit_cmd(chip, cmd, 4, "sending data");
dump_tpm_buf(cmd);
+ /* Convert TPM error to -EPERM. */
if (rc > 0)
- /* TPM error */
rc = -EPERM;
tpm_put_ops(chip);
@@ -624,23 +620,23 @@ static int tpm_unseal(struct tpm_buf *tb,
static int key_seal(struct trusted_key_payload *p,
struct trusted_key_options *o)
{
- struct tpm_buf tb;
int ret;
- ret = tpm_buf_init(&tb, 0, 0);
- if (ret)
- return ret;
+ struct tpm_buf *tb __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+ if (!tb)
+ return -ENOMEM;
+
+ tpm_buf_init(tb, TPM_BUFSIZE);
/* include migratable flag at end of sealed key */
p->key[p->key_len] = p->migratable;
- ret = tpm_seal(&tb, o->keytype, o->keyhandle, o->keyauth,
+ ret = tpm_seal(tb, o->keytype, o->keyhandle, o->keyauth,
p->key, p->key_len + 1, p->blob, &p->blob_len,
o->blobauth, o->pcrinfo, o->pcrinfo_len);
if (ret < 0)
pr_info("srkseal failed (%d)\n", ret);
- tpm_buf_destroy(&tb);
return ret;
}
@@ -650,14 +646,15 @@ static int key_seal(struct trusted_key_payload *p,
static int key_unseal(struct trusted_key_payload *p,
struct trusted_key_options *o)
{
- struct tpm_buf tb;
int ret;
- ret = tpm_buf_init(&tb, 0, 0);
- if (ret)
- return ret;
+ struct tpm_buf *tb __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+ if (!tb)
+ return -ENOMEM;
+
+ tpm_buf_init(tb, TPM_BUFSIZE);
- ret = tpm_unseal(&tb, o->keyhandle, o->keyauth, p->blob, p->blob_len,
+ ret = tpm_unseal(tb, o->keyhandle, o->keyauth, p->blob, p->blob_len,
o->blobauth, p->key, &p->key_len);
if (ret < 0)
pr_info("srkunseal failed (%d)\n", ret);
@@ -665,7 +662,6 @@ static int key_unseal(struct trusted_key_payload *p,
/* pull migratable flag out of sealed key */
p->migratable = p->key[--p->key_len];
- tpm_buf_destroy(&tb);
return ret;
}
diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
index 119d5152c0db..71a3674b495a 100644
--- a/security/keys/trusted-keys/trusted_tpm2.c
+++ b/security/keys/trusted-keys/trusted_tpm2.c
@@ -212,13 +212,20 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
struct trusted_key_options *options)
{
off_t offset = TPM_HEADER_SIZE;
- struct tpm_buf buf, sized;
int blob_len = 0;
u32 hash;
u32 flags;
int i;
int rc;
+ struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ struct tpm_buf *sized __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+ if (!sized)
+ return -ENOMEM;
+
for (i = 0; i < ARRAY_SIZE(tpm2_hash_map); i++) {
if (options->hash == tpm2_hash_map[i].crypto_id) {
hash = tpm2_hash_map[i].tpm_id;
@@ -240,91 +247,79 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
if (rc)
goto out_put;
- rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_CREATE);
- if (rc) {
- tpm2_end_auth_session(chip);
- goto out_put;
- }
-
- rc = tpm_buf_init_sized(&sized);
- if (rc) {
- tpm_buf_destroy(&buf);
- tpm2_end_auth_session(chip);
- goto out_put;
- }
-
- tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
- tpm_buf_append_hmac_session(chip, &buf, TPM2_SA_DECRYPT,
+ tpm_buf_init(buf, TPM_BUFSIZE);
+ tpm_buf_init_sized(sized, TPM_BUFSIZE);
+ tpm_buf_reset(buf, TPM2_ST_SESSIONS, TPM2_CC_CREATE);
+ tpm_buf_append_name(chip, buf, options->keyhandle, NULL);
+ tpm_buf_append_hmac_session(chip, buf, TPM2_SA_DECRYPT,
options->keyauth, TPM_DIGEST_SIZE);
/* sensitive */
- tpm_buf_append_u16(&sized, options->blobauth_len);
+ tpm_buf_append_u16(sized, options->blobauth_len);
if (options->blobauth_len)
- tpm_buf_append(&sized, options->blobauth, options->blobauth_len);
+ tpm_buf_append(sized, options->blobauth, options->blobauth_len);
- tpm_buf_append_u16(&sized, payload->key_len);
- tpm_buf_append(&sized, payload->key, payload->key_len);
- tpm_buf_append(&buf, sized.data, sized.length);
+ tpm_buf_append_u16(sized, payload->key_len);
+ tpm_buf_append(sized, payload->key, payload->key_len);
+ tpm_buf_append(buf, sized->data, sized->length);
/* public */
- tpm_buf_reset_sized(&sized);
- tpm_buf_append_u16(&sized, TPM_ALG_KEYEDHASH);
- tpm_buf_append_u16(&sized, hash);
+ tpm_buf_init_sized(sized, TPM_BUFSIZE);
+ tpm_buf_append_u16(sized, TPM_ALG_KEYEDHASH);
+ tpm_buf_append_u16(sized, hash);
/* key properties */
flags = 0;
flags |= options->policydigest_len ? 0 : TPM2_OA_USER_WITH_AUTH;
flags |= payload->migratable ? 0 : (TPM2_OA_FIXED_TPM | TPM2_OA_FIXED_PARENT);
- tpm_buf_append_u32(&sized, flags);
+ tpm_buf_append_u32(sized, flags);
/* policy */
- tpm_buf_append_u16(&sized, options->policydigest_len);
+ tpm_buf_append_u16(sized, options->policydigest_len);
if (options->policydigest_len)
- tpm_buf_append(&sized, options->policydigest, options->policydigest_len);
+ tpm_buf_append(sized, options->policydigest, options->policydigest_len);
/* public parameters */
- tpm_buf_append_u16(&sized, TPM_ALG_NULL);
- tpm_buf_append_u16(&sized, 0);
+ tpm_buf_append_u16(sized, TPM_ALG_NULL);
+ tpm_buf_append_u16(sized, 0);
- tpm_buf_append(&buf, sized.data, sized.length);
+ tpm_buf_append(buf, sized->data, sized->length);
/* outside info */
- tpm_buf_append_u16(&buf, 0);
+ tpm_buf_append_u16(buf, 0);
/* creation PCR */
- tpm_buf_append_u32(&buf, 0);
+ tpm_buf_append_u32(buf, 0);
- if (buf.flags & TPM_BUF_INVALID) {
+ if (buf->flags & TPM_BUF_INVALID) {
rc = -E2BIG;
tpm2_end_auth_session(chip);
goto out;
}
- tpm_buf_fill_hmac_session(chip, &buf);
- rc = tpm_transmit_cmd(chip, &buf, 4, "sealing data");
- rc = tpm_buf_check_hmac_response(chip, &buf, rc);
+ tpm_buf_fill_hmac_session(chip, buf);
+ rc = tpm_transmit_cmd(chip, buf, 4, "sealing data");
+ rc = tpm_buf_check_hmac_response(chip, buf, rc);
if (rc)
goto out;
- blob_len = tpm_buf_read_u32(&buf, &offset);
- if (blob_len > MAX_BLOB_SIZE || buf.flags & TPM_BUF_INVALID) {
+ blob_len = tpm_buf_read_u32(buf, &offset);
+ if (blob_len > MAX_BLOB_SIZE || buf->flags & TPM_BUF_INVALID) {
rc = -E2BIG;
goto out;
}
- if (buf.length - offset < blob_len) {
+ if (buf->length - offset < blob_len) {
rc = -EFAULT;
goto out;
}
- blob_len = tpm2_key_encode(payload, options, &buf.data[offset], blob_len);
+ blob_len = tpm2_key_encode(payload, options, &buf->data[offset],
+ blob_len);
if (blob_len < 0)
rc = blob_len;
out:
- tpm_buf_destroy(&sized);
- tpm_buf_destroy(&buf);
-
if (!rc)
payload->blob_len = blob_len;
@@ -351,7 +346,6 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
struct trusted_key_options *options,
u32 *blob_handle)
{
- struct tpm_buf buf;
unsigned int private_len;
unsigned int public_len;
unsigned int blob_len;
@@ -359,6 +353,10 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
int rc;
u32 attrs;
+ struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
rc = tpm2_key_decode(payload, options, &blob);
if (rc) {
/* old form */
@@ -402,35 +400,30 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
if (rc)
return rc;
- rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_LOAD);
- if (rc) {
- tpm2_end_auth_session(chip);
- return rc;
- }
-
- tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
- tpm_buf_append_hmac_session(chip, &buf, 0, options->keyauth,
+ tpm_buf_init(buf, TPM_BUFSIZE);
+ tpm_buf_reset(buf, TPM2_ST_SESSIONS, TPM2_CC_LOAD);
+ tpm_buf_append_name(chip, buf, options->keyhandle, NULL);
+ tpm_buf_append_hmac_session(chip, buf, 0, options->keyauth,
TPM_DIGEST_SIZE);
- tpm_buf_append(&buf, blob, blob_len);
+ tpm_buf_append(buf, blob, blob_len);
- if (buf.flags & TPM_BUF_INVALID) {
+ if (buf->flags & TPM_BUF_INVALID) {
rc = -E2BIG;
tpm2_end_auth_session(chip);
goto out;
}
- tpm_buf_fill_hmac_session(chip, &buf);
- rc = tpm_transmit_cmd(chip, &buf, 4, "loading blob");
- rc = tpm_buf_check_hmac_response(chip, &buf, rc);
+ tpm_buf_fill_hmac_session(chip, buf);
+ rc = tpm_transmit_cmd(chip, buf, 4, "loading blob");
+ rc = tpm_buf_check_hmac_response(chip, buf, rc);
if (!rc)
*blob_handle = be32_to_cpup(
- (__be32 *) &buf.data[TPM_HEADER_SIZE]);
+ (__be32 *)&buf->data[TPM_HEADER_SIZE]);
out:
if (blob != payload->blob)
kfree(blob);
- tpm_buf_destroy(&buf);
return tpm_ret_to_err(rc);
}
@@ -453,26 +446,25 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
u32 blob_handle)
{
struct tpm_header *head;
- struct tpm_buf buf;
u16 data_len;
int offset;
u8 *data;
int rc;
+ struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
rc = tpm2_start_auth_session(chip);
if (rc)
return rc;
- rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_UNSEAL);
- if (rc) {
- tpm2_end_auth_session(chip);
- return rc;
- }
-
- tpm_buf_append_name(chip, &buf, blob_handle, NULL);
+ tpm_buf_init(buf, TPM_BUFSIZE);
+ tpm_buf_reset(buf, TPM2_ST_SESSIONS, TPM2_CC_UNSEAL);
+ tpm_buf_append_name(chip, buf, blob_handle, NULL);
if (!options->policyhandle) {
- tpm_buf_append_hmac_session(chip, &buf, TPM2_SA_ENCRYPT,
+ tpm_buf_append_hmac_session(chip, buf, TPM2_SA_ENCRYPT,
options->blobauth,
options->blobauth_len);
} else {
@@ -481,39 +473,35 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
* the password will end up being unencrypted on the bus, as
* HMAC nonce cannot be calculated for it.
*/
- tpm_buf_append_u32(&buf, 9 + options->blobauth_len);
- tpm_buf_append_u32(&buf, options->policyhandle);
- tpm_buf_append_u16(&buf, 0);
- tpm_buf_append_u8(&buf, 0);
- tpm_buf_append_u16(&buf, options->blobauth_len);
- tpm_buf_append(&buf, options->blobauth, options->blobauth_len);
+ tpm_buf_append_u32(buf, 9 + options->blobauth_len);
+ tpm_buf_append_u32(buf, options->policyhandle);
+ tpm_buf_append_u16(buf, 0);
+ tpm_buf_append_u8(buf, 0);
+ tpm_buf_append_u16(buf, options->blobauth_len);
+ tpm_buf_append(buf, options->blobauth, options->blobauth_len);
if (tpm2_chip_auth(chip)) {
- tpm_buf_append_hmac_session(chip, &buf, TPM2_SA_ENCRYPT, NULL, 0);
+ tpm_buf_append_hmac_session(chip, buf, TPM2_SA_ENCRYPT, NULL, 0);
} else {
- offset = buf.handles * 4 + TPM_HEADER_SIZE;
- head = (struct tpm_header *)buf.data;
- if (tpm_buf_length(&buf) == offset)
+ offset = buf->handles * 4 + TPM_HEADER_SIZE;
+ head = (struct tpm_header *)buf->data;
+ if (tpm_buf_length(buf) == offset)
head->tag = cpu_to_be16(TPM2_ST_NO_SESSIONS);
}
}
- tpm_buf_fill_hmac_session(chip, &buf);
- rc = tpm_transmit_cmd(chip, &buf, 6, "unsealing");
- rc = tpm_buf_check_hmac_response(chip, &buf, rc);
+ tpm_buf_fill_hmac_session(chip, buf);
+ rc = tpm_transmit_cmd(chip, buf, 6, "unsealing");
+ rc = tpm_buf_check_hmac_response(chip, buf, rc);
if (!rc) {
data_len = be16_to_cpup(
- (__be16 *) &buf.data[TPM_HEADER_SIZE + 4]);
- if (data_len < MIN_KEY_SIZE || data_len > MAX_KEY_SIZE) {
- rc = -EFAULT;
- goto out;
- }
+ (__be16 *)&buf->data[TPM_HEADER_SIZE + 4]);
+ if (data_len < MIN_KEY_SIZE || data_len > MAX_KEY_SIZE)
+ return -EFAULT;
- if (tpm_buf_length(&buf) < TPM_HEADER_SIZE + 6 + data_len) {
- rc = -EFAULT;
- goto out;
- }
- data = &buf.data[TPM_HEADER_SIZE + 6];
+ if (tpm_buf_length(buf) < TPM_HEADER_SIZE + 6 + data_len)
+ return -EFAULT;
+ data = &buf->data[TPM_HEADER_SIZE + 6];
if (payload->old_format) {
/* migratable flag is at the end of the key */
@@ -530,8 +518,6 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
}
}
-out:
- tpm_buf_destroy(&buf);
return tpm_ret_to_err(rc);
}
--
2.39.5
^ permalink raw reply related
* Re: [PATCH v3 01/10] tpm: Cap the number of PCR banks
From: Jarkko Sakkinen @ 2025-10-01 11:16 UTC (permalink / raw)
To: James Bottomley
Cc: Jonathan McDowell, linux-integrity, dpsmith, ross.philipson,
Jarkko Sakkinen, Roberto Sassu, Peter Huewe, Jason Gunthorpe,
David Howells, Paul Moore, James Morris, Serge E. Hallyn,
open list, open list:KEYS/KEYRINGS, open list:SECURITY SUBSYSTEM
In-Reply-To: <cf3fb265dd70a23d598fc3d68562b4be5355e7ae.camel@HansenPartnership.com>
On Tue, Sep 30, 2025 at 10:17:22AM -0400, James Bottomley wrote:
> On Tue, 2025-09-30 at 15:36 +0300, Jarkko Sakkinen wrote:
> > On Tue, Sep 30, 2025 at 12:09:15PM +0100, Jonathan McDowell wrote:
> > > On Mon, Sep 29, 2025 at 10:48:23PM +0300, Jarkko Sakkinen wrote:
> [...]
> > > > +#define TPM2_MAX_DIGEST_SIZE SHA512_DIGEST_SIZE
> > > > +#define TPM2_MAX_BANKS 4
> > >
> > > Where does this max come from? It matches what I see with swtpm by
> > > default (SHA1, SHA2-256, SHA2-384, SHA-512), so I haven't seen
> > > anything that exceeds it myself.
> >
> > I've never seen hardware TPM that would have more than one or two
> > banks. We can double it to leave some room. This was tested with
> > swtpm defaults.
>
> I've got a hardware TPM that comes with 3 banks by default (it's a
> chinese one which has sha1 sha256 and sm2). swtpm isn't a good
> indicator because it's default allocation is rather pejorative (it
> disables sha1 whereas most field TPMs don't).
>
> However, if you look at how the reference implementation works, the
> user is allowed to define any number of banks they want, up to the
> number of supported hashes. The only limitation being there can't be
> >1 bank for the same hash. Field TPM implementations are allowed to
> constrain this, but most don't. The question you should be asking
> here is not how many banks does a particular implementation allow by
> default, but what's the maximum number a user could configure.
It needs some compilation time cap as the value comes from external
device. If someone hits to that value, then it needs to be increased
but as unconstrained it's a bug.
> Regards,
>
> James
>
BR, Jarkko
^ permalink raw reply
* [PATCH v2 1/2] landlock: Multithreading support for landlock_restrict_self()
From: Günther Noack @ 2025-10-01 11:18 UTC (permalink / raw)
To: Mickaël Salaün
Cc: Günther Noack, Konstantin Meskhidze, Tingmao Wang,
Paul Moore, linux-security-module, Jann Horn
In-Reply-To: <20251001111807.18902-1-gnoack@google.com>
Introduce the LANDLOCK_RESTRICT_SELF_TSYNC flag. With this flag, a
given Landlock ruleset is applied to all threads of the calling
process, instead of only the current one.
Without this flag, multithreaded userspace programs currently resort
to using the nptl(7)/libpsx hack for multithreaded policy enforcement,
which is also used by libcap and for setuid(2). Using this scheme,
the threads of a process enforce the same Landlock ruleset, but the
resulting Landlock domains are still separate, which makes a
difference for Landlock's "scoped" access rights, where the domain
identity and nesting is used. As a result, when using
LANLDOCK_SCOPE_SIGNAL, signaling between sibling threads stops
working. This is a problem for programming languages and frameworks
which are inherently multithreaded (e.g. Go).
Cc: Mickaël Salaün <mic@digikod.net>
Cc: Paul Moore <paul@paul-moore.com>
Cc: linux-security-module@vger.kernel.org
Suggested-by: Jann Horn <jannh@google.com>
Signed-off-by: Günther Noack <gnoack@google.com>
---
include/uapi/linux/landlock.h | 4 +
security/landlock/cred.h | 12 +
security/landlock/limits.h | 2 +-
security/landlock/syscalls.c | 433 +++++++++++++++++++++++++++++++++-
4 files changed, 448 insertions(+), 3 deletions(-)
diff --git a/include/uapi/linux/landlock.h b/include/uapi/linux/landlock.h
index f030adc462ee..7c6c7f004a41 100644
--- a/include/uapi/linux/landlock.h
+++ b/include/uapi/linux/landlock.h
@@ -117,11 +117,15 @@ struct landlock_ruleset_attr {
* future nested domains, not the one being created. It can also be used
* with a @ruleset_fd value of -1 to mute subdomain logs without creating a
* domain.
+ *
+ * %LANDLOCK_RESTRICT_SELF_TSYNC
+ * Apply the given ruleset atomically to all threads of the current process.
*/
/* clang-format off */
#define LANDLOCK_RESTRICT_SELF_LOG_SAME_EXEC_OFF (1U << 0)
#define LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON (1U << 1)
#define LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF (1U << 2)
+#define LANDLOCK_RESTRICT_SELF_TSYNC (1U << 3)
/* clang-format on */
/**
diff --git a/security/landlock/cred.h b/security/landlock/cred.h
index c82fe63ec598..eb28eeade760 100644
--- a/security/landlock/cred.h
+++ b/security/landlock/cred.h
@@ -65,6 +65,18 @@ landlock_cred(const struct cred *cred)
return cred->security + landlock_blob_sizes.lbs_cred;
}
+static inline void landlock_cred_copy(struct landlock_cred_security *dst,
+ const struct landlock_cred_security *src)
+{
+ if (dst->domain)
+ landlock_put_ruleset(dst->domain);
+
+ *dst = *src;
+
+ if (dst->domain)
+ landlock_get_ruleset(src->domain);
+}
+
static inline struct landlock_ruleset *landlock_get_current_domain(void)
{
return landlock_cred(current_cred())->domain;
diff --git a/security/landlock/limits.h b/security/landlock/limits.h
index 65b5ff051674..eb584f47288d 100644
--- a/security/landlock/limits.h
+++ b/security/landlock/limits.h
@@ -31,7 +31,7 @@
#define LANDLOCK_MASK_SCOPE ((LANDLOCK_LAST_SCOPE << 1) - 1)
#define LANDLOCK_NUM_SCOPE __const_hweight64(LANDLOCK_MASK_SCOPE)
-#define LANDLOCK_LAST_RESTRICT_SELF LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF
+#define LANDLOCK_LAST_RESTRICT_SELF LANDLOCK_RESTRICT_SELF_TSYNC
#define LANDLOCK_MASK_RESTRICT_SELF ((LANDLOCK_LAST_RESTRICT_SELF << 1) - 1)
/* clang-format on */
diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c
index 0116e9f93ffe..5ba14d641c11 100644
--- a/security/landlock/syscalls.c
+++ b/security/landlock/syscalls.c
@@ -14,6 +14,7 @@
#include <linux/capability.h>
#include <linux/cleanup.h>
#include <linux/compiler_types.h>
+#include <linux/completion.h>
#include <linux/dcache.h>
#include <linux/err.h>
#include <linux/errno.h>
@@ -25,6 +26,7 @@
#include <linux/security.h>
#include <linux/stddef.h>
#include <linux/syscalls.h>
+#include <linux/task_work.h>
#include <linux/types.h>
#include <linux/uaccess.h>
#include <uapi/linux/landlock.h>
@@ -445,6 +447,416 @@ SYSCALL_DEFINE4(landlock_add_rule, const int, ruleset_fd,
/* Enforcement */
+/*
+ * Shared state between multiple threads which are enforcing Landlock rulesets
+ * in lockstep with each other.
+ */
+struct tsync_shared_context {
+ /* The old and tentative new creds of the calling thread. */
+ const struct cred *old_cred;
+ const struct cred *new_cred;
+
+ /* An error encountered in preparation step, or 0. */
+ atomic_t preparation_error;
+
+ /*
+ * Barrier after preparation step in the inner loop.
+ * The calling thread waits for completion.
+ *
+ * Re-initialized on every round of looking for newly spawned threads.
+ */
+ atomic_t num_preparing;
+ struct completion all_prepared;
+
+ /* Sibling threads wait for completion. */
+ struct completion ready_to_commit;
+
+ /*
+ * Barrier after commit step (used by syscall impl to wait for
+ * completion).
+ */
+ atomic_t num_unfinished;
+ struct completion all_finished;
+};
+
+struct tsync_work {
+ struct callback_head work;
+ struct task_struct *task;
+ struct tsync_shared_context *shared_ctx;
+};
+
+/*
+ * restrict_one_thread - update a thread's Landlock domain in lockstep with the
+ * other threads in the same process
+ *
+ * When this is run, the same function gets run in all other threads in the same
+ * process (except for the calling thread which called landlock_restrict_self).
+ * The concurrently running invocations of restrict_one_thread coordinate
+ * through the shared ctx object to do their work in lockstep to implement
+ * all-or-nothing semantics for enforcing the new Landlock domain.
+ *
+ * Afterwards, depending on the presence of an error, all threads either commit
+ * or abort the prepared credentials. The commit operation can not fail any more.
+ */
+static void restrict_one_thread(struct tsync_shared_context *ctx)
+{
+ int res;
+ struct cred *cred = NULL;
+ const struct cred *current_cred = current_cred();
+
+ if (current_cred == ctx->old_cred) {
+ /*
+ * As a shortcut, switch out old_cred with new_cred, if
+ * possible.
+ *
+ * Note: We are intentionally dropping the const qualifier here,
+ * because it is required by commit_creds() and abort_creds().
+ */
+ cred = (struct cred *)get_cred(ctx->new_cred);
+ } else {
+ /* Else, prepare new creds and populate them. */
+ cred = prepare_creds();
+
+ if (!cred) {
+ atomic_set(&ctx->preparation_error, -ENOMEM);
+
+ /*
+ * Even on error, we need to adhere to the protocol and
+ * coordinate with concurrently running invocations.
+ */
+ if (atomic_dec_return(&ctx->num_preparing) == 0)
+ complete_all(&ctx->all_prepared);
+
+ goto out;
+ }
+
+ landlock_cred_copy(landlock_cred(cred),
+ landlock_cred(ctx->new_cred));
+ }
+
+ /*
+ * Barrier: Wait until all threads are done preparing.
+ * After this point, we can have no more failures.
+ */
+ if (atomic_dec_return(&ctx->num_preparing) == 0)
+ complete_all(&ctx->all_prepared);
+
+ /*
+ * Wait for signal from calling thread that it's safe to read the
+ * preparation error now and we are ready to commit (or abort).
+ */
+ wait_for_completion(&ctx->ready_to_commit);
+
+ /* Abort the commit if any of the other threads had an error. */
+ res = atomic_read(&ctx->preparation_error);
+ if (res) {
+ abort_creds(cred);
+ goto out;
+ }
+
+ /* If needed, establish enforcement prerequisites. */
+ if (!ns_capable_noaudit(current_user_ns(), CAP_SYS_ADMIN))
+ task_set_no_new_privs(current);
+
+ commit_creds(cred);
+
+out:
+ /* Notify the calling thread once all threads are done */
+ if (atomic_dec_return(&ctx->num_unfinished) == 0)
+ complete_all(&ctx->all_finished);
+}
+
+/*
+ * restrict_one_thread_callback - task_work callback for restricting a thread
+ *
+ * Calls restrict_one_thread with the struct landlock_shared_tsync_context.
+ */
+static void restrict_one_thread_callback(struct callback_head *work)
+{
+ struct tsync_work *ctx = container_of(work, struct tsync_work, work);
+
+ restrict_one_thread(ctx->shared_ctx);
+}
+
+/*
+ * struct tsync_works - a growable array of per-task contexts
+ *
+ * The zero-initialized struct represents the empty array.
+ */
+struct tsync_works {
+ struct tsync_work **works;
+ size_t size;
+ size_t capacity;
+};
+
+/*
+ * tsync_works_provide - provides a preallocated tsync_work for the given task
+ *
+ * This also stores a task pointer in the context and increments the reference
+ * count of the task.
+ *
+ * Returns:
+ * A pointer to the preallocated context struct, with task filled in.
+ *
+ * NULL, if we ran out of preallocated context structs.
+ */
+static struct tsync_work *tsync_works_provide(struct tsync_works *s,
+ struct task_struct *task)
+{
+ struct tsync_work *ctx;
+
+ if (s->size >= s->capacity)
+ return NULL;
+
+ ctx = s->works[s->size];
+ s->size++;
+
+ ctx->task = get_task_struct(task);
+ return ctx;
+}
+
+/*
+ * tsync_works_grow_by - preallocates space for n more contexts in s
+ *
+ * Returns:
+ * -ENOMEM if the (re)allocation fails
+ * 0 if the allocation succeeds, partially succeeds, or no reallocation was needed
+ */
+static int tsync_works_grow_by(struct tsync_works *s, size_t n, gfp_t flags)
+{
+ int i;
+ size_t new_capacity = s->capacity + n;
+ struct tsync_work **works;
+
+ if (new_capacity <= s->capacity)
+ return 0;
+
+ works = krealloc_array(s->works, new_capacity, sizeof(s->works[0]),
+ flags);
+ if (IS_ERR(works))
+ return PTR_ERR(works);
+
+ s->works = works;
+
+ for (i = s->capacity; i < new_capacity; i++) {
+ s->works[i] = kzalloc(sizeof(*s->works[i]), flags);
+ if (IS_ERR(s->works[i])) {
+ /*
+ * Leave the object in a consistent state,
+ * but return an error.
+ */
+ s->capacity = i;
+ return PTR_ERR(s->works[i]);
+ }
+ }
+ s->capacity = new_capacity;
+ return 0;
+}
+
+/*
+ * tsync_works_contains - checks for presence of task in s
+ */
+static bool tsync_works_contains_task(struct tsync_works *s,
+ struct task_struct *task)
+{
+ size_t i;
+
+ for (i = 0; i < s->size; i++)
+ if (s->works[i]->task == task)
+ return true;
+ return false;
+}
+
+/*
+ * tsync_works_free - free memory held by s and drop all task references
+ */
+static void tsync_works_free(struct tsync_works *s)
+{
+ int i;
+
+ for (i = 0; i < s->size; i++)
+ put_task_struct(s->works[i]->task);
+ for (i = 0; i < s->capacity; i++)
+ kfree(s->works[i]);
+ kfree(s->works);
+ s->works = NULL;
+ s->size = 0;
+ s->capacity = 0;
+}
+
+/*
+ * restrict_sibling_threads - enables a Landlock policy for all sibling threads
+ */
+static int restrict_sibling_threads(const struct cred *old_cred,
+ const struct cred *new_cred)
+{
+ int res;
+ struct task_struct *thread, *caller;
+ struct tsync_shared_context shared_ctx;
+ struct tsync_works works = {};
+ size_t newly_discovered_threads;
+ bool found_more_threads;
+ struct tsync_work *ctx;
+
+ atomic_set(&shared_ctx.preparation_error, 0);
+ init_completion(&shared_ctx.all_prepared);
+ init_completion(&shared_ctx.ready_to_commit);
+ atomic_set(&shared_ctx.num_unfinished, 0);
+ init_completion(&shared_ctx.all_finished);
+ shared_ctx.old_cred = old_cred;
+ shared_ctx.new_cred = new_cred;
+
+ caller = current;
+
+ /*
+ * We schedule a pseudo-signal task_work for each of the calling task's
+ * sibling threads. In the task work, each thread:
+ *
+ * 1) runs prepare_creds() and writes back the error to
+ * shared_ctx.preparation_error, if needed.
+ *
+ * 2) signals that it's done with prepare_creds() to the calling task.
+ * (completion "all_prepared").
+ *
+ * 3) waits for the completion "ready_to_commit". This is sent by the
+ * calling task after ensuring that all sibling threads have done
+ * with the "preparation" stage.
+ *
+ * After this barrier is reached, it's safe to read
+ * shared_ctx.preparation_error.
+ *
+ * 4) reads shared_ctx.preparation_error and then either does
+ * commit_creds() or abort_creds().
+ *
+ * 5) signals that it's done altogether (barrier synchronization
+ * "all_finished")
+ */
+ do {
+ found_more_threads = false;
+
+ /*
+ * The "all_prepared" barrier is used locally to the inner loop,
+ * this use of for_each_thread(). We can reset it on each loop
+ * iteration because all previous loop iterations are done with
+ * it already.
+ *
+ * num_preparing is initialized to 1 so that the counter can not
+ * go to 0 and mark the completion as done before all task works
+ * are registered. (We decrement it at the end of this loop.)
+ */
+ atomic_set(&shared_ctx.num_preparing, 1);
+ reinit_completion(&shared_ctx.all_prepared);
+
+ /* In RCU read-lock, count the threads we need. */
+ newly_discovered_threads = 0;
+ rcu_read_lock();
+ for_each_thread(caller, thread) {
+ /* Skip current, since it is initiating the sync. */
+ if (thread == caller)
+ continue;
+
+ /* Skip exited threads. */
+ if (thread->flags & PF_EXITING)
+ continue;
+
+ /* Skip threads that we have already seen. */
+ if (tsync_works_contains_task(&works, thread))
+ continue;
+
+ newly_discovered_threads++;
+ }
+ rcu_read_unlock();
+
+ if (newly_discovered_threads == 0)
+ break; /* done */
+
+ res = tsync_works_grow_by(&works, newly_discovered_threads,
+ GFP_KERNEL_ACCOUNT);
+ if (res) {
+ atomic_set(&shared_ctx.preparation_error, res);
+ break;
+ }
+
+ rcu_read_lock();
+ for_each_thread(caller, thread) {
+ /* Skip current, since it is initiating the sync. */
+ if (thread == caller)
+ continue;
+
+ /* Skip exited threads. */
+ if (thread->flags & PF_EXITING)
+ continue;
+
+ /* Skip threads that we already looked at. */
+ if (tsync_works_contains_task(&works, thread))
+ continue;
+
+ /*
+ * We found a sibling thread that is not doing its
+ * task_work yet, and which might spawn new threads
+ * before our task work runs, so we need at least one
+ * more round in the outer loop.
+ */
+ found_more_threads = true;
+
+ ctx = tsync_works_provide(&works, thread);
+ if (!ctx) {
+ /*
+ * We ran out of preallocated contexts -- we
+ * need to try again with this thread at a later
+ * time! found_more_threads is already true
+ * at this point.
+ */
+ break;
+ }
+
+ ctx->shared_ctx = &shared_ctx;
+
+ atomic_inc(&shared_ctx.num_preparing);
+ atomic_inc(&shared_ctx.num_unfinished);
+
+ init_task_work(&ctx->work,
+ restrict_one_thread_callback);
+ res = task_work_add(thread, &ctx->work, TWA_SIGNAL);
+ if (res) {
+ /*
+ * Remove the task from ctx so that we will
+ * revisit the task at a later stage, if it
+ * still exists.
+ */
+ put_task_struct_rcu_user(ctx->task);
+ ctx->task = NULL;
+
+ atomic_set(&shared_ctx.preparation_error, res);
+ atomic_dec(&shared_ctx.num_preparing);
+ atomic_dec(&shared_ctx.num_unfinished);
+ }
+ }
+ rcu_read_unlock();
+
+ /*
+ * Decrement num_preparing for current, to undo that we
+ * initialized it to 1 at the beginning of the inner loop.
+ */
+ if (atomic_dec_return(&shared_ctx.num_preparing) > 0)
+ wait_for_completion(&shared_ctx.all_prepared);
+ } while (found_more_threads &&
+ !atomic_read(&shared_ctx.preparation_error));
+
+ /*
+ * We now have all sibling threads blocking and in "prepared" state in
+ * the task work. Ask all threads to commit.
+ */
+ complete_all(&shared_ctx.ready_to_commit);
+
+ if (works.size)
+ wait_for_completion(&shared_ctx.all_finished);
+
+ tsync_works_free(&works);
+
+ return atomic_read(&shared_ctx.preparation_error);
+}
+
/**
* sys_landlock_restrict_self - Enforce a ruleset on the calling thread
*
@@ -454,12 +866,20 @@ SYSCALL_DEFINE4(landlock_add_rule, const int, ruleset_fd,
* - %LANDLOCK_RESTRICT_SELF_LOG_SAME_EXEC_OFF
* - %LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON
* - %LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF
+ * - %LANDLOCK_RESTRICT_SELF_TSYNC
*
- * This system call enables to enforce a Landlock ruleset on the current
- * thread. Enforcing a ruleset requires that the task has %CAP_SYS_ADMIN in its
+ * This system call enforces a Landlock ruleset on the current thread.
+ * Enforcing a ruleset requires that the task has %CAP_SYS_ADMIN in its
* namespace or is running with no_new_privs. This avoids scenarios where
* unprivileged tasks can affect the behavior of privileged children.
*
+ * If %LANDLOCK_RESTRICT_SELF_TSYNC is specified in @flags, all other threads of
+ * the process will be brought into the exact same Landlock configuration as the
+ * calling thread. This includes both the enforced ruleset and logging
+ * configuration, and happens irrespective of previously established rulesets
+ * and logging configurations on these threads. If required, this operation
+ * also enables the no_new_privs flag for these threads.
+ *
* Possible returned errors are:
*
* - %EOPNOTSUPP: Landlock is supported by the kernel but disabled at boot time;
@@ -484,6 +904,7 @@ SYSCALL_DEFINE2(landlock_restrict_self, const int, ruleset_fd, const __u32,
struct landlock_cred_security *new_llcred;
bool __maybe_unused log_same_exec, log_new_exec, log_subdomains,
prev_log_subdomains;
+ int res;
if (!is_initialized())
return -EOPNOTSUPP;
@@ -566,5 +987,13 @@ SYSCALL_DEFINE2(landlock_restrict_self, const int, ruleset_fd, const __u32,
new_llcred->domain_exec |= BIT(new_dom->num_layers - 1);
#endif /* CONFIG_AUDIT */
+ if (flags & LANDLOCK_RESTRICT_SELF_TSYNC) {
+ res = restrict_sibling_threads(current_cred(), new_cred);
+ if (res != 0) {
+ abort_creds(new_cred);
+ return res;
+ }
+ }
+
return commit_creds(new_cred);
}
--
2.51.0.618.g983fd99d29-goog
^ permalink raw reply related
* [PATCH v2 2/2] landlock: selftests for LANDLOCK_RESTRICT_SELF_TSYNC
From: Günther Noack @ 2025-10-01 11:18 UTC (permalink / raw)
To: Mickaël Salaün
Cc: Günther Noack, Konstantin Meskhidze, Tingmao Wang,
Paul Moore, linux-security-module
In-Reply-To: <20251001111807.18902-1-gnoack@google.com>
Exercise various scenarios where Landlock domains are enforced across
all of a processes' threads.
Cc: Mickaël Salaün <mic@digikod.net>
Cc: Paul Moore <paul@paul-moore.com>
Cc: linux-security-module@vger.kernel.org
Signed-off-by: Günther Noack <gnoack@google.com>
---
tools/testing/selftests/landlock/base_test.c | 6 +-
tools/testing/selftests/landlock/tsync_test.c | 99 +++++++++++++++++++
2 files changed, 102 insertions(+), 3 deletions(-)
create mode 100644 tools/testing/selftests/landlock/tsync_test.c
diff --git a/tools/testing/selftests/landlock/base_test.c b/tools/testing/selftests/landlock/base_test.c
index 7b69002239d7..b9be6dcb7e8c 100644
--- a/tools/testing/selftests/landlock/base_test.c
+++ b/tools/testing/selftests/landlock/base_test.c
@@ -288,7 +288,7 @@ TEST(restrict_self_fd)
EXPECT_EQ(EBADFD, errno);
}
-TEST(restrict_self_fd_flags)
+TEST(restrict_self_fd_logging_flags)
{
int fd;
@@ -304,9 +304,9 @@ TEST(restrict_self_fd_flags)
EXPECT_EQ(EBADFD, errno);
}
-TEST(restrict_self_flags)
+TEST(restrict_self_logging_flags)
{
- const __u32 last_flag = LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF;
+ const __u32 last_flag = LANDLOCK_RESTRICT_SELF_TSYNC;
/* Tests invalid flag combinations. */
diff --git a/tools/testing/selftests/landlock/tsync_test.c b/tools/testing/selftests/landlock/tsync_test.c
new file mode 100644
index 000000000000..356e20de352f
--- /dev/null
+++ b/tools/testing/selftests/landlock/tsync_test.c
@@ -0,0 +1,99 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Landlock tests - Enforcing the same restrictions across multiple threads
+ *
+ * Copyright © 2025 Günther Noack <gnoack3000@gmail.com>
+ */
+
+#define _GNU_SOURCE
+#include <pthread.h>
+#include <sys/prctl.h>
+#include <linux/landlock.h>
+
+#include "common.h"
+
+/* create_ruleset - Create a simple ruleset FD common to all tests */
+static int create_ruleset(struct __test_metadata *const _metadata)
+{
+ struct landlock_ruleset_attr ruleset_attr = {
+ .handled_access_fs = (LANDLOCK_ACCESS_FS_WRITE_FILE |
+ LANDLOCK_ACCESS_FS_TRUNCATE),
+ };
+ const int ruleset_fd =
+ landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
+
+ ASSERT_LE(0, ruleset_fd);
+ return ruleset_fd;
+}
+
+TEST(single_threaded_success)
+{
+ const int ruleset_fd = create_ruleset(_metadata);
+
+ disable_caps(_metadata);
+
+ ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0));
+ ASSERT_EQ(0, landlock_restrict_self(ruleset_fd,
+ LANDLOCK_RESTRICT_SELF_TSYNC));
+
+ ASSERT_EQ(0, close(ruleset_fd));
+}
+
+void *idle(void *data)
+{
+ while (true)
+ sleep(1);
+}
+
+TEST(multi_threaded_success)
+{
+ pthread_t t1, t2;
+ const int ruleset_fd = create_ruleset(_metadata);
+
+ disable_caps(_metadata);
+
+ ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0));
+
+ ASSERT_EQ(0, pthread_create(&t1, NULL, idle, NULL));
+ ASSERT_EQ(0, pthread_create(&t2, NULL, idle, NULL));
+
+ EXPECT_EQ(0, landlock_restrict_self(ruleset_fd,
+ LANDLOCK_RESTRICT_SELF_TSYNC));
+
+ ASSERT_EQ(0, pthread_cancel(t1));
+ ASSERT_EQ(0, pthread_cancel(t2));
+ ASSERT_EQ(0, pthread_join(t1, NULL));
+ ASSERT_EQ(0, pthread_join(t2, NULL));
+ ASSERT_EQ(0, close(ruleset_fd));
+}
+
+TEST(multi_threaded_success_despite_diverging_domains)
+{
+ pthread_t t1, t2;
+ const int ruleset_fd = create_ruleset(_metadata);
+
+ disable_caps(_metadata);
+
+ ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0));
+
+ ASSERT_EQ(0, pthread_create(&t1, NULL, idle, NULL));
+ ASSERT_EQ(0, pthread_create(&t2, NULL, idle, NULL));
+
+ /*
+ * The main thread enforces a ruleset,
+ * thereby bringing the threads' Landlock domains out of sync.
+ */
+ EXPECT_EQ(0, landlock_restrict_self(ruleset_fd, 0));
+
+ /* Still, TSYNC succeeds, bringing the threads in sync again. */
+ EXPECT_EQ(0, landlock_restrict_self(ruleset_fd,
+ LANDLOCK_RESTRICT_SELF_TSYNC));
+
+ ASSERT_EQ(0, pthread_cancel(t1));
+ ASSERT_EQ(0, pthread_cancel(t2));
+ ASSERT_EQ(0, pthread_join(t1, NULL));
+ ASSERT_EQ(0, pthread_join(t2, NULL));
+ ASSERT_EQ(0, close(ruleset_fd));
+}
+
+TEST_HARNESS_MAIN
--
2.51.0.618.g983fd99d29-goog
^ permalink raw reply related
* [PATCH v2 0/2] Landlock multithreaded enforcement
From: Günther Noack @ 2025-10-01 11:23 UTC (permalink / raw)
To: Mickaël Salaün
Cc: linux-security-module, Günther Noack, Konstantin Meskhidze,
Tingmao Wang
This patch set adds the LANDLOCK_RESTRICT_SELF_TSYNC flag to
landlock_restrict_self(). With this flag, the passed Landlock ruleset
will not only be applied to the calling thread, but to all threads
which belong to the same process.
Motivation
==========
TL;DR: The libpsx/nptl(7) signal hack which we use in user space for
multi-threaded Landlock enforcement is incompatible with Landlock's
signal scoping support. Landlock can restrict the use of signals
across Landlock domains, but we need signals ourselves in user space
in ways that are not permitted any more under these restrictions.
Enabling Landlock proves to be difficult in processes that are already
multi-threaded at the time of enforcement:
* Enforcement in only one thread is usually a mistake because threads
do not normally have proper security boundaries between them.
* Also, multithreading is unavoidable in some circumstances, such as
when using Landlock from a Go program. Go programs are already
multithreaded by the time that they enter the "func main()".
So far, the approach in Go[1] was to use libpsx[2]. This library
implements the mechanism described in nptl(7) [3]: It keeps track of
all threads with a linker hack and then makes all threads do the same
syscall by registering a signal handler for them and invoking it.
With commit 54a6e6bbf3be ("landlock: Add signal scoping"), Landlock
gained the ability to restrict the use of signals across different
Landlock domains.
Landlock's signal scoping support is incompatible with the libpsx
approach of enabling Landlock:
(1) With libpsx, although all threads enforce the same ruleset object,
they technically do the operation separately and end up in
distinct Landlock domains. This breaks signaling across threads
when using LANDLOCK_SCOPE_SIGNAL.
(2) Cross-thread Signals are themselves needed to enforce further
nested Landlock domains across multiple threads. So nested
Landlock policies become impossible there.
In addition to Landlock itself, cross-thread signals are also needed
for other seemingly-harmless API calls like the setuid(2) [4] and for
the use of libcap (co-developed with libpsx), which have the same
problem where the underlying syscall only applies to the calling
thread.
Implementation details
======================
Enforcement prerequisites
-------------------------
Normally, the prerequisite for enforcing a Landlock policy is to
either have CAP_SYS_ADMIN or the no_new_privs flag. With
LANDLOCK_RESTRICT_SELF_TSYNC, the no_new_privs flag will automatically
be applied for sibling threads if they don't already fulfill these
requirements.
Pseudo-signals
--------------
Landlock domains are stored in struct cred, and a task's struct cred
can only be modified by the task itself [6].
To make that work, we use task_work_add() to register a pseudo-signal
for each of the affected threads. At signal execution time, these
tasks will coordinate to switch out their Landlock policy in lockstep
with each other, guaranteeing all-or-nothing semantics.
This implementation can be thought of as a kernel-side implementation
of the userspace hack that glibc/NPTL use for setuid(2) [3] [4], and
which libpsx implements for libcap [2].
Finding all sibling threads
---------------------------
In order to avoid grabbing the global task_list_lock, we employ the
scheme proposed by Jann Horn in [7]:
1. Loop through the list of sibling threads
2. Schedule a pseudo-signal for each and make each thread wait in the
pseudo-signal
3. Go back to 1. and look for more sibling thread that we have not
seen yet
Do this until no more new threads are found. As all threads were
waiting in their pseudo-signals, they can not spawn additional threads
and we found them all.
Coordination between tasks
--------------------------
As tasks run their pseudo-signal task work, they coordinate through
the following completions:
- all_prepared (with counter num_preparing)
When done, all new sibling threads in the inner loop(!) of finding
new threads are now in their pseudo-signal handlers and have
prepared the struct cred object to commit (or written an error into
the shared "preparation_error").
The lifetime of all_prepared is only the inner loop of finding new
threads.
- ready_to_commit
When done, the outer loop of finding new threads is done and all
sibling threads have prepared their struct cred object. Marked
completed by the calling thread.
- all_finished
When done, all sibling threads are done executing their
pseudo-signal handlers.
Use of credentials API
----------------------
Under normal circumstances, sibling threads share the same struct cred
object. To avoid unnecessary duplication, if we find that a thread
uses the same struct cred as the calling thread, we side-step the
normal use of the credentials API [6] and place a pointer to that
existing struct cred instead of creating a new one using
prepare_creds() in the sibling thread.
Noteworthy discussion points
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* We are side-stepping the normal credentials API [6], by re-wiring an
existing struct cred object instead of calling prepare_creds().
We can technically avoid it, but it would create unnecessary
duplicate struct cred objects in multithreaded scenarios.
* I am slightly unhappy with the elaborate memory allocation scheme
that I built for the task work objects. Ideas are welcome.
Change Log
==========
v2:
- https://lore.kernel.org/all/20250221184417.27954-2-gnoack3000@gmail.com/
- Semantics:
- Threads implicitly set NO_NEW_PRIVS unless they have
CAP_SYS_ADMIN, to fulfill Landlock policy enforcement
prerequisites
- Landlock policy gets unconditionally overridden even if the
previously established Landlock domains in sibling threads were
diverging.
- Restructure discovery of all sibling threads, with the algorithm
proposed by Jann Horn [7]: Loop through threads multiple times, and
get them all stuck in the pseudo signal (task work), until no new
sibling threads show up.
- Use RCU lock when iterating over sibling threads.
- Override existing Landlock domains of other threads,
instead of applying a new Landlock policy on top
- Directly re-wire the struct cred for sibling threads,
instread of creating a new one with prepare_creds().
- Tests:
- Remove multi_threaded_failure test
(The only remaining failure case is ENOMEM,
there is no good way to provoke that in a selftest)
- Add test for success despite diverging Landlock domains.
[1] https://github.com/landlock-lsm/go-landlock
[2] https://sites.google.com/site/fullycapable/who-ordered-libpsx
[3] https://man.gnoack.org/7/nptl
[4] https://man.gnoack.org/2/setuid#VERSIONS
[5] https://lore.kernel.org/all/20240805-remove-cred-transfer-v2-0-a2aa1d45e6b8@google.com/
[6] https://www.kernel.org/doc/html/latest/security/credentials.html
[7] https://lore.kernel.org/all/CAG48ez0pWg3OTABfCKRk5sWrURM-HdJhQMcWedEppc_z1rrVJw@mail.gmail.com/
Günther Noack (2):
landlock: Multithreading support for landlock_restrict_self()
landlock: selftests for LANDLOCK_RESTRICT_SELF_TSYNC
include/uapi/linux/landlock.h | 4 +
security/landlock/cred.h | 12 +
security/landlock/limits.h | 2 +-
security/landlock/syscalls.c | 433 +++++++++++++++++-
tools/testing/selftests/landlock/base_test.c | 6 +-
tools/testing/selftests/landlock/tsync_test.c | 99 ++++
6 files changed, 550 insertions(+), 6 deletions(-)
create mode 100644 tools/testing/selftests/landlock/tsync_test.c
--
2.51.0.618.g983fd99d29-goog
^ permalink raw reply
* Re: [PATCH v3 01/10] tpm: Cap the number of PCR banks
From: Jarkko Sakkinen @ 2025-10-01 12:52 UTC (permalink / raw)
To: James Bottomley
Cc: Jonathan McDowell, linux-integrity, dpsmith, ross.philipson,
Jarkko Sakkinen, Roberto Sassu, Peter Huewe, Jason Gunthorpe,
David Howells, Paul Moore, James Morris, Serge E. Hallyn,
open list, open list:KEYS/KEYRINGS, open list:SECURITY SUBSYSTEM
In-Reply-To: <aN0NcIlyrUsejMXW@kernel.org>
On Wed, Oct 01, 2025 at 02:16:04PM +0300, Jarkko Sakkinen wrote:
> On Tue, Sep 30, 2025 at 10:17:22AM -0400, James Bottomley wrote:
> > On Tue, 2025-09-30 at 15:36 +0300, Jarkko Sakkinen wrote:
> > > On Tue, Sep 30, 2025 at 12:09:15PM +0100, Jonathan McDowell wrote:
> > > > On Mon, Sep 29, 2025 at 10:48:23PM +0300, Jarkko Sakkinen wrote:
> > [...]
> > > > > +#define TPM2_MAX_DIGEST_SIZE SHA512_DIGEST_SIZE
> > > > > +#define TPM2_MAX_BANKS 4
> > > >
> > > > Where does this max come from? It matches what I see with swtpm by
> > > > default (SHA1, SHA2-256, SHA2-384, SHA-512), so I haven't seen
> > > > anything that exceeds it myself.
> > >
> > > I've never seen hardware TPM that would have more than one or two
> > > banks. We can double it to leave some room. This was tested with
> > > swtpm defaults.
> >
> > I've got a hardware TPM that comes with 3 banks by default (it's a
> > chinese one which has sha1 sha256 and sm2). swtpm isn't a good
> > indicator because it's default allocation is rather pejorative (it
> > disables sha1 whereas most field TPMs don't).
> >
> > However, if you look at how the reference implementation works, the
> > user is allowed to define any number of banks they want, up to the
> > number of supported hashes. The only limitation being there can't be
> > >1 bank for the same hash. Field TPM implementations are allowed to
> > constrain this, but most don't. The question you should be asking
> > here is not how many banks does a particular implementation allow by
> > default, but what's the maximum number a user could configure.
>
> It needs some compilation time cap as the value comes from external
> device. If someone hits to that value, then it needs to be increased
> but as unconstrained it's a bug.
Maximum eight banks should be spacy enough for the time being (and for
the foreseeable future).
BR, Jarkko
^ permalink raw reply
* Re: [PATCH v4 31/34] ima,evm: move initcalls to the LSM framework
From: Mimi Zohar @ 2025-10-01 17:03 UTC (permalink / raw)
To: Paul Moore
Cc: selinux, linux-integrity, linux-security-module, John Johansen,
Roberto Sassu, Fan Wu, Mickaël Salaün,
Günther Noack, Kees Cook, Micah Morton, Casey Schaufler,
Tetsuo Handa, Nicolas Bouchinet, Xiu Jianfeng
In-Reply-To: <CAHC9VhQCmFJQ1=Eyu1D+Mcg2FVDByrk8QcwV5HaZdB95esiA7Q@mail.gmail.com>
On Tue, 2025-09-30 at 16:11 -0400, Paul Moore wrote:
> On Tue, Sep 16, 2025 at 6:14 PM Paul Moore <paul@paul-moore.com> wrote:
> >
> > From: Roberto Sassu <roberto.sassu@huawei.com>
> >
> > This patch converts IMA and EVM to use the LSM frameworks's initcall
> > mechanism. It moved the integrity_fs_init() call to ima_fs_init() and
> > evm_init_secfs(), to work around the fact that there is no "integrity" LSM,
> > and introduced integrity_fs_fini() to remove the integrity directory, if
> > empty. Both integrity_fs_init() and integrity_fs_fini() support the
> > scenario of being called by both the IMA and EVM LSMs.
> >
> > This patch does not touch any of the platform certificate code that
> > lives under the security/integrity/platform_certs directory as the
> > IMA/EVM developers would prefer to address that in a future patchset.
> >
> > Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
> > [PM: adjust description as discussed over email]
> > Signed-off-by: Paul Moore <paul@paul-moore.com>
> > ---
> > security/integrity/evm/evm_main.c | 3 +--
> > security/integrity/evm/evm_secfs.c | 11 +++++++++--
> > security/integrity/iint.c | 14 ++++++++++++--
> > security/integrity/ima/ima_fs.c | 11 +++++++++--
> > security/integrity/ima/ima_main.c | 4 ++--
> > security/integrity/integrity.h | 2 ++
> > 6 files changed, 35 insertions(+), 10 deletions(-)
>
> I appreciate you reviewing most (all?) of the other patches in this
> patchset, but any chance you could review the IMA/EVM from Roberto?
> This is the only patch that really needs your review ...
I've already reviewed the patch, just not Acked it yet. I'll hopefully get to
testing it later this week or next week.
Mimi
^ permalink raw reply
* Re: [PATCH v4 31/34] ima,evm: move initcalls to the LSM framework
From: Paul Moore @ 2025-10-01 17:23 UTC (permalink / raw)
To: Mimi Zohar
Cc: selinux, linux-integrity, linux-security-module, John Johansen,
Roberto Sassu, Fan Wu, Mickaël Salaün,
Günther Noack, Kees Cook, Micah Morton, Casey Schaufler,
Tetsuo Handa, Nicolas Bouchinet, Xiu Jianfeng
In-Reply-To: <74178ea117c18f88b4c3607e5a2afb81fc80e428.camel@linux.ibm.com>
On Wed, Oct 1, 2025 at 1:04 PM Mimi Zohar <zohar@linux.ibm.com> wrote:
> On Tue, 2025-09-30 at 16:11 -0400, Paul Moore wrote:
> > On Tue, Sep 16, 2025 at 6:14 PM Paul Moore <paul@paul-moore.com> wrote:
> > >
> > > From: Roberto Sassu <roberto.sassu@huawei.com>
> > >
> > > This patch converts IMA and EVM to use the LSM frameworks's initcall
> > > mechanism. It moved the integrity_fs_init() call to ima_fs_init() and
> > > evm_init_secfs(), to work around the fact that there is no "integrity" LSM,
> > > and introduced integrity_fs_fini() to remove the integrity directory, if
> > > empty. Both integrity_fs_init() and integrity_fs_fini() support the
> > > scenario of being called by both the IMA and EVM LSMs.
> > >
> > > This patch does not touch any of the platform certificate code that
> > > lives under the security/integrity/platform_certs directory as the
> > > IMA/EVM developers would prefer to address that in a future patchset.
> > >
> > > Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
> > > [PM: adjust description as discussed over email]
> > > Signed-off-by: Paul Moore <paul@paul-moore.com>
> > > ---
> > > security/integrity/evm/evm_main.c | 3 +--
> > > security/integrity/evm/evm_secfs.c | 11 +++++++++--
> > > security/integrity/iint.c | 14 ++++++++++++--
> > > security/integrity/ima/ima_fs.c | 11 +++++++++--
> > > security/integrity/ima/ima_main.c | 4 ++--
> > > security/integrity/integrity.h | 2 ++
> > > 6 files changed, 35 insertions(+), 10 deletions(-)
> >
> > I appreciate you reviewing most (all?) of the other patches in this
> > patchset, but any chance you could review the IMA/EVM from Roberto?
> > This is the only patch that really needs your review ...
>
> I've already reviewed the patch, just not Acked it yet. I'll hopefully get to
> testing it later this week or next week.
As mentioned off-list, a review-by tag is worthless if you want me to
hold it for your ACK. When I'm asking you for a review on code which
you maintain, I'm asking for your go/no-go on the patch for merging;
that's an ACK.
--
paul-moore.com
^ permalink raw reply
* Re: [PATCH bpf-next v2 0/3] BPF signature hash chains
From: Paul Moore @ 2025-10-01 21:37 UTC (permalink / raw)
To: Linus Torvalds, Blaise Boscaccy, ast, kpsingh, james.bottomley
Cc: bpf, linux-security-module, kys, daniel, andrii, wufan, qmo
In-Reply-To: <20250929213520.1821223-1-bboscaccy@linux.microsoft.com>
On Mon, Sep 29, 2025 at 5:35 PM Blaise Boscaccy
<bboscaccy@linux.microsoft.com> wrote:
>
> This patchset extends the currently proposed signature verification
> patchset
> https://lore.kernel.org/linux-security-module/20250813205526.2992911-1-kpsingh@kernel.org/
> with hash-chain functionality to verify the contents of arbitrary
> maps.
>
> The currently proposed loader + map signature verification
> scheme—requested by Alexei and KP—is simple to implement and
> acceptable if users/admins are satisfied with it. However, verifying
> both the loader and the maps offers additional benefits beyond just
> verifying the loader:
>
> 1. Simplified Loader Logic: The lskel loader becomes simpler since it
> doesn’t need to verify program maps—this is already handled by
> bpf_check_signature().
>
> 2. Security and Audit Integrity: A key advantage is that the LSM
> (Linux Security Module) hook for authorizing BPF program loads can
> operate after signature verification. This ensures:
>
> * Access control decisions can be based on verified signature
> * status. Accurate system state measurement and logging. Log
> * events claiming a verified signature are fully truthful, avoiding
> * misleading entries that only the loader was verified while the
> * actual BPF program verification happens later without logging.
>
> This approach addresses concerns from users who require strict audit
> trails and verification guarantees, especially in security-sensitive
> environments.
>
> A working tree with this patchset is being maintained at
> https://github.com/blaiseboscaccy/linux/tree/bpf-hash-chains
>
> bpf CI tests passed as well
> https://github.com/kernel-patches/bpf/actions/runs/18110352925
>
> v2 -> v1:
> - Fix regression found by syzkaller
> - Add bash auto-complete support for new command line switch
>
> Blaise Boscaccy (3):
> bpf: Add hash chain signature support for arbitrary maps
> selftests/bpf: Enable map verification for some lskel tests
> bpftool: Add support for signing program and map hash chains
>
> include/uapi/linux/bpf.h | 6 ++
> kernel/bpf/syscall.c | 73 ++++++++++++++++++-
> .../bpf/bpftool/Documentation/bpftool-gen.rst | 7 +-
> tools/bpf/bpftool/bash-completion/bpftool | 2 +-
> tools/bpf/bpftool/gen.c | 27 ++++++-
> tools/bpf/bpftool/main.c | 9 ++-
> tools/bpf/bpftool/main.h | 1 +
> tools/bpf/bpftool/sign.c | 16 +++-
> tools/include/uapi/linux/bpf.h | 6 ++
> tools/lib/bpf/libbpf.h | 3 +-
> tools/lib/bpf/skel_internal.h | 6 +-
> tools/testing/selftests/bpf/Makefile | 18 ++++-
> 12 files changed, 159 insertions(+), 15 deletions(-)
During a discussion of one of Blaise's earlier BPF signature
verification proposals Alexei mentioned that it sounded like I was
looking for Linus' opinion on our debate[1]. At the time I replied
that I was more interested in trying to find out what the BPF devs
wanted for a BPF program signing solution, and working towards making
sure we had something that worked for everyone[2]. That was almost
five months ago, and while Alexei and KP have provided an example of
their ideal solution, it can now be found in Linus' tree, they have
ignored the larger issues brought up by the LSM community and have
refused to review or comment on Blaise's many attempts[3][4][5][6] to
reconcile the needs of the two communities.
With the lack of engagement from the BPF devs, I'm now at the point
where I'm asking Linus to comment on the current situation around
Blaise's patchset. I recognize that Alexei and KP have a strong
affinity for the signature scheme implemented in KP's patchset, which
is fine, but if we are going to be serious about implementing BPF
signature verification that can be used by a number of different user
groups, we also need to support the signature scheme that Blaise is
proposing[6].
To make it clear at the start, Blaise's patchset does not change,
block, or otherwise prevent the BPF program signature scheme
implemented in KP's patchset. Blaise intentionally designed his
patches such that the two signature schemes can coexist together in
the same kernel, allowing users to select which scheme to use on each
BPF program load, enabling the kernel to support policy to selectively
enforce rules around which signature scheme is permitted for each BPF
program load.
Blaise's patch implements an alternate BPF program signature scheme,
using the same basic concepts as KP's design (light skeletons, hash
chaining, etc.), but does so in such a way that the kernel verifies
all relevant parts of the BPF program load prior to calling the
security_bpf_prog_load() LSM hook. KP's signature scheme only
verifies the light skeleton prior to calling the LSM hook and relies
on the BPF code in the light skeleton to verify the original BPF
program.
Relying on a light skeleton to verify the BPF program means that any
verification failures in the light skeleton will be "lost" as there is
no way to convey an error code back to the user who is attempting the
BPF program load. Blaise's approach to verifying the full signature
in the kernel, and not relying on the light skeleton for verification,
means that verification failures can be returned to the user; there
are no silent signature verification failures like one can experience
with KP's verification scheme.
KP's signature verification scheme is a two-part scheme with the
security_bpf_prog_load() LSM hook being called after the light
skeleton signature has been verified, but before the light skeleton
verifies the BPF program. Aside from breaking with typical
conventions around the placement of LSM hooks, this "halfway" approach
makes it difficult for LSMs to log anything about the signature status
of a BPF program being loaded, or use the signature status in any type
of access decision. This is important for a number of user groups
that use LSM based security policies as a way to help reason about the
security properties of a system, as KP's scheme would require the
users to analyze the signature verification code in every BPF light
skeleton they authorize as well as the LSM policy in order to reason
about the security mechanisms involved in BPF program loading.
Blaise's signature scheme also has the nice property that BPF ELF
objects created using his scheme are backwards compatible with
existing released kernels that do not support any BPF signature
verification schemes, of course without any signature verification.
Loading a BPF ELF object using KP's signature scheme will likely fail
when loaded on existing released kernels.
[1] https://lore.kernel.org/linux-security-module/CAADnVQ+C2KNR1ryRtBGOZTNk961pF+30FnU9n3dt3QjaQu_N6Q@mail.gmail.com/
[2] https://lore.kernel.org/linux-security-module/CAHC9VhRjKV4AbSgqb4J_-xhkWAp_VAcKDfLJ4GwhBNPOr+cvpg@mail.gmail.com/
[3] https://lore.kernel.org/linux-security-module/87sei58vy3.fsf@microsoft.com/
[4] https://lore.kernel.org/linux-security-module/20250909162345.569889-2-bboscaccy@linux.microsoft.com/
[5] https://lore.kernel.org/linux-security-module/20250926203111.1305999-1-bboscaccy@linux.microsoft.com/
[6] https://lore.kernel.org/linux-security-module/20250929213520.1821223-1-bboscaccy@linux.microsoft.com/
--
paul-moore.com
^ permalink raw reply
* [PATCH 0/2] LSM: Identify module using network facilities
From: Casey Schaufler @ 2025-10-01 21:56 UTC (permalink / raw)
To: casey, paul, linux-security-module
Cc: jmorris, serge, keescook, john.johansen, penguin-kernel,
stephen.smalley.work, linux-kernel, selinux
In-Reply-To: <20251001215643.31465-1-casey.ref@schaufler-ca.com>
Security identification for network packets is provided by two mechanisms,
secmarks and netlabel.
Secmarks are 32 bit quantities managed by the netfilter system. It is
strongly believed that there is no hope that the size of this will ever
change. This is problematic in the face of multiple security modules
trying to use this facility at the same time. There is no identified use
case, nor user space support for specifying netfilter rules for multiple
LSMs. The LSMs have been modified to request use of the secmark, and to
eschew them if the request is denied. The first LSM that requests use
of secmarks is granted it, and all subsequent requests are denied.
Netlabel uses the CIPSO2 and CALIPSO IP options to transmit security
information on IP packets. It does not support sending multiple sets of
data. It is unlikely that any two LSMs would agree on how a packet should
be labeled. As with the secmarks, LSMs have been modified to request use
of netlabel, and to eschew them if the request is denied. The first LSM
that requests use of netlabel is granted it, and all subsequent requests
are denied.
The ordering determines which LSM gets these features. The ability
to determine which LSM gets the feature at boot time, perhaps with
lsm.secmark and lsm.netlabel boot options, is left for future work.
https://github.com/cschaufler/lsm-stacking#secmark-6.17-rc6-v1
Casey Schaufler (2):
LSM: Exclusive secmark usage
LSM: Allow reservation of netlabel
include/linux/lsm_hooks.h | 2 ++
security/apparmor/include/net.h | 5 ++++
security/apparmor/lsm.c | 7 +++---
security/security.c | 12 +++++++++
security/selinux/hooks.c | 11 +++++---
security/selinux/include/netlabel.h | 5 ++++
security/selinux/netlabel.c | 4 +--
security/smack/smack.h | 10 ++++++++
security/smack/smack_lsm.c | 39 +++++++++++++++++++++--------
security/smack/smack_netfilter.c | 10 ++++++--
security/smack/smackfs.c | 20 ++++++++++++++-
11 files changed, 103 insertions(+), 22 deletions(-)
--
2.51.0
^ permalink raw reply
* [PATCH 2/2] LSM: Allow reservation of netlabel
From: Casey Schaufler @ 2025-10-01 21:56 UTC (permalink / raw)
To: casey, paul, linux-security-module
Cc: jmorris, serge, keescook, john.johansen, penguin-kernel,
stephen.smalley.work, linux-kernel, selinux
In-Reply-To: <20251001215643.31465-1-casey@schaufler-ca.com>
Allow LSMs to request exclusive access to the netlabel facility.
Provide mechanism for LSMs to determine if they have access to
netlabel. Update the current users of netlabel, SELinux and Smack,
to use and respect the exclusive use of netlabel.
Signed-off-by: Casey Schaufler <casey@schaufler-ca.com>
---
include/linux/lsm_hooks.h | 1 +
security/security.c | 6 +++++
security/selinux/hooks.c | 7 +++---
security/selinux/include/netlabel.h | 5 ++++
security/selinux/netlabel.c | 4 ++--
security/smack/smack.h | 5 ++++
security/smack/smack_lsm.c | 36 +++++++++++++++++++++--------
security/smack/smackfs.c | 20 +++++++++++++++-
8 files changed, 69 insertions(+), 15 deletions(-)
diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
index 69c1b509577a..e49b5617383f 100644
--- a/include/linux/lsm_hooks.h
+++ b/include/linux/lsm_hooks.h
@@ -117,6 +117,7 @@ struct lsm_blob_sizes {
int lbs_tun_dev;
int lbs_bdev;
bool lbs_secmark; /* expressed desire for secmark use */
+ bool lbs_netlabel; /* expressed desire for netlabel use */
};
/*
diff --git a/security/security.c b/security/security.c
index e59e3d403de6..9eca10844b56 100644
--- a/security/security.c
+++ b/security/security.c
@@ -289,6 +289,12 @@ static void __init lsm_set_blob_sizes(struct lsm_blob_sizes *needed)
else
blob_sizes.lbs_secmark = true;
}
+ if (needed->lbs_netlabel) {
+ if (blob_sizes.lbs_netlabel)
+ needed->lbs_netlabel = false;
+ else
+ blob_sizes.lbs_netlabel = true;
+ }
}
/* Prepare LSM for initialization. */
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 5b6db7d8effb..24edeef41d25 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -182,7 +182,7 @@ static int selinux_secmark_enabled(void)
static int selinux_peerlbl_enabled(void)
{
return (selinux_policycap_alwaysnetwork() ||
- netlbl_enabled() || selinux_xfrm_enabled());
+ selinux_netlbl_enabled() || selinux_xfrm_enabled());
}
static int selinux_netcache_avc_callback(u32 event)
@@ -5863,7 +5863,7 @@ static unsigned int selinux_ip_forward(void *priv, struct sk_buff *skb,
SECCLASS_PACKET, PACKET__FORWARD_IN, &ad))
return NF_DROP;
- if (netlbl_enabled())
+ if (selinux_netlbl_enabled())
/* we do this in the FORWARD path and not the POST_ROUTING
* path because we want to make sure we apply the necessary
* labeling before IPsec is applied so we can leverage AH
@@ -5880,7 +5880,7 @@ static unsigned int selinux_ip_output(void *priv, struct sk_buff *skb,
struct sock *sk;
u32 sid;
- if (!netlbl_enabled())
+ if (!selinux_netlbl_enabled())
return NF_ACCEPT;
/* we do this in the LOCAL_OUT path and not the POST_ROUTING path
@@ -7185,6 +7185,7 @@ struct lsm_blob_sizes selinux_blob_sizes __ro_after_init = {
.lbs_tun_dev = sizeof(struct tun_security_struct),
.lbs_ib = sizeof(struct ib_security_struct),
.lbs_secmark = true,
+ .lbs_netlabel = true,
};
#ifdef CONFIG_PERF_EVENTS
diff --git a/security/selinux/include/netlabel.h b/security/selinux/include/netlabel.h
index 5731c0dcd3e8..5be82aa8e7ca 100644
--- a/security/selinux/include/netlabel.h
+++ b/security/selinux/include/netlabel.h
@@ -134,4 +134,9 @@ static inline int selinux_netlbl_socket_connect_locked(struct sock *sk,
}
#endif /* CONFIG_NETLABEL */
+static inline bool selinux_netlbl_enabled(void)
+{
+ return selinux_blob_sizes.lbs_netlabel && netlbl_enabled();
+}
+
#endif
diff --git a/security/selinux/netlabel.c b/security/selinux/netlabel.c
index d51dfe892312..a6c58b8e7bfd 100644
--- a/security/selinux/netlabel.c
+++ b/security/selinux/netlabel.c
@@ -199,7 +199,7 @@ int selinux_netlbl_skbuff_getsid(struct sk_buff *skb,
int rc;
struct netlbl_lsm_secattr secattr;
- if (!netlbl_enabled()) {
+ if (!selinux_netlbl_enabled()) {
*type = NETLBL_NLTYPE_NONE;
*sid = SECSID_NULL;
return 0;
@@ -444,7 +444,7 @@ int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec,
u32 perm;
struct netlbl_lsm_secattr secattr;
- if (!netlbl_enabled())
+ if (!selinux_netlbl_enabled())
return 0;
netlbl_secattr_init(&secattr);
diff --git a/security/smack/smack.h b/security/smack/smack.h
index 89bf62ad60f1..46e513f27e0a 100644
--- a/security/smack/smack.h
+++ b/security/smack/smack.h
@@ -374,6 +374,11 @@ static inline struct smack_known **smack_key(const struct key *key)
}
#endif /* CONFIG_KEYS */
+static inline bool smack_netlabel(void)
+{
+ return smack_blob_sizes.lbs_netlabel;
+}
+
/*
* Is the directory transmuting?
*/
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index ee86818633c1..4cbdb8c91a07 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -2575,6 +2575,9 @@ static int smack_netlbl_add(struct sock *sk)
struct smack_known *skp = ssp->smk_out;
int rc;
+ if (!smack_netlabel())
+ return 0;
+
local_bh_disable();
bh_lock_sock_nested(sk);
@@ -2606,6 +2609,9 @@ static void smack_netlbl_delete(struct sock *sk)
{
struct socket_smack *ssp = smack_sock(sk);
+ if (!smack_netlabel())
+ return;
+
/*
* Take the label off the socket if one is set.
*/
@@ -2656,7 +2662,7 @@ static int smk_ipv4_check(struct sock *sk, struct sockaddr_in *sap)
/*
* Clear the socket netlabel if it's set.
*/
- if (!rc)
+ if (!rc && smack_netlabel())
smack_netlbl_delete(sk);
}
rcu_read_unlock();
@@ -3982,6 +3988,8 @@ static struct smack_known *smack_from_secattr(struct netlbl_lsm_secattr *sap,
int acat;
int kcat;
+ if (!smack_netlabel())
+ return smack_net_ambient;
/*
* Netlabel found it in the cache.
*/
@@ -4132,6 +4140,9 @@ static struct smack_known *smack_from_netlbl(const struct sock *sk, u16 family,
struct socket_smack *ssp = NULL;
struct smack_known *skp = NULL;
+ if (!smack_netlabel())
+ return NULL;
+
netlbl_secattr_init(&secattr);
if (sk)
@@ -4202,7 +4213,7 @@ static int smack_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
rc = smk_access(skp, ssp->smk_in, MAY_WRITE, &ad);
rc = smk_bu_note("IPv4 delivery", skp, ssp->smk_in,
MAY_WRITE, rc);
- if (rc != 0)
+ if (rc != 0 && smack_netlabel())
netlbl_skbuff_err(skb, family, rc, 0);
break;
#if IS_ENABLED(CONFIG_IPV6)
@@ -4390,7 +4401,7 @@ static int smack_inet_conn_request(const struct sock *sk, struct sk_buff *skb,
if (skp == NULL) {
skp = smack_from_netlbl(sk, family, skb);
if (skp == NULL)
- skp = &smack_known_huh;
+ skp = smack_net_ambient;
}
#ifdef CONFIG_AUDIT
@@ -4411,8 +4422,11 @@ static int smack_inet_conn_request(const struct sock *sk, struct sk_buff *skb,
/*
* Save the peer's label in the request_sock so we can later setup
* smk_packet in the child socket so that SO_PEERCRED can report it.
+ *
+ * Only do this if Smack is using netlabel.
*/
- req->peer_secid = skp->smk_secid;
+ if (smack_netlabel())
+ req->peer_secid = skp->smk_secid;
/*
* We need to decide if we want to label the incoming connection here
@@ -4425,10 +4439,13 @@ static int smack_inet_conn_request(const struct sock *sk, struct sk_buff *skb,
hskp = smack_ipv4host_label(&addr);
rcu_read_unlock();
- if (hskp == NULL)
- rc = netlbl_req_setattr(req, &ssp->smk_out->smk_netlabel);
- else
- netlbl_req_delattr(req);
+ if (smack_netlabel()) {
+ if (hskp == NULL)
+ rc = netlbl_req_setattr(req,
+ &ssp->smk_out->smk_netlabel);
+ else
+ netlbl_req_delattr(req);
+ }
return rc;
}
@@ -4446,7 +4463,7 @@ static void smack_inet_csk_clone(struct sock *sk,
struct socket_smack *ssp = smack_sock(sk);
struct smack_known *skp;
- if (req->peer_secid != 0) {
+ if (smack_netlabel() && req->peer_secid != 0) {
skp = smack_from_secid(req->peer_secid);
ssp->smk_packet = skp;
} else
@@ -5031,6 +5048,7 @@ struct lsm_blob_sizes smack_blob_sizes __ro_after_init = {
.lbs_superblock = sizeof(struct superblock_smack),
.lbs_xattr_count = SMACK_INODE_INIT_XATTRS,
.lbs_secmark = true,
+ .lbs_netlabel = true,
};
static const struct lsm_id smack_lsmid = {
diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c
index b1e5e62f5cbd..b2487f676e0a 100644
--- a/security/smack/smackfs.c
+++ b/security/smack/smackfs.c
@@ -79,7 +79,7 @@ static DEFINE_MUTEX(smk_net6addr_lock);
* If it isn't somehow marked, use this.
* It can be reset via smackfs/ambient
*/
-struct smack_known *smack_net_ambient;
+struct smack_known *smack_net_ambient = &smack_known_floor;
/*
* This is the level in a CIPSO header that indicates a
@@ -671,6 +671,9 @@ static void smk_cipso_doi(void)
struct cipso_v4_doi *doip;
struct netlbl_audit nai;
+ if (!smack_netlabel())
+ return;
+
smk_netlabel_audit_set(&nai);
rc = netlbl_cfg_map_del(NULL, PF_INET, NULL, NULL, &nai);
@@ -711,6 +714,9 @@ static void smk_unlbl_ambient(char *oldambient)
int rc;
struct netlbl_audit nai;
+ if (!smack_netlabel())
+ return;
+
smk_netlabel_audit_set(&nai);
if (oldambient != NULL) {
@@ -834,6 +840,8 @@ static ssize_t smk_set_cipso(struct file *file, const char __user *buf,
*/
if (!smack_privileged(CAP_MAC_ADMIN))
return -EPERM;
+ if (!smack_netlabel())
+ return -EINVAL;
if (*ppos != 0)
return -EINVAL;
if (format == SMK_FIXED24_FMT &&
@@ -1156,6 +1164,8 @@ static ssize_t smk_write_net4addr(struct file *file, const char __user *buf,
*/
if (!smack_privileged(CAP_MAC_ADMIN))
return -EPERM;
+ if (!smack_netlabel())
+ return -EINVAL;
if (*ppos != 0)
return -EINVAL;
if (count < SMK_NETLBLADDRMIN || count > PAGE_SIZE - 1)
@@ -1414,6 +1424,8 @@ static ssize_t smk_write_net6addr(struct file *file, const char __user *buf,
*/
if (!smack_privileged(CAP_MAC_ADMIN))
return -EPERM;
+ if (!smack_netlabel())
+ return -EINVAL;
if (*ppos != 0)
return -EINVAL;
if (count < SMK_NETLBLADDRMIN || count > PAGE_SIZE - 1)
@@ -1585,6 +1597,8 @@ static ssize_t smk_write_doi(struct file *file, const char __user *buf,
if (!smack_privileged(CAP_MAC_ADMIN))
return -EPERM;
+ if (!smack_netlabel())
+ return -EINVAL;
if (count >= sizeof(temp) || count == 0)
return -EINVAL;
@@ -1652,6 +1666,8 @@ static ssize_t smk_write_direct(struct file *file, const char __user *buf,
if (!smack_privileged(CAP_MAC_ADMIN))
return -EPERM;
+ if (!smack_netlabel())
+ return -EINVAL;
if (count >= sizeof(temp) || count == 0)
return -EINVAL;
@@ -1730,6 +1746,8 @@ static ssize_t smk_write_mapped(struct file *file, const char __user *buf,
if (!smack_privileged(CAP_MAC_ADMIN))
return -EPERM;
+ if (!smack_netlabel())
+ return -EINVAL;
if (count >= sizeof(temp) || count == 0)
return -EINVAL;
--
2.51.0
^ permalink raw reply related
* [PATCH 1/2] LSM: Exclusive secmark usage
From: Casey Schaufler @ 2025-10-01 21:56 UTC (permalink / raw)
To: casey, paul, linux-security-module
Cc: jmorris, serge, keescook, john.johansen, penguin-kernel,
stephen.smalley.work, linux-kernel, selinux
In-Reply-To: <20251001215643.31465-1-casey@schaufler-ca.com>
The network secmark can only be used by one security module
at a time. Establish mechanism to identify to security modules
whether they have access to the secmark. SELinux already
incorparates mechanism, but it has to be added to Smack and
AppArmor.
Signed-off-by: Casey Schaufler <casey@schaufler-ca.com>
---
include/linux/lsm_hooks.h | 1 +
security/apparmor/include/net.h | 5 +++++
security/apparmor/lsm.c | 7 ++++---
security/security.c | 6 ++++++
security/selinux/hooks.c | 4 +++-
security/smack/smack.h | 5 +++++
security/smack/smack_lsm.c | 3 ++-
security/smack/smack_netfilter.c | 10 ++++++++--
8 files changed, 34 insertions(+), 7 deletions(-)
diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
index 090d1d3e19fe..69c1b509577a 100644
--- a/include/linux/lsm_hooks.h
+++ b/include/linux/lsm_hooks.h
@@ -116,6 +116,7 @@ struct lsm_blob_sizes {
int lbs_xattr_count; /* number of xattr slots in new_xattrs array */
int lbs_tun_dev;
int lbs_bdev;
+ bool lbs_secmark; /* expressed desire for secmark use */
};
/*
diff --git a/security/apparmor/include/net.h b/security/apparmor/include/net.h
index 0d0b0ce42723..1199918448a9 100644
--- a/security/apparmor/include/net.h
+++ b/security/apparmor/include/net.h
@@ -52,6 +52,11 @@ struct aa_sk_ctx {
struct aa_label __rcu *peer_lastupdate; /* ptr cmp only, no deref */
};
+static inline bool aa_secmark(void)
+{
+ return apparmor_blob_sizes.lbs_secmark;
+}
+
static inline struct aa_sk_ctx *aa_sock(const struct sock *sk)
{
return sk->sk_security + apparmor_blob_sizes.lbs_sock;
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
index 8e1cc229b41b..34eac7da80e6 100644
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -1512,7 +1512,7 @@ static int apparmor_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
struct aa_sk_ctx *ctx = aa_sock(sk);
int error;
- if (!skb->secmark)
+ if (!aa_secmark() || !skb->secmark)
return 0;
/*
@@ -1641,7 +1641,7 @@ static int apparmor_inet_conn_request(const struct sock *sk, struct sk_buff *skb
struct aa_sk_ctx *ctx = aa_sock(sk);
int error;
- if (!skb->secmark)
+ if (!aa_secmark() || !skb->secmark)
return 0;
rcu_read_lock();
@@ -1661,6 +1661,7 @@ struct lsm_blob_sizes apparmor_blob_sizes __ro_after_init = {
.lbs_file = sizeof(struct aa_file_ctx),
.lbs_task = sizeof(struct aa_task_ctx),
.lbs_sock = sizeof(struct aa_sk_ctx),
+ .lbs_secmark = true,
};
static const struct lsm_id apparmor_lsmid = {
@@ -2360,7 +2361,7 @@ static unsigned int apparmor_ip_postroute(void *priv,
struct sock *sk;
int error;
- if (!skb->secmark)
+ if (!aa_secmark() || !skb->secmark)
return NF_ACCEPT;
sk = skb_to_full_sk(skb);
diff --git a/security/security.c b/security/security.c
index ad163f06bf7a..e59e3d403de6 100644
--- a/security/security.c
+++ b/security/security.c
@@ -283,6 +283,12 @@ static void __init lsm_set_blob_sizes(struct lsm_blob_sizes *needed)
lsm_set_blob_size(&needed->lbs_xattr_count,
&blob_sizes.lbs_xattr_count);
lsm_set_blob_size(&needed->lbs_bdev, &blob_sizes.lbs_bdev);
+ if (needed->lbs_secmark) {
+ if (blob_sizes.lbs_secmark)
+ needed->lbs_secmark = false;
+ else
+ blob_sizes.lbs_secmark = true;
+ }
}
/* Prepare LSM for initialization. */
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index c95a5874bf7d..5b6db7d8effb 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -164,7 +164,8 @@ __setup("checkreqprot=", checkreqprot_setup);
*/
static int selinux_secmark_enabled(void)
{
- return (selinux_policycap_alwaysnetwork() ||
+ return selinux_blob_sizes.lbs_secmark &&
+ (selinux_policycap_alwaysnetwork() ||
atomic_read(&selinux_secmark_refcount));
}
@@ -7183,6 +7184,7 @@ struct lsm_blob_sizes selinux_blob_sizes __ro_after_init = {
.lbs_xattr_count = SELINUX_INODE_INIT_XATTRS,
.lbs_tun_dev = sizeof(struct tun_security_struct),
.lbs_ib = sizeof(struct ib_security_struct),
+ .lbs_secmark = true,
};
#ifdef CONFIG_PERF_EVENTS
diff --git a/security/smack/smack.h b/security/smack/smack.h
index bf6a6ed3946c..89bf62ad60f1 100644
--- a/security/smack/smack.h
+++ b/security/smack/smack.h
@@ -383,6 +383,11 @@ static inline int smk_inode_transmutable(const struct inode *isp)
return (sip->smk_flags & SMK_INODE_TRANSMUTE) != 0;
}
+static inline bool smack_secmark(void)
+{
+ return smack_blob_sizes.lbs_secmark;
+}
+
/*
* Present a pointer to the smack label entry in an inode blob.
*/
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index fc340a6f0dde..ee86818633c1 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -4102,7 +4102,7 @@ static int smk_skb_to_addr_ipv6(struct sk_buff *skb, struct sockaddr_in6 *sip)
#ifdef CONFIG_NETWORK_SECMARK
static struct smack_known *smack_from_skb(struct sk_buff *skb)
{
- if (skb == NULL || skb->secmark == 0)
+ if (!smack_secmark() || skb == NULL || skb->secmark == 0)
return NULL;
return smack_from_secid(skb->secmark);
@@ -5030,6 +5030,7 @@ struct lsm_blob_sizes smack_blob_sizes __ro_after_init = {
.lbs_sock = sizeof(struct socket_smack),
.lbs_superblock = sizeof(struct superblock_smack),
.lbs_xattr_count = SMACK_INODE_INIT_XATTRS,
+ .lbs_secmark = true,
};
static const struct lsm_id smack_lsmid = {
diff --git a/security/smack/smack_netfilter.c b/security/smack/smack_netfilter.c
index 8fd747b3653a..2e82051b3998 100644
--- a/security/smack/smack_netfilter.c
+++ b/security/smack/smack_netfilter.c
@@ -26,7 +26,7 @@ static unsigned int smack_ip_output(void *priv,
struct socket_smack *ssp;
struct smack_known *skp;
- if (sk) {
+ if (smack_secmark() && sk) {
ssp = smack_sock(sk);
skp = ssp->smk_out;
skb->secmark = skp->smk_secid;
@@ -54,12 +54,18 @@ static const struct nf_hook_ops smack_nf_ops[] = {
static int __net_init smack_nf_register(struct net *net)
{
+ if (!smack_secmark())
+ return 0;
+
return nf_register_net_hooks(net, smack_nf_ops,
ARRAY_SIZE(smack_nf_ops));
}
static void __net_exit smack_nf_unregister(struct net *net)
{
+ if (!smack_secmark())
+ return;
+
nf_unregister_net_hooks(net, smack_nf_ops, ARRAY_SIZE(smack_nf_ops));
}
@@ -70,7 +76,7 @@ static struct pernet_operations smack_net_ops = {
static int __init smack_nf_ip_init(void)
{
- if (smack_enabled == 0)
+ if (smack_enabled == 0 || !smack_secmark())
return 0;
printk(KERN_DEBUG "Smack: Registering netfilter hooks\n");
--
2.51.0
^ permalink raw reply related
* Re: [PATCH bpf-next v2 0/3] BPF signature hash chains
From: KP Singh @ 2025-10-02 13:48 UTC (permalink / raw)
To: Paul Moore
Cc: Linus Torvalds, Blaise Boscaccy, ast, james.bottomley, bpf,
linux-security-module, kys, daniel, andrii, wufan, qmo
In-Reply-To: <CAHC9VhTQ_DR=ANzoDBjcCtrimV7XcCZVUsANPt=TjcvM4d-vjg@mail.gmail.com>
On Wed, Oct 1, 2025 at 11:37 PM Paul Moore <paul@paul-moore.com> wrote:
>
[...]
> With the lack of engagement from the BPF devs, I'm now at the point
> where I'm asking Linus to comment on the current situation around
The lack of engagement is because Blaise has repeatedly sent patches
and ignored maintainer feedback and continued pushing a broken
approach. The community, in fact, prioritized the signing work to
unblock your use-case.
> Blaise's patchset. I recognize that Alexei and KP have a strong
> affinity for the signature scheme implemented in KP's patchset, which
> is fine, but if we are going to be serious about implementing BPF
> signature verification that can be used by a number of different user
> groups, we also need to support the signature scheme that Blaise is
> proposing[6].
>
Blaise's implementation fails on any modern BPF programs since
programs use more than one map, BTF information and kernel functions.
> To make it clear at the start, Blaise's patchset does not change,
> block, or otherwise prevent the BPF program signature scheme
> implemented in KP's patchset. Blaise intentionally designed his
> patches such that the two signature schemes can coexist together in
We cannot have multiple signature schemes, this is not the experience
we want for BPF users.
> the same kernel, allowing users to select which scheme to use on each
> BPF program load, enabling the kernel to support policy to selectively
> enforce rules around which signature scheme is permitted for each BPF
> program load.
>
> Blaise's patch implements an alternate BPF program signature scheme,
> using the same basic concepts as KP's design (light skeletons, hash
> chaining, etc.), but does so in such a way that the kernel verifies
> all relevant parts of the BPF program load prior to calling the
> security_bpf_prog_load() LSM hook. KP's signature scheme only
> verifies the light skeleton prior to calling the LSM hook and relies
You are confusing a light skeleton from a loader program. Loader
programs are independent of light skeletons.
> on the BPF code in the light skeleton to verify the original BPF
> program.
>
> Relying on a light skeleton to verify the BPF program means that any
> verification failures in the light skeleton will be "lost" as there is
> no way to convey an error code back to the user who is attempting the
This is not correct, the error is propagated back if the loader program fails.
> BPF program load. Blaise's approach to verifying the full signature
> in the kernel, and not relying on the light skeleton for verification,
> means that verification failures can be returned to the user; there
> are no silent signature verification failures like one can experience
> with KP's verification scheme.
>
> KP's signature verification scheme is a two-part scheme with the
> security_bpf_prog_load() LSM hook being called after the light
> skeleton signature has been verified, but before the light skeleton
> verifies the BPF program. Aside from breaking with typical
Again you mean, loader BPF program. Skeletons are just for
convenience. You don't have to use them. libbpf provides an
implementation to easily generate loader programs, but you don't have
to use that either.
> conventions around the placement of LSM hooks, this "halfway" approach
> makes it difficult for LSMs to log anything about the signature status
> of a BPF program being loaded, or use the signature status in any type
> of access decision. This is important for a number of user groups
> that use LSM based security policies as a way to help reason about the
> security properties of a system, as KP's scheme would require the
> users to analyze the signature verification code in every BPF light
> skeleton they authorize as well as the LSM policy in order to reason
> about the security mechanisms involved in BPF program loading.
>
> Blaise's signature scheme also has the nice property that BPF ELF
> objects created using his scheme are backwards compatible with
> existing released kernels that do not support any BPF signature
> verification schemes, of course without any signature verification.
> Loading a BPF ELF object using KP's signature scheme will likely fail
> when loaded on existing released kernels.
This does not make any sense. The ELF format and the way loaders like
libbpf interpret it, has nothing to do with the kernel or UAPI.
I had given detailed feedback to Blaise in
https://lore.kernel.org/bpf/CACYkzJ6yNjFOTzC04uOuCmFn=+51_ie2tB9_x-u2xbcO=yobTw@mail.gmail.com/
mentions also why we don't want any additional UAPI.
You keep mentioning having visibility in the LSM code and I again
ask, to implement what specific security policy and there is no clear
answer? On a system where you would like to only allow signed BPF
programs, you can purely deny any programs where the signature is not
provided and this can be implemented today.
Stable programs work as it is, programs that require runtime
relocation work with loader programs. We don't want to add more UAPI
as, in the future, it's quite possible that we can make the
instruction buffer stable.
- KP
>
> [1] https://lore.kernel.org/linux-security-module/CAADnVQ+C2KNR1ryRtBGOZTNk961pF+30FnU9n3dt3QjaQu_N6Q@mail.gmail.com/
> [2] https://lore.kernel.org/linux-security-module/CAHC9VhRjKV4AbSgqb4J_-xhkWAp_VAcKDfLJ4GwhBNPOr+cvpg@mail.gmail.com/
> [3] https://lore.kernel.org/linux-security-module/87sei58vy3.fsf@microsoft.com/
> [4] https://lore.kernel.org/linux-security-module/20250909162345.569889-2-bboscaccy@linux.microsoft.com/
> [5] https://lore.kernel.org/linux-security-module/20250926203111.1305999-1-bboscaccy@linux.microsoft.com/
> [6] https://lore.kernel.org/linux-security-module/20250929213520.1821223-1-bboscaccy@linux.microsoft.com/
>
> --
> paul-moore.com
^ permalink raw reply
* Re: [PATCH] ima: Fall back to default kernel module signature verification
From: kernel test robot @ 2025-10-02 17:17 UTC (permalink / raw)
To: Coiby Xu, linux-integrity
Cc: oe-kbuild-all, Dmitry Torokhov, Karel Srot, Mimi Zohar,
Roberto Sassu, Dmitry Kasatkin, Eric Snowberg, Paul Moore,
James Morris, Serge E. Hallyn, linux-security-module,
linux-kernel
In-Reply-To: <20250928030358.3873311-1-coxu@redhat.com>
Hi Coiby,
kernel test robot noticed the following build errors:
[auto build test ERROR on cec1e6e5d1ab33403b809f79cd20d6aff124ccfe]
url: https://github.com/intel-lab-lkp/linux/commits/Coiby-Xu/ima-Fall-back-to-default-kernel-module-signature-verification/20250928-110501
base: cec1e6e5d1ab33403b809f79cd20d6aff124ccfe
patch link: https://lore.kernel.org/r/20250928030358.3873311-1-coxu%40redhat.com
patch subject: [PATCH] ima: Fall back to default kernel module signature verification
config: i386-randconfig-012-20251002 (https://download.01.org/0day-ci/archive/20251003/202510030029.VRKgik99-lkp@intel.com/config)
compiler: gcc-14 (Debian 14.2.0-19) 14.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20251003/202510030029.VRKgik99-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202510030029.VRKgik99-lkp@intel.com/
All errors (new ones prefixed by >>):
ld: security/integrity/ima/ima_appraise.o: in function `ima_appraise_measurement':
>> security/integrity/ima/ima_appraise.c:587:(.text+0xbbb): undefined reference to `set_module_sig_enforced'
vim +587 security/integrity/ima/ima_appraise.c
483
484 /*
485 * ima_appraise_measurement - appraise file measurement
486 *
487 * Call evm_verifyxattr() to verify the integrity of 'security.ima'.
488 * Assuming success, compare the xattr hash with the collected measurement.
489 *
490 * Return 0 on success, error code otherwise
491 */
492 int ima_appraise_measurement(enum ima_hooks func, struct ima_iint_cache *iint,
493 struct file *file, const unsigned char *filename,
494 struct evm_ima_xattr_data *xattr_value,
495 int xattr_len, const struct modsig *modsig)
496 {
497 static const char op[] = "appraise_data";
498 int audit_msgno = AUDIT_INTEGRITY_DATA;
499 const char *cause = "unknown";
500 struct dentry *dentry = file_dentry(file);
501 struct inode *inode = d_backing_inode(dentry);
502 enum integrity_status status = INTEGRITY_UNKNOWN;
503 int rc = xattr_len;
504 bool try_modsig = iint->flags & IMA_MODSIG_ALLOWED && modsig;
505 bool enforce_module_sig = iint->flags & IMA_DIGSIG_REQUIRED && func == MODULE_CHECK;
506
507 /* If not appraising a modsig or using default module verification, we need an xattr. */
508 if (!(inode->i_opflags & IOP_XATTR) && !try_modsig && !enforce_module_sig)
509 return INTEGRITY_UNKNOWN;
510
511 /*
512 * Unlike any of the other LSM hooks where the kernel enforces file
513 * integrity, enforcing file integrity for the bprm_creds_for_exec()
514 * LSM hook with the AT_EXECVE_CHECK flag is left up to the discretion
515 * of the script interpreter(userspace). Differentiate kernel and
516 * userspace enforced integrity audit messages.
517 */
518 if (is_bprm_creds_for_exec(func, file))
519 audit_msgno = AUDIT_INTEGRITY_USERSPACE;
520
521 /* If reading the xattr failed and there's no modsig or module verification, error out. */
522 if (rc <= 0 && !try_modsig && !enforce_module_sig) {
523 if (rc && rc != -ENODATA)
524 goto out;
525
526 if (iint->flags & IMA_DIGSIG_REQUIRED) {
527 if (iint->flags & IMA_VERITY_REQUIRED)
528 cause = "verity-signature-required";
529 else
530 cause = "IMA-signature-required";
531 } else {
532 cause = "missing-hash";
533 }
534
535 status = INTEGRITY_NOLABEL;
536 if (file->f_mode & FMODE_CREATED)
537 iint->flags |= IMA_NEW_FILE;
538 if ((iint->flags & IMA_NEW_FILE) &&
539 (!(iint->flags & IMA_DIGSIG_REQUIRED) ||
540 (inode->i_size == 0)))
541 status = INTEGRITY_PASS;
542 goto out;
543 }
544
545 status = evm_verifyxattr(dentry, XATTR_NAME_IMA, xattr_value,
546 rc < 0 ? 0 : rc);
547 switch (status) {
548 case INTEGRITY_PASS:
549 case INTEGRITY_PASS_IMMUTABLE:
550 case INTEGRITY_UNKNOWN:
551 break;
552 case INTEGRITY_NOXATTRS: /* No EVM protected xattrs. */
553 /* Fine to not have xattrs when using a modsig or default module verification. */
554 if (try_modsig || enforce_module_sig)
555 break;
556 fallthrough;
557 case INTEGRITY_NOLABEL: /* No security.evm xattr. */
558 cause = "missing-HMAC";
559 goto out;
560 case INTEGRITY_FAIL_IMMUTABLE:
561 set_bit(IMA_DIGSIG, &iint->atomic_flags);
562 cause = "invalid-fail-immutable";
563 goto out;
564 case INTEGRITY_FAIL: /* Invalid HMAC/signature. */
565 cause = "invalid-HMAC";
566 goto out;
567 default:
568 WARN_ONCE(true, "Unexpected integrity status %d\n", status);
569 }
570
571 if (xattr_value)
572 rc = xattr_verify(func, iint, xattr_value, xattr_len, &status,
573 &cause);
574
575 /*
576 * If we have a modsig and either no imasig or the imasig's key isn't
577 * known, then try verifying the modsig.
578 */
579 if (try_modsig &&
580 (!xattr_value || xattr_value->type == IMA_XATTR_DIGEST_NG ||
581 rc == -ENOKEY))
582 rc = modsig_verify(func, modsig, &status, &cause);
583
584 /* Fall back to default kernel module signature verification */
585 if (rc && enforce_module_sig) {
586 rc = 0;
> 587 set_module_sig_enforced();
588 /* CONFIG_MODULE_SIG may be disabled */
589 if (is_module_sig_enforced()) {
590 rc = 0;
591 status = INTEGRITY_PASS;
592 pr_debug("Fall back to default kernel module verification for %s\n", filename);
593 }
594 }
595
596 out:
597 /*
598 * File signatures on some filesystems can not be properly verified.
599 * When such filesystems are mounted by an untrusted mounter or on a
600 * system not willing to accept such a risk, fail the file signature
601 * verification.
602 */
603 if ((inode->i_sb->s_iflags & SB_I_IMA_UNVERIFIABLE_SIGNATURE) &&
604 ((inode->i_sb->s_iflags & SB_I_UNTRUSTED_MOUNTER) ||
605 (iint->flags & IMA_FAIL_UNVERIFIABLE_SIGS))) {
606 status = INTEGRITY_FAIL;
607 cause = "unverifiable-signature";
608 integrity_audit_msg(audit_msgno, inode, filename,
609 op, cause, rc, 0);
610 } else if (status != INTEGRITY_PASS) {
611 /* Fix mode, but don't replace file signatures. */
612 if ((ima_appraise & IMA_APPRAISE_FIX) && !try_modsig &&
613 (!xattr_value ||
614 xattr_value->type != EVM_IMA_XATTR_DIGSIG)) {
615 if (!ima_fix_xattr(dentry, iint))
616 status = INTEGRITY_PASS;
617 }
618
619 /*
620 * Permit new files with file/EVM portable signatures, but
621 * without data.
622 */
623 if (inode->i_size == 0 && iint->flags & IMA_NEW_FILE &&
624 test_bit(IMA_DIGSIG, &iint->atomic_flags)) {
625 status = INTEGRITY_PASS;
626 }
627
628 integrity_audit_msg(audit_msgno, inode, filename,
629 op, cause, rc, 0);
630 } else {
631 ima_cache_flags(iint, func);
632 }
633
634 ima_set_cache_status(iint, func, status);
635 return status;
636 }
637
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply
* Re: [PATCH bpf-next v2 0/3] BPF signature hash chains
From: Blaise Boscaccy @ 2025-10-02 20:01 UTC (permalink / raw)
To: KP Singh, Paul Moore
Cc: Linus Torvalds, ast, james.bottomley, bpf, linux-security-module,
kys, daniel, andrii, wufan, qmo
In-Reply-To: <CACYkzJ4yG1d8ujZ8PVzsRr_PWpyr6goD9DezQTu8ydaf-skn6g@mail.gmail.com>
KP Singh <kpsingh@kernel.org> writes:
> On Wed, Oct 1, 2025 at 11:37 PM Paul Moore <paul@paul-moore.com> wrote:
>>
>
> [...]
>
[...]
I am confident that Paul will address your remaining points. However, I
would like to clarify a few factual inaccuracies outlined below.
>
> Blaise's implementation fails on any modern BPF programs since
> programs use more than one map, BTF information and kernel functions.
>
If you read the patch series you'd see that it supports verification of
any number of maps. If you've identified an issue with map verification,
please share the details and I’ll address it.
[...]
>> conventions around the placement of LSM hooks, this "halfway" approach
>> makes it difficult for LSMs to log anything about the signature status
>> of a BPF program being loaded, or use the signature status in any type
>> of access decision. This is important for a number of user groups
>> that use LSM based security policies as a way to help reason about the
>> security properties of a system, as KP's scheme would require the
>> users to analyze the signature verification code in every BPF light
>> skeleton they authorize as well as the LSM policy in order to reason
>> about the security mechanisms involved in BPF program loading.
>>
>> Blaise's signature scheme also has the nice property that BPF ELF
>> objects created using his scheme are backwards compatible with
>> existing released kernels that do not support any BPF signature
>> verification schemes, of course without any signature verification.
>> Loading a BPF ELF object using KP's signature scheme will likely fail
>> when loaded on existing released kernels.
>
> This does not make any sense. The ELF format and the way loaders like
> libbpf interpret it, has nothing to do with the kernel or UAPI.
>
We signed a program with your upstream tools and it failed to load on a
vanilla kernel 6.16. The loader in your patchset is intepreting the
first few fields of struct bpf_map as a byte array containing a sha256
digest on older kernels.
-blaise
> I had given detailed feedback to Blaise in
> https://lore.kernel.org/bpf/CACYkzJ6yNjFOTzC04uOuCmFn=+51_ie2tB9_x-u2xbcO=yobTw@mail.gmail.com/
> mentions also why we don't want any additional UAPI.
>
> You keep mentioning having visibility in the LSM code and I again
> ask, to implement what specific security policy and there is no clear
> answer? On a system where you would like to only allow signed BPF
> programs, you can purely deny any programs where the signature is not
> provided and this can be implemented today.
>
> Stable programs work as it is, programs that require runtime
> relocation work with loader programs. We don't want to add more UAPI
> as, in the future, it's quite possible that we can make the
> instruction buffer stable.
>
> - KP
>
>>
>> [1] https://lore.kernel.org/linux-security-module/CAADnVQ+C2KNR1ryRtBGOZTNk961pF+30FnU9n3dt3QjaQu_N6Q@mail.gmail.com/
>> [2] https://lore.kernel.org/linux-security-module/CAHC9VhRjKV4AbSgqb4J_-xhkWAp_VAcKDfLJ4GwhBNPOr+cvpg@mail.gmail.com/
>> [3] https://lore.kernel.org/linux-security-module/87sei58vy3.fsf@microsoft.com/
>> [4] https://lore.kernel.org/linux-security-module/20250909162345.569889-2-bboscaccy@linux.microsoft.com/
>> [5] https://lore.kernel.org/linux-security-module/20250926203111.1305999-1-bboscaccy@linux.microsoft.com/
>> [6] https://lore.kernel.org/linux-security-module/20250929213520.1821223-1-bboscaccy@linux.microsoft.com/
>>
>> --
>> paul-moore.com
^ permalink raw reply
* Re: [PATCH bpf-next v2 0/3] BPF signature hash chains
From: Paul Moore @ 2025-10-03 2:35 UTC (permalink / raw)
To: KP Singh
Cc: Linus Torvalds, Blaise Boscaccy, ast, james.bottomley, bpf,
linux-security-module, kys, daniel, andrii, wufan, qmo
In-Reply-To: <CACYkzJ4yG1d8ujZ8PVzsRr_PWpyr6goD9DezQTu8ydaf-skn6g@mail.gmail.com>
On Thu, Oct 2, 2025 at 9:48 AM KP Singh <kpsingh@kernel.org> wrote:
> On Wed, Oct 1, 2025 at 11:37 PM Paul Moore <paul@paul-moore.com> wrote:
> >
> > With the lack of engagement from the BPF devs, I'm now at the point
> > where I'm asking Linus to comment on the current situation around
>
> The lack of engagement is because Blaise has repeatedly sent patches
> and ignored maintainer feedback and continued pushing a broken
> approach.
I'm sorry you feel that way, but that simply does not appear to be the
case. Looking at the archives from this year I see that Blase has
proposed three different approaches[7][8][9] to verifying signed BPF
programs, with each new approach a result of the feedback received.
> The community, in fact, prioritized the signing work to unblock your use-case.
As mentioned previously, many times over, while your signature scheme
may satisfy your own
requirements, it does not provide a workable solution for use cases
that have more stringent security requirements. Blaise's latest
approach, a small patch on top of your patchset, is an attempt to
bridge that divide.
> Blaise's implementation ...
I think Blaise's response addressed your other comments rather well so
I'll just skip over those points.
> > To make it clear at the start, Blaise's patchset does not change,
> > block, or otherwise prevent the BPF program signature scheme
> > implemented in KP's patchset. Blaise intentionally designed his
> > patches such that the two signature schemes can coexist together in
>
> We cannot have multiple signature schemes, this is not the experience
> we want for BPF users.
In a perfect world there would be a singular BPF signature scheme
which would satisfy all the different use cases, but the sad reality
is that your signature scheme which Alexei sent to Linus during this
merge window falls short of that goal. Blaise's patch is an attempt
to provide a solution for the BPF use cases that are not sufficiently
addressed by your signature scheme.
> > Relying on a light skeleton to verify the BPF program means that any
> > verification failures in the light skeleton will be "lost" as there is
> > no way to convey an error code back to the user who is attempting the
>
> This is not correct, the error is propagated back if the loader program fails.
The loader BPF program which verifies the original BPF program, stored
as a map as part of the light skeleton, is not executed as part of the
original bpf() syscall issued from userspace. The loader BPF program,
and its verification code, is executed during a subsequent call. It
is possible for the PKCS7 signature on the loader to pass, with the
kernel reporting a successful program load, the LSM authorizing the
load based on a good signature, and audit recording a successful
signature verification yet the loader could still fail the integrity
check on the original BPF program, leaving the system with a false
positive on the BPF program load and a "questionable" audit trail.
> You keep mentioning having visibility in the LSM code and I again
> ask, to implement what specific security policy and there is no clear
> answer?
No one policy can satisfy the different security requirements of all
known users, simply look at all of the LSMs (including the BPF LSM)
which support different security policies as a real world example of
this. Even the presence of the LSM framework as an abstract layer is
an admission that no one policy, or model, solves all problems.
Instead, the goal is to ensure we have mechanisms in place which are
flexible enough to support a number of different policies and models.
[7] https://lore.kernel.org/all/20250109214617.485144-1-bboscaccy@linux.microsoft.com/
[8] https://lore.kernel.org/linux-security-module/20250321164537.16719-1-bboscaccy@linux.microsoft.com/
[9] https://lore.kernel.org/linux-security-module/87sei58vy3.fsf@microsoft.com/
--
paul-moore.com
^ permalink raw reply
* [PATCH] include/uapi/linux/lsm.h,Documentation/userspace-api/lsm.rst: introduce LSM_ATTR_UNSHARE
From: Stephen Smalley @ 2025-10-03 13:20 UTC (permalink / raw)
To: paul
Cc: linux-security-module, selinux, john.johansen, casey, serge,
corbet, jmorris, linux-doc, Stephen Smalley
This defines a new LSM_ATTR_UNSHARE attribute for the
lsm_set_self_attr(2) and lsm_get_self_attr(2) system calls. When
passed to lsm_set_self_attr(2), the LSM-specific namespace for the
specified LSM id is immediately unshared in a similar manner to the
unshare(2) system call for other Linux namespaces. When passed to
lsm_get_self_attr(2), the return value is a boolean (0 or 1) that
indicates whether the LSM-specific namespace for the specified LSM id
has been unshared and not yet fully initialized (e.g. no policy yet
loaded).
Link: https://lore.kernel.org/selinux/20250918135904.9997-2-stephen.smalley.work@gmail.com/
Link: https://lore.kernel.org/selinux/CAHC9VhRGMmhxbajwQNfGFy+ZFF1uN=UEBjqQZQ4UBy7yds3eVQ@mail.gmail.com/
Signed-off-by: Stephen Smalley <stephen.smalley.work@gmail.com>
---
Documentation/userspace-api/lsm.rst | 9 +++++++++
include/uapi/linux/lsm.h | 1 +
2 files changed, 10 insertions(+)
diff --git a/Documentation/userspace-api/lsm.rst b/Documentation/userspace-api/lsm.rst
index a76da373841b..93638c1e275a 100644
--- a/Documentation/userspace-api/lsm.rst
+++ b/Documentation/userspace-api/lsm.rst
@@ -48,6 +48,15 @@ creating socket objects.
The proc filesystem provides this value in ``/proc/self/attr/sockcreate``.
This is supported by the SELinux security module.
+``LSM_ATTR_UNSHARE`` is used to unshare the LSM-specific namespace for
+the process. When passed to ``lsm_set_self_attr(2)``, the LSM-specific
+namespace for the specified LSM id is immediately unshared
+in a similar manner to the ``unshare(2)`` system call for other
+Linux namespaces. When passed to ``lsm_get_self_attr(2)``,
+the return value is a boolean (0 or 1) that indicates whether the
+LSM-specific namespace for the specified LSM id has been unshared
+and not yet fully initialized (e.g. no policy yet loaded).
+
Kernel interface
================
diff --git a/include/uapi/linux/lsm.h b/include/uapi/linux/lsm.h
index 938593dfd5da..fb1b4a8aa639 100644
--- a/include/uapi/linux/lsm.h
+++ b/include/uapi/linux/lsm.h
@@ -83,6 +83,7 @@ struct lsm_ctx {
#define LSM_ATTR_KEYCREATE 103
#define LSM_ATTR_PREV 104
#define LSM_ATTR_SOCKCREATE 105
+#define LSM_ATTR_UNSHARE 106
/*
* LSM_FLAG_XXX definitions identify special handling instructions
--
2.51.0
^ permalink raw reply related
* Re: [PATCH bpf-next v2 0/3] BPF signature hash chains
From: KP Singh @ 2025-10-03 16:24 UTC (permalink / raw)
To: Paul Moore
Cc: Linus Torvalds, Blaise Boscaccy, ast, james.bottomley, bpf,
linux-security-module, kys, daniel, andrii, wufan, qmo
In-Reply-To: <CAHC9VhR2Ab8Rw8RBm9je9-Ss++wufstxh4fB3zrZXnBoZpSi_Q@mail.gmail.com>
On Fri, Oct 3, 2025 at 4:36 AM Paul Moore <paul@paul-moore.com> wrote:
>
> On Thu, Oct 2, 2025 at 9:48 AM KP Singh <kpsingh@kernel.org> wrote:
> > On Wed, Oct 1, 2025 at 11:37 PM Paul Moore <paul@paul-moore.com> wrote:
> > >
> > > With the lack of engagement from the BPF devs, I'm now at the point
> > > where I'm asking Linus to comment on the current situation around
> >
> > The lack of engagement is because Blaise has repeatedly sent patches
> > and ignored maintainer feedback and continued pushing a broken
> > approach.
>
> I'm sorry you feel that way, but that simply does not appear to be the
> case. Looking at the archives from this year I see that Blase has
> proposed three different approaches[7][8][9] to verifying signed BPF
> programs, with each new approach a result of the feedback received.
>
> > The community, in fact, prioritized the signing work to unblock your use-case.
>
> As mentioned previously, many times over, while your signature scheme
> may satisfy your own
> requirements, it does not provide a workable solution for use cases
> that have more stringent security requirements. Blaise's latest
> approach, a small patch on top of your patchset, is an attempt to
> bridge that divide.
>
> > Blaise's implementation ...
>
> I think Blaise's response addressed your other comments rather well so
> I'll just skip over those points.
>
> > > To make it clear at the start, Blaise's patchset does not change,
> > > block, or otherwise prevent the BPF program signature scheme
> > > implemented in KP's patchset. Blaise intentionally designed his
> > > patches such that the two signature schemes can coexist together in
> >
> > We cannot have multiple signature schemes, this is not the experience
> > we want for BPF users.
>
> In a perfect world there would be a singular BPF signature scheme
> which would satisfy all the different use cases, but the sad reality
> is that your signature scheme which Alexei sent to Linus during this
> merge window falls short of that goal. Blaise's patch is an attempt
> to provide a solution for the BPF use cases that are not sufficiently
> addressed by your signature scheme.
I am failing to understand your security requirements.
>
> > > Relying on a light skeleton to verify the BPF program means that any
> > > verification failures in the light skeleton will be "lost" as there is
> > > no way to convey an error code back to the user who is attempting the
> >
> > This is not correct, the error is propagated back if the loader program fails.
>
> The loader BPF program which verifies the original BPF program, stored
> as a map as part of the light skeleton, is not executed as part of the
> original bpf() syscall issued from userspace. The loader BPF program,
> and its verification code, is executed during a subsequent call. It
> is possible for the PKCS7 signature on the loader to pass, with the
> kernel reporting a successful program load, the LSM authorizing the
> load based on a good signature, and audit recording a successful
> signature verification yet the loader could still fail the integrity
> check on the original BPF program, leaving the system with a false
> positive on the BPF program load and a "questionable" audit trail.
>
> > You keep mentioning having visibility in the LSM code and I again
> > ask, to implement what specific security policy and there is no clear
> > answer?
>
> No one policy can satisfy the different security requirements of all
> known users, simply look at all of the LSMs (including the BPF LSM)
> which support different security policies as a real world example of
> this. Even the presence of the LSM framework as an abstract layer is
> an admission that no one policy, or model, solves all problems.
> Instead, the goal is to ensure we have mechanisms in place which are
> flexible enough to support a number of different policies and models.
Please share concrete policies you would like to implement, this is very vague.
- KP
>
> [7] https://lore.kernel.org/all/20250109214617.485144-1-bboscaccy@linux.microsoft.com/
> [8] https://lore.kernel.org/linux-security-module/20250321164537.16719-1-bboscaccy@linux.microsoft.com/
> [9] https://lore.kernel.org/linux-security-module/87sei58vy3.fsf@microsoft.com/
>
> --
> paul-moore.com
^ permalink raw reply
* Re: [PATCH bpf-next v2 0/3] BPF signature hash chains
From: KP Singh @ 2025-10-03 16:59 UTC (permalink / raw)
To: Blaise Boscaccy
Cc: Paul Moore, Linus Torvalds, ast, james.bottomley, bpf,
linux-security-module, kys, daniel, andrii, wufan, qmo
In-Reply-To: <871pnlysx1.fsf@microsoft.com>
On Thu, Oct 2, 2025 at 10:01 PM Blaise Boscaccy
<bboscaccy@linux.microsoft.com> wrote:
>
> KP Singh <kpsingh@kernel.org> writes:
>
> > On Wed, Oct 1, 2025 at 11:37 PM Paul Moore <paul@paul-moore.com> wrote:
> >>
> >
> > [...]
> >
>
> [...]
>
> I am confident that Paul will address your remaining points. However, I
> would like to clarify a few factual inaccuracies outlined below.
>
> >
> > Blaise's implementation fails on any modern BPF programs since
> > programs use more than one map, BTF information and kernel functions.
> >
>
> If you read the patch series you'd see that it supports verification of
> any number of maps. If you've identified an issue with map verification,
> please share the details and I’ll address it.
>
I am sorry but this does not work, the UAPI here is
+ /* Pointer to a buffer containing the maps used in the signature
+ * hash chain of the BPF program.
+ */
+ __aligned_u64 signature_maps;
+ /* Size of the signature maps buffer. */
+ __u32 signature_maps_size;
This needs to be generically applicable and it's not, What happens if
the program is not a loader program / when the instruction buffer is
stable?
If you really want the property that all of the content is signed and
verified within the kernel, please explore approaches to make the
instruction buffer stable or feel free to deny any programs that do
relocations at load time for whatever "strict" security policy that
you want to implement.
Please stop pursuing this extension as it adds cruft to the UAPI
that's too specific, encodes the hash chain in the kernel and we won't
need in the future.
> [...]
>
> >> conventions around the placement of LSM hooks, this "halfway" approach
> >> makes it difficult for LSMs to log anything about the signature status
> >> of a BPF program being loaded, or use the signature status in any type
> >> of access decision. This is important for a number of user groups
> >> that use LSM based security policies as a way to help reason about the
> >> security properties of a system, as KP's scheme would require the
> >> users to analyze the signature verification code in every BPF light
> >> skeleton they authorize as well as the LSM policy in order to reason
> >> about the security mechanisms involved in BPF program loading.
> >>
> >> Blaise's signature scheme also has the nice property that BPF ELF
> >> objects created using his scheme are backwards compatible with
> >> existing released kernels that do not support any BPF signature
> >> verification schemes, of course without any signature verification.
> >> Loading a BPF ELF object using KP's signature scheme will likely fail
> >> when loaded on existing released kernels.
> >
> > This does not make any sense. The ELF format and the way loaders like
> > libbpf interpret it, has nothing to do with the kernel or UAPI.
> >
>
> We signed a program with your upstream tools and it failed to load on a
> vanilla kernel 6.16. The loader in your patchset is intepreting the
> first few fields of struct bpf_map as a byte array containing a sha256
> digest on older kernels.
We can convert BPF_OBJ_GET_INFO_BY_FD to be called from loader
programs to not rely on the struct field. and or libbbf can call
BPF_OBJ_GET_INFO_BY_FD to check if map_get_hash is supported before it
generates the hash check.
You should not expect bpftool -S -k -i to work on older kernels but it
should error out if the options are passed.
- KP
>
> -blaise
>
>
> > I had given detailed feedback to Blaise in
> > https://lore.kernel.org/bpf/CACYkzJ6yNjFOTzC04uOuCmFn=+51_ie2tB9_x-u2xbcO=yobTw@mail.gmail.com/
> > mentions also why we don't want any additional UAPI.
> >
> > You keep mentioning having visibility in the LSM code and I again
> > ask, to implement what specific security policy and there is no clear
> > answer? On a system where you would like to only allow signed BPF
> > programs, you can purely deny any programs where the signature is not
> > provided and this can be implemented today.
> >
> > Stable programs work as it is, programs that require runtime
> > relocation work with loader programs. We don't want to add more UAPI
> > as, in the future, it's quite possible that we can make the
> > instruction buffer stable.
> >
> > - KP
> >
> >>
> >> [1] https://lore.kernel.org/linux-security-module/CAADnVQ+C2KNR1ryRtBGOZTNk961pF+30FnU9n3dt3QjaQu_N6Q@mail.gmail.com/
> >> [2] https://lore.kernel.org/linux-security-module/CAHC9VhRjKV4AbSgqb4J_-xhkWAp_VAcKDfLJ4GwhBNPOr+cvpg@mail.gmail.com/
> >> [3] https://lore.kernel.org/linux-security-module/87sei58vy3.fsf@microsoft.com/
> >> [4] https://lore.kernel.org/linux-security-module/20250909162345.569889-2-bboscaccy@linux.microsoft.com/
> >> [5] https://lore.kernel.org/linux-security-module/20250926203111.1305999-1-bboscaccy@linux.microsoft.com/
> >> [6] https://lore.kernel.org/linux-security-module/20250929213520.1821223-1-bboscaccy@linux.microsoft.com/
> >>
> >> --
> >> paul-moore.com
^ permalink raw reply
* Re: [PATCH] include/uapi/linux/lsm.h,Documentation/userspace-api/lsm.rst: introduce LSM_ATTR_UNSHARE
From: Stephen Smalley @ 2025-10-03 17:15 UTC (permalink / raw)
To: paul
Cc: linux-security-module, selinux, john.johansen, casey, serge,
corbet, jmorris, linux-doc
In-Reply-To: <20251003131959.23057-3-stephen.smalley.work@gmail.com>
On Fri, Oct 3, 2025 at 9:23 AM Stephen Smalley
<stephen.smalley.work@gmail.com> wrote:
>
> This defines a new LSM_ATTR_UNSHARE attribute for the
> lsm_set_self_attr(2) and lsm_get_self_attr(2) system calls. When
> passed to lsm_set_self_attr(2), the LSM-specific namespace for the
> specified LSM id is immediately unshared in a similar manner to the
> unshare(2) system call for other Linux namespaces. When passed to
> lsm_get_self_attr(2), the return value is a boolean (0 or 1) that
> indicates whether the LSM-specific namespace for the specified LSM id
> has been unshared and not yet fully initialized (e.g. no policy yet
> loaded).
Upon implementing the 2nd part for SELinux, it turns out that the
lsm_get_self_attr(2) call can't unambiguously return 0 or 1 due to the
current interface definition, so will be spinning a v2 that instead
sets the *size argument accordingly.
>
> Link: https://lore.kernel.org/selinux/20250918135904.9997-2-stephen.smalley.work@gmail.com/
> Link: https://lore.kernel.org/selinux/CAHC9VhRGMmhxbajwQNfGFy+ZFF1uN=UEBjqQZQ4UBy7yds3eVQ@mail.gmail.com/
>
> Signed-off-by: Stephen Smalley <stephen.smalley.work@gmail.com>
> ---
> Documentation/userspace-api/lsm.rst | 9 +++++++++
> include/uapi/linux/lsm.h | 1 +
> 2 files changed, 10 insertions(+)
>
> diff --git a/Documentation/userspace-api/lsm.rst b/Documentation/userspace-api/lsm.rst
> index a76da373841b..93638c1e275a 100644
> --- a/Documentation/userspace-api/lsm.rst
> +++ b/Documentation/userspace-api/lsm.rst
> @@ -48,6 +48,15 @@ creating socket objects.
> The proc filesystem provides this value in ``/proc/self/attr/sockcreate``.
> This is supported by the SELinux security module.
>
> +``LSM_ATTR_UNSHARE`` is used to unshare the LSM-specific namespace for
> +the process. When passed to ``lsm_set_self_attr(2)``, the LSM-specific
> +namespace for the specified LSM id is immediately unshared
> +in a similar manner to the ``unshare(2)`` system call for other
> +Linux namespaces. When passed to ``lsm_get_self_attr(2)``,
> +the return value is a boolean (0 or 1) that indicates whether the
> +LSM-specific namespace for the specified LSM id has been unshared
> +and not yet fully initialized (e.g. no policy yet loaded).
> +
> Kernel interface
> ================
>
> diff --git a/include/uapi/linux/lsm.h b/include/uapi/linux/lsm.h
> index 938593dfd5da..fb1b4a8aa639 100644
> --- a/include/uapi/linux/lsm.h
> +++ b/include/uapi/linux/lsm.h
> @@ -83,6 +83,7 @@ struct lsm_ctx {
> #define LSM_ATTR_KEYCREATE 103
> #define LSM_ATTR_PREV 104
> #define LSM_ATTR_SOCKCREATE 105
> +#define LSM_ATTR_UNSHARE 106
>
> /*
> * LSM_FLAG_XXX definitions identify special handling instructions
> --
> 2.51.0
>
^ permalink raw reply
* Re: [PATCH bpf-next v2 0/3] BPF signature hash chains
From: Blaise Boscaccy @ 2025-10-03 18:14 UTC (permalink / raw)
To: KP Singh
Cc: Paul Moore, Linus Torvalds, ast, james.bottomley, bpf,
linux-security-module, kys, daniel, andrii, wufan, qmo
In-Reply-To: <CACYkzJ7F6ax2AcWNFxnAkyVb26Dr2mwdBnW=HFVFeJ1pC-BObg@mail.gmail.com>
KP Singh <kpsingh@kernel.org> writes:
> On Thu, Oct 2, 2025 at 10:01 PM Blaise Boscaccy
> <bboscaccy@linux.microsoft.com> wrote:
>>
>> KP Singh <kpsingh@kernel.org> writes:
>>
>> > On Wed, Oct 1, 2025 at 11:37 PM Paul Moore <paul@paul-moore.com> wrote:
>> >>
>> >
>> > [...]
>> >
>>
>> [...]
>>
>> I am confident that Paul will address your remaining points. However, I
>> would like to clarify a few factual inaccuracies outlined below.
>>
>> >
>> > Blaise's implementation fails on any modern BPF programs since
>> > programs use more than one map, BTF information and kernel functions.
>> >
>>
>> If you read the patch series you'd see that it supports verification of
>> any number of maps. If you've identified an issue with map verification,
>> please share the details and I’ll address it.
>>
>
> I am sorry but this does not work, the UAPI here is
>
> + /* Pointer to a buffer containing the maps used in the signature
> + * hash chain of the BPF program.
> + */
> + __aligned_u64 signature_maps;
> + /* Size of the signature maps buffer. */
> + __u32 signature_maps_size;
>
> This needs to be generically applicable and it's not, What happens if
> the program is not a loader program / when the instruction buffer is
> stable?
>
The map array is fully configurable by the signer. Signing any or all
maps is optional. If the instruction buffer is stable, the signer can
generate the signature without maps and the caller passes zero in
those fields.
> If you really want the property that all of the content is signed and
> verified within the kernel,
That's what code signing is.
> please explore approaches to make the
> instruction buffer stable or feel free to deny any programs that do
> relocations at load time for whatever "strict" security policy that
> you want to implement.
>
> Please stop pursuing this extension as it adds cruft to the UAPI
> that's too specific, encodes the hash chain in the kernel and we won't
> need in the future.
>
If your primary complaint at this point is UAPI bloat, we'd be happy to
rework the configurable hash-chain patch to use the existing signature
buffer provided in your patchset.
>> [...]
>>
>> >> conventions around the placement of LSM hooks, this "halfway" approach
>> >> makes it difficult for LSMs to log anything about the signature status
>> >> of a BPF program being loaded, or use the signature status in any type
>> >> of access decision. This is important for a number of user groups
>> >> that use LSM based security policies as a way to help reason about the
>> >> security properties of a system, as KP's scheme would require the
>> >> users to analyze the signature verification code in every BPF light
>> >> skeleton they authorize as well as the LSM policy in order to reason
>> >> about the security mechanisms involved in BPF program loading.
>> >>
>> >> Blaise's signature scheme also has the nice property that BPF ELF
>> >> objects created using his scheme are backwards compatible with
>> >> existing released kernels that do not support any BPF signature
>> >> verification schemes, of course without any signature verification.
>> >> Loading a BPF ELF object using KP's signature scheme will likely fail
>> >> when loaded on existing released kernels.
>> >
>> > This does not make any sense. The ELF format and the way loaders like
>> > libbpf interpret it, has nothing to do with the kernel or UAPI.
>> >
>>
>> We signed a program with your upstream tools and it failed to load on a
>> vanilla kernel 6.16. The loader in your patchset is intepreting the
>> first few fields of struct bpf_map as a byte array containing a sha256
>> digest on older kernels.
>
> We can convert BPF_OBJ_GET_INFO_BY_FD to be called from loader
> programs to not rely on the struct field. and or libbbf can call
> BPF_OBJ_GET_INFO_BY_FD to check if map_get_hash is supported before it
> generates the hash check.
>
> You should not expect bpftool -S -k -i to work on older kernels but it
> should error out if the options are passed.
>
`bpftool gen` shouldn't have a priori knowledge of the target kernel
version.
-blaise
> - KP
>
>>
>> -blaise
>>
>>
>> > I had given detailed feedback to Blaise in
>> > https://lore.kernel.org/bpf/CACYkzJ6yNjFOTzC04uOuCmFn=+51_ie2tB9_x-u2xbcO=yobTw@mail.gmail.com/
>> > mentions also why we don't want any additional UAPI.
>> >
>> > You keep mentioning having visibility in the LSM code and I again
>> > ask, to implement what specific security policy and there is no clear
>> > answer? On a system where you would like to only allow signed BPF
>> > programs, you can purely deny any programs where the signature is not
>> > provided and this can be implemented today.
>> >
>> > Stable programs work as it is, programs that require runtime
>> > relocation work with loader programs. We don't want to add more UAPI
>> > as, in the future, it's quite possible that we can make the
>> > instruction buffer stable.
>> >
>> > - KP
>> >
>> >>
>> >> [1] https://lore.kernel.org/linux-security-module/CAADnVQ+C2KNR1ryRtBGOZTNk961pF+30FnU9n3dt3QjaQu_N6Q@mail.gmail.com/
>> >> [2] https://lore.kernel.org/linux-security-module/CAHC9VhRjKV4AbSgqb4J_-xhkWAp_VAcKDfLJ4GwhBNPOr+cvpg@mail.gmail.com/
>> >> [3] https://lore.kernel.org/linux-security-module/87sei58vy3.fsf@microsoft.com/
>> >> [4] https://lore.kernel.org/linux-security-module/20250909162345.569889-2-bboscaccy@linux.microsoft.com/
>> >> [5] https://lore.kernel.org/linux-security-module/20250926203111.1305999-1-bboscaccy@linux.microsoft.com/
>> >> [6] https://lore.kernel.org/linux-security-module/20250929213520.1821223-1-bboscaccy@linux.microsoft.com/
>> >>
>> >> --
>> >> paul-moore.com
^ permalink raw reply
* Re: [PATCH bpf-next v2 0/3] BPF signature hash chains
From: KP Singh @ 2025-10-03 19:02 UTC (permalink / raw)
To: Blaise Boscaccy
Cc: Paul Moore, Linus Torvalds, ast, james.bottomley, bpf,
linux-security-module, kys, daniel, andrii, wufan, qmo
In-Reply-To: <87y0pryhs8.fsf@microsoft.com>
On Fri, Oct 3, 2025 at 8:14 PM Blaise Boscaccy
<bboscaccy@linux.microsoft.com> wrote:
>
> KP Singh <kpsingh@kernel.org> writes:
>
> > On Thu, Oct 2, 2025 at 10:01 PM Blaise Boscaccy
> > <bboscaccy@linux.microsoft.com> wrote:
> >>
> >> KP Singh <kpsingh@kernel.org> writes:
[...]
> >
> > I am sorry but this does not work, the UAPI here is
> >
> > + /* Pointer to a buffer containing the maps used in the signature
> > + * hash chain of the BPF program.
> > + */
> > + __aligned_u64 signature_maps;
> > + /* Size of the signature maps buffer. */
> > + __u32 signature_maps_size;
> >
> > This needs to be generically applicable and it's not, What happens if
> > the program is not a loader program / when the instruction buffer is
> > stable?
> >
>
> The map array is fully configurable by the signer. Signing any or all
> maps is optional. If the instruction buffer is stable, the signer can
> generate the signature without maps and the caller passes zero in
> those fields.
>
> > If you really want the property that all of the content is signed and
> > verified within the kernel,
>
> That's what code signing is.
Sorry, this is wrong, allowing signed and trusted code to verify
subsequent executions is established in security.
Also you folks keep saying you want in-kernel verification, the loader
runs in the kernel, the same as any kernel code, so that requirement
is met. What you want is the logic to be hard coded in the kernel and
this goes against the BPF approach of flexibility.
>
> > please explore approaches to make the
> > instruction buffer stable or feel free to deny any programs that do
> > relocations at load time for whatever "strict" security policy that
> > you want to implement.
> >
> > Please stop pursuing this extension as it adds cruft to the UAPI
> > that's too specific, encodes the hash chain in the kernel and we won't
> > need in the future.
> >
>
> If your primary complaint at this point is UAPI bloat, we'd be happy to
> rework the configurable hash-chain patch to use the existing signature
> buffer provided in your patchset.
>
> >> [...]
> >>
> >> >> conventions around the placement of LSM hooks, this "halfway" approach
> >> >> makes it difficult for LSMs to log anything about the signature status
[...]
> >>
> >> We signed a program with your upstream tools and it failed to load on a
> >> vanilla kernel 6.16. The loader in your patchset is intepreting the
> >> first few fields of struct bpf_map as a byte array containing a sha256
> >> digest on older kernels.
> >
> > We can convert BPF_OBJ_GET_INFO_BY_FD to be called from loader
> > programs to not rely on the struct field. and or libbbf can call
> > BPF_OBJ_GET_INFO_BY_FD to check if map_get_hash is supported before it
> > generates the hash check.
> >
> > You should not expect bpftool -S -k -i to work on older kernels but it
> > should error out if the options are passed.
> >
>
> `bpftool gen` shouldn't have a priori knowledge of the target kernel
> version.
It can check whether the functionality is supported, it already does
it in many other places. I will follow-up with a fix for this.
- KP
>
> -blaise
>
>
> > - KP
> >
> >>
> >> -blaise
> >>
> >>
> >> > I had given detailed feedback to Blaise in
> >> > https://lore.kernel.org/bpf/CACYkzJ6yNjFOTzC04uOuCmFn=+51_ie2tB9_x-u2xbcO=yobTw@mail.gmail.com/
> >> > mentions also why we don't want any additional UAPI.
> >> >
> >> > You keep mentioning having visibility in the LSM code and I again
> >> > ask, to implement what specific security policy and there is no clear
> >> > answer? On a system where you would like to only allow signed BPF
> >> > programs, you can purely deny any programs where the signature is not
> >> > provided and this can be implemented today.
> >> >
> >> > Stable programs work as it is, programs that require runtime
> >> > relocation work with loader programs. We don't want to add more UAPI
> >> > as, in the future, it's quite possible that we can make the
> >> > instruction buffer stable.
> >> >
> >> > - KP
> >> >
> >> >>
[...]
> >> >> --
> >> >> paul-moore.com
^ permalink raw reply
* [PATCH v2] lsm,uapi: introduce LSM_ATTR_UNSHARE
From: Stephen Smalley @ 2025-10-03 19:10 UTC (permalink / raw)
To: paul
Cc: linux-security-module, selinux, john.johansen, casey, serge,
corbet, jmorris, linux-doc, Stephen Smalley
This defines a new LSM_ATTR_UNSHARE attribute for the
lsm_set_self_attr(2) and lsm_get_self_attr(2) system calls. When
passed to lsm_set_self_attr(2), the LSM-specific namespace for the
specified LSM id is immediately unshared in a similar manner to the
unshare(2) system call for other Linux namespaces. When passed to
lsm_get_self_attr(2), ctx->ctx_len is set to 1 and ctx->ctx[0] is set
to a boolean (0 or 1) that indicates whether the LSM-specific
namespace for the specified LSM id has been unshared and not yet fully
initialized (e.g. no policy yet loaded).
Link: https://lore.kernel.org/selinux/CAHC9VhRGMmhxbajwQNfGFy+ZFF1uN=UEBjqQZQ4UBy7yds3eVQ@mail.gmail.com/
Signed-off-by: Stephen Smalley <stephen.smalley.work@gmail.com>
---
Documentation/userspace-api/lsm.rst | 11 +++++++++++
include/uapi/linux/lsm.h | 1 +
2 files changed, 12 insertions(+)
diff --git a/Documentation/userspace-api/lsm.rst b/Documentation/userspace-api/lsm.rst
index a76da373841b..1134629863cf 100644
--- a/Documentation/userspace-api/lsm.rst
+++ b/Documentation/userspace-api/lsm.rst
@@ -48,6 +48,17 @@ creating socket objects.
The proc filesystem provides this value in ``/proc/self/attr/sockcreate``.
This is supported by the SELinux security module.
+``LSM_ATTR_UNSHARE`` is used to unshare the LSM-specific namespace for
+the process.
+When passed to ``lsm_set_self_attr(2)``, the LSM-specific namespace
+for the specified LSM id is immediately unshared in a similar manner
+to the ``unshare(2)`` system call for other Linux namespaces. When
+passed to ``lsm_get_self_attr(2)``, ``ctx->ctx_len`` is set to ``1``
+and ``ctx->ctx[0]`` is set to a boolean (``0`` or ``1``) that
+indicates whether the LSM-specific namespace for the specified LSM id
+has been unshared and not yet fully initialized (e.g. no policy yet
+loaded).
+
Kernel interface
================
diff --git a/include/uapi/linux/lsm.h b/include/uapi/linux/lsm.h
index 938593dfd5da..fb1b4a8aa639 100644
--- a/include/uapi/linux/lsm.h
+++ b/include/uapi/linux/lsm.h
@@ -83,6 +83,7 @@ struct lsm_ctx {
#define LSM_ATTR_KEYCREATE 103
#define LSM_ATTR_PREV 104
#define LSM_ATTR_SOCKCREATE 105
+#define LSM_ATTR_UNSHARE 106
/*
* LSM_FLAG_XXX definitions identify special handling instructions
--
2.51.0
^ permalink raw reply related
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