* Re: [GIT PULL] KEYS: trusted: keys-trusted-next-6.19-rc7
From: pr-tracker-bot @ 2026-01-25 20:21 UTC (permalink / raw)
To: Jarkko Sakkinen
Cc: Linus Torvalds, David Howells, James Bottomley, Mimi Zohar,
keyrings, linux-integrity, linux-security-module
In-Reply-To: <aXZPW1HnC0kM-VDC@kernel.org>
The pull request you sent on Sun, 25 Jan 2026 19:14:03 +0200:
> git://git.kernel.org/pub/scm/linux/kernel/git/jarkko/linux-tpmdd.git tags/keys-trusted-next-6.19-rc7
has been merged into torvalds/linux.git:
https://git.kernel.org/torvalds/c/f9e6e6d210669f24697e615b68b5abbae9d7a32e
Thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/prtracker.html
^ permalink raw reply
* [PATCH v2 3/3] landlock: transpose the layer masks data structure
From: Günther Noack @ 2026-01-25 19:58 UTC (permalink / raw)
To: Mickaël Salaün
Cc: linux-security-module, Tingmao Wang, Justin Suess,
Samasth Norway Ananda, Matthieu Buffet, Mikhail Ivanov,
konstantin.meskhidze, Randy Dunlap, Günther Noack
In-Reply-To: <20260125195853.109967-1-gnoack3000@gmail.com>
The layer masks data structure tracks the requested but unfulfilled
access rights during an operation's security check. It stores one bit
for each combination of access right and layer index. If the bit is
set, that access right is not granted (yet) in the given layer and we
have to traverse the path further upwards to grant it.
Previously, the layer masks were stored as arrays mapping from access
right indices to layer_mask_t. The layer_mask_t value then indicates
all layers in which the given access right is still (tentatively)
denied.
This patch introduces struct layer_access_masks instead: This struct
contains an array with the access_mask_t of each (tentatively) denied
access right in that layer.
The hypothesis of this patch is that this simplifies the code enough
so that the resulting code will run faster:
* We can use bitwise operations in multiple places where we previously
looped over bits individually with macros. (Should require less
branch speculation and lends itself to better loop unrolling.)
* Code is ~75 lines smaller.
Other noteworthy changes:
* In no_more_access(), call a new helper function may_refer(), which
only solves the asymmetric case. Previously, the code interleaved
the checks for the two symmetric cases in RENAME_EXCHANGE. It feels
that the code is clearer when renames without RENAME_EXCHANGE are
more obviously the normal case.
Tradeoffs:
This change improves performance, at a slight size increase to the
layer masks data structure.
At the moment, for the filesystem access rights, the data structure
has the same size as before, but once we introduce the 17th filesystem
access right, it will double in size (from 32 to 64 bytes), as
access_mask_t grows from 16 to 32 bit. See the link below for
measurements.
Link: https://lore.kernel.org/all/20260120.haeCh4li9Vae@digikod.net/
Signed-off-by: Günther Noack <gnoack3000@gmail.com>
---
security/landlock/access.h | 10 +-
security/landlock/audit.c | 84 +++------
security/landlock/audit.h | 3 +-
security/landlock/domain.c | 45 +++--
security/landlock/domain.h | 4 +-
security/landlock/fs.c | 352 ++++++++++++++++--------------------
security/landlock/net.c | 11 +-
security/landlock/ruleset.c | 88 ++++-----
security/landlock/ruleset.h | 21 ++-
9 files changed, 274 insertions(+), 344 deletions(-)
diff --git a/security/landlock/access.h b/security/landlock/access.h
index 5c0caef9eaf6..1c911fa3555d 100644
--- a/security/landlock/access.h
+++ b/security/landlock/access.h
@@ -61,14 +61,14 @@ union access_masks_all {
static_assert(sizeof(typeof_member(union access_masks_all, masks)) ==
sizeof(typeof_member(union access_masks_all, all)));
-typedef u16 layer_mask_t;
-
-/* Makes sure all layers can be checked. */
-static_assert(BITS_PER_TYPE(layer_mask_t) >= LANDLOCK_MAX_NUM_LAYERS);
-
/*
* Tracks domains responsible of a denied access. This is required to avoid
* storing in each object the full layer_masks[] required by update_request().
+ *
+ * Each nibble represents the layer index of the newest layer which denied a
+ * certain access right. For file system access rights, the upper four bits are
+ * the index of the layer which denies LANDLOCK_ACCESS_FS_IOCTL_DEV and the
+ * lower nibble represents LANDLOCK_ACCESS_FS_TRUNCATE.
*/
typedef u8 deny_masks_t;
diff --git a/security/landlock/audit.c b/security/landlock/audit.c
index e899995f1fd5..979a33f480aa 100644
--- a/security/landlock/audit.c
+++ b/security/landlock/audit.c
@@ -180,38 +180,21 @@ static void test_get_hierarchy(struct kunit *const test)
#endif /* CONFIG_SECURITY_LANDLOCK_KUNIT_TEST */
+/* get_denied_layer - get the youngest layer that denied the access_request */
static size_t get_denied_layer(const struct landlock_ruleset *const domain,
access_mask_t *const access_request,
- const layer_mask_t (*const layer_masks)[],
- const size_t layer_masks_size)
+ const struct layer_access_masks *masks)
{
- const unsigned long access_req = *access_request;
- unsigned long access_bit;
- access_mask_t missing = 0;
- long youngest_layer = -1;
-
- for_each_set_bit(access_bit, &access_req, layer_masks_size) {
- const layer_mask_t mask = (*layer_masks)[access_bit];
- long layer;
-
- if (!mask)
- continue;
-
- /* __fls(1) == 0 */
- layer = __fls(mask);
- if (layer > youngest_layer) {
- youngest_layer = layer;
- missing = BIT(access_bit);
- } else if (layer == youngest_layer) {
- missing |= BIT(access_bit);
+ for (int i = ARRAY_SIZE(masks->access) - 1; i >= 0; i--) {
+ if (masks->access[i] & *access_request) {
+ *access_request &= masks->access[i];
+ return i;
}
}
- *access_request = missing;
- if (youngest_layer == -1)
- return domain->num_layers - 1;
-
- return youngest_layer;
+ /* Not found - fall back to default values */
+ *access_request = 0;
+ return domain->num_layers - 1;
}
#ifdef CONFIG_SECURITY_LANDLOCK_KUNIT_TEST
@@ -221,50 +204,39 @@ static void test_get_denied_layer(struct kunit *const test)
const struct landlock_ruleset dom = {
.num_layers = 5,
};
- const layer_mask_t layer_masks[LANDLOCK_NUM_ACCESS_FS] = {
- [BIT_INDEX(LANDLOCK_ACCESS_FS_EXECUTE)] = BIT(0),
- [BIT_INDEX(LANDLOCK_ACCESS_FS_READ_FILE)] = BIT(1),
- [BIT_INDEX(LANDLOCK_ACCESS_FS_READ_DIR)] = BIT(1) | BIT(0),
- [BIT_INDEX(LANDLOCK_ACCESS_FS_REMOVE_DIR)] = BIT(2),
+ const struct layer_access_masks masks = {
+ .access[0] = LANDLOCK_ACCESS_FS_EXECUTE |
+ LANDLOCK_ACCESS_FS_READ_DIR,
+ .access[1] = LANDLOCK_ACCESS_FS_READ_FILE |
+ LANDLOCK_ACCESS_FS_READ_DIR,
+ .access[2] = LANDLOCK_ACCESS_FS_REMOVE_DIR,
};
access_mask_t access;
access = LANDLOCK_ACCESS_FS_EXECUTE;
- KUNIT_EXPECT_EQ(test, 0,
- get_denied_layer(&dom, &access, &layer_masks,
- sizeof(layer_masks)));
+ KUNIT_EXPECT_EQ(test, 0, get_denied_layer(&dom, &access, &masks));
KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_EXECUTE);
access = LANDLOCK_ACCESS_FS_READ_FILE;
- KUNIT_EXPECT_EQ(test, 1,
- get_denied_layer(&dom, &access, &layer_masks,
- sizeof(layer_masks)));
+ KUNIT_EXPECT_EQ(test, 1, get_denied_layer(&dom, &access, &masks));
KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_READ_FILE);
access = LANDLOCK_ACCESS_FS_READ_DIR;
- KUNIT_EXPECT_EQ(test, 1,
- get_denied_layer(&dom, &access, &layer_masks,
- sizeof(layer_masks)));
+ KUNIT_EXPECT_EQ(test, 1, get_denied_layer(&dom, &access, &masks));
KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_READ_DIR);
access = LANDLOCK_ACCESS_FS_READ_FILE | LANDLOCK_ACCESS_FS_READ_DIR;
- KUNIT_EXPECT_EQ(test, 1,
- get_denied_layer(&dom, &access, &layer_masks,
- sizeof(layer_masks)));
+ KUNIT_EXPECT_EQ(test, 1, get_denied_layer(&dom, &access, &masks));
KUNIT_EXPECT_EQ(test, access,
LANDLOCK_ACCESS_FS_READ_FILE |
LANDLOCK_ACCESS_FS_READ_DIR);
access = LANDLOCK_ACCESS_FS_EXECUTE | LANDLOCK_ACCESS_FS_READ_DIR;
- KUNIT_EXPECT_EQ(test, 1,
- get_denied_layer(&dom, &access, &layer_masks,
- sizeof(layer_masks)));
+ KUNIT_EXPECT_EQ(test, 1, get_denied_layer(&dom, &access, &masks));
KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_READ_DIR);
access = LANDLOCK_ACCESS_FS_WRITE_FILE;
- KUNIT_EXPECT_EQ(test, 4,
- get_denied_layer(&dom, &access, &layer_masks,
- sizeof(layer_masks)));
+ KUNIT_EXPECT_EQ(test, 4, get_denied_layer(&dom, &access, &masks));
KUNIT_EXPECT_EQ(test, access, 0);
}
@@ -361,18 +333,15 @@ static bool is_valid_request(const struct landlock_request *const request)
return false;
if (request->access) {
- if (WARN_ON_ONCE(!(!!request->layer_masks ^
+ if (WARN_ON_ONCE(!(!!request->masks ^
!!request->all_existing_optional_access)))
return false;
} else {
- if (WARN_ON_ONCE(request->layer_masks ||
+ if (WARN_ON_ONCE(request->masks ||
request->all_existing_optional_access))
return false;
}
- if (WARN_ON_ONCE(!!request->layer_masks ^ !!request->layer_masks_size))
- return false;
-
if (request->deny_masks) {
if (WARN_ON_ONCE(!request->all_existing_optional_access))
return false;
@@ -405,13 +374,12 @@ void landlock_log_denial(const struct landlock_cred_security *const subject,
missing = request->access;
if (missing) {
/* Gets the nearest domain that denies the request. */
- if (request->layer_masks) {
+ if (request->masks) {
youngest_layer = get_denied_layer(
- subject->domain, &missing, request->layer_masks,
- request->layer_masks_size);
+ subject->domain, &missing, request->masks);
} else {
youngest_layer = get_layer_from_deny_masks(
- &missing, request->all_existing_optional_access,
+ &missing, _LANDLOCK_ACCESS_FS_OPTIONAL,
request->deny_masks);
}
youngest_denied =
diff --git a/security/landlock/audit.h b/security/landlock/audit.h
index 92428b7fc4d8..104472060ef5 100644
--- a/security/landlock/audit.h
+++ b/security/landlock/audit.h
@@ -43,8 +43,7 @@ struct landlock_request {
access_mask_t access;
/* Required fields for requests with layer masks. */
- const layer_mask_t (*layer_masks)[];
- size_t layer_masks_size;
+ const struct layer_access_masks *masks;
/* Required fields for requests with deny masks. */
const access_mask_t all_existing_optional_access;
diff --git a/security/landlock/domain.c b/security/landlock/domain.c
index a647b68e8d06..5b11ddb22d3a 100644
--- a/security/landlock/domain.c
+++ b/security/landlock/domain.c
@@ -7,6 +7,7 @@
* Copyright © 2024-2025 Microsoft Corporation
*/
+#include "ruleset.h"
#include <kunit/test.h>
#include <linux/bitops.h>
#include <linux/bits.h>
@@ -182,32 +183,36 @@ static void test_get_layer_deny_mask(struct kunit *const test)
deny_masks_t
landlock_get_deny_masks(const access_mask_t all_existing_optional_access,
const access_mask_t optional_access,
- const layer_mask_t (*const layer_masks)[],
- const size_t layer_masks_size)
+ const struct layer_access_masks *const masks)
{
const unsigned long access_opt = optional_access;
unsigned long access_bit;
+ access_mask_t all_denied = 0;
deny_masks_t deny_masks = 0;
/* This may require change with new object types. */
- WARN_ON_ONCE(access_opt !=
- (optional_access & all_existing_optional_access));
+ WARN_ON_ONCE(!access_mask_subset(optional_access,
+ all_existing_optional_access));
- if (WARN_ON_ONCE(!layer_masks))
+ if (WARN_ON_ONCE(!masks))
return 0;
if (WARN_ON_ONCE(!access_opt))
return 0;
- for_each_set_bit(access_bit, &access_opt, layer_masks_size) {
- const layer_mask_t mask = (*layer_masks)[access_bit];
+ for (int i = ARRAY_SIZE(masks->access) - 1; i >= 0; i--) {
+ const access_mask_t denied = masks->access[i] & optional_access;
+ const unsigned long newly_denied = denied & ~all_denied;
- if (!mask)
+ if (!newly_denied)
continue;
- /* __fls(1) == 0 */
- deny_masks |= get_layer_deny_mask(all_existing_optional_access,
- access_bit, __fls(mask));
+ for_each_set_bit(access_bit, &newly_denied,
+ 8 * sizeof(access_mask_t)) {
+ deny_masks |= get_layer_deny_mask(
+ all_existing_optional_access, access_bit, i);
+ }
+ all_denied |= denied;
}
return deny_masks;
}
@@ -216,28 +221,28 @@ landlock_get_deny_masks(const access_mask_t all_existing_optional_access,
static void test_landlock_get_deny_masks(struct kunit *const test)
{
- const layer_mask_t layers1[BITS_PER_TYPE(access_mask_t)] = {
- [BIT_INDEX(LANDLOCK_ACCESS_FS_EXECUTE)] = BIT_ULL(0) |
- BIT_ULL(9),
- [BIT_INDEX(LANDLOCK_ACCESS_FS_TRUNCATE)] = BIT_ULL(1),
- [BIT_INDEX(LANDLOCK_ACCESS_FS_IOCTL_DEV)] = BIT_ULL(2) |
- BIT_ULL(0),
+ const struct layer_access_masks layers1 = {
+ .access[0] = LANDLOCK_ACCESS_FS_EXECUTE |
+ LANDLOCK_ACCESS_FS_IOCTL_DEV,
+ .access[1] = LANDLOCK_ACCESS_FS_TRUNCATE,
+ .access[2] = LANDLOCK_ACCESS_FS_IOCTL_DEV,
+ .access[9] = LANDLOCK_ACCESS_FS_EXECUTE,
};
KUNIT_EXPECT_EQ(test, 0x1,
landlock_get_deny_masks(_LANDLOCK_ACCESS_FS_OPTIONAL,
LANDLOCK_ACCESS_FS_TRUNCATE,
- &layers1, ARRAY_SIZE(layers1)));
+ &layers1));
KUNIT_EXPECT_EQ(test, 0x20,
landlock_get_deny_masks(_LANDLOCK_ACCESS_FS_OPTIONAL,
LANDLOCK_ACCESS_FS_IOCTL_DEV,
- &layers1, ARRAY_SIZE(layers1)));
+ &layers1));
KUNIT_EXPECT_EQ(
test, 0x21,
landlock_get_deny_masks(_LANDLOCK_ACCESS_FS_OPTIONAL,
LANDLOCK_ACCESS_FS_TRUNCATE |
LANDLOCK_ACCESS_FS_IOCTL_DEV,
- &layers1, ARRAY_SIZE(layers1)));
+ &layers1));
}
#endif /* CONFIG_SECURITY_LANDLOCK_KUNIT_TEST */
diff --git a/security/landlock/domain.h b/security/landlock/domain.h
index 621f054c9a2b..227066d667f7 100644
--- a/security/landlock/domain.h
+++ b/security/landlock/domain.h
@@ -10,6 +10,7 @@
#ifndef _SECURITY_LANDLOCK_DOMAIN_H
#define _SECURITY_LANDLOCK_DOMAIN_H
+#include "ruleset.h"
#include <linux/limits.h>
#include <linux/mm.h>
#include <linux/path.h>
@@ -122,8 +123,7 @@ struct landlock_hierarchy {
deny_masks_t
landlock_get_deny_masks(const access_mask_t all_existing_optional_access,
const access_mask_t optional_access,
- const layer_mask_t (*const layer_masks)[],
- size_t layer_masks_size);
+ const struct layer_access_masks *const masks);
int landlock_init_hierarchy_log(struct landlock_hierarchy *const hierarchy);
diff --git a/security/landlock/fs.c b/security/landlock/fs.c
index bf8e37fcc7c0..cef0013c2cf6 100644
--- a/security/landlock/fs.c
+++ b/security/landlock/fs.c
@@ -398,57 +398,55 @@ static const struct access_masks any_fs = {
.fs = ~0,
};
+/*
+ * Returns true iff the child file with the given src_child access rights under
+ * src_parent would result in having the same or fewer access rights if it were
+ * moved under new_parent.
+ */
+static bool may_refer(const struct layer_access_masks *const src_parent,
+ const struct layer_access_masks *const src_child,
+ const struct layer_access_masks *const new_parent,
+ const bool child_is_dir)
+{
+ for (size_t i = 0; i < ARRAY_SIZE(new_parent->access); i++) {
+ access_mask_t child_access = src_parent->access[i] &
+ src_child->access[i];
+ access_mask_t parent_access = new_parent->access[i];
+
+ if (!child_is_dir) {
+ child_access &= ACCESS_FILE;
+ parent_access &= ACCESS_FILE;
+ }
+
+ if (!access_mask_subset(child_access, parent_access))
+ return false;
+ }
+ return true;
+}
+
/*
* Check that a destination file hierarchy has more restrictions than a source
* file hierarchy. This is only used for link and rename actions.
*
- * @layer_masks_child2: Optional child masks.
+ * Returns: true if child1 may be moved from parent1 to parent2 without
+ * increasing its access rights. If child2 is set, an additional condition is
+ * that child2 may be used from parent2 to parent1 without increasing its access
+ * rights.
*/
-static bool no_more_access(
- const layer_mask_t (*const layer_masks_parent1)[LANDLOCK_NUM_ACCESS_FS],
- const layer_mask_t (*const layer_masks_child1)[LANDLOCK_NUM_ACCESS_FS],
- const bool child1_is_directory,
- const layer_mask_t (*const layer_masks_parent2)[LANDLOCK_NUM_ACCESS_FS],
- const layer_mask_t (*const layer_masks_child2)[LANDLOCK_NUM_ACCESS_FS],
- const bool child2_is_directory)
+static bool no_more_access(const struct layer_access_masks *const parent1,
+ const struct layer_access_masks *const child1,
+ const bool child1_is_dir,
+ const struct layer_access_masks *const parent2,
+ const struct layer_access_masks *const child2,
+ const bool child2_is_dir)
{
- unsigned long access_bit;
+ if (!may_refer(parent1, child1, parent2, child1_is_dir))
+ return false;
- for (access_bit = 0; access_bit < ARRAY_SIZE(*layer_masks_parent2);
- access_bit++) {
- /* Ignores accesses that only make sense for directories. */
- const bool is_file_access =
- !!(BIT_ULL(access_bit) & ACCESS_FILE);
+ if (!child2)
+ return true;
- if (child1_is_directory || is_file_access) {
- /*
- * Checks if the destination restrictions are a
- * superset of the source ones (i.e. inherited access
- * rights without child exceptions):
- * restrictions(parent2) >= restrictions(child1)
- */
- if ((((*layer_masks_parent1)[access_bit] &
- (*layer_masks_child1)[access_bit]) |
- (*layer_masks_parent2)[access_bit]) !=
- (*layer_masks_parent2)[access_bit])
- return false;
- }
-
- if (!layer_masks_child2)
- continue;
- if (child2_is_directory || is_file_access) {
- /*
- * Checks inverted restrictions for RENAME_EXCHANGE:
- * restrictions(parent1) >= restrictions(child2)
- */
- if ((((*layer_masks_parent2)[access_bit] &
- (*layer_masks_child2)[access_bit]) |
- (*layer_masks_parent1)[access_bit]) !=
- (*layer_masks_parent1)[access_bit])
- return false;
- }
- }
- return true;
+ return may_refer(parent2, child2, parent1, child2_is_dir);
}
#define NMA_TRUE(...) KUNIT_EXPECT_TRUE(test, no_more_access(__VA_ARGS__))
@@ -458,25 +456,25 @@ static bool no_more_access(
static void test_no_more_access(struct kunit *const test)
{
- const layer_mask_t rx0[LANDLOCK_NUM_ACCESS_FS] = {
- [BIT_INDEX(LANDLOCK_ACCESS_FS_EXECUTE)] = BIT_ULL(0),
- [BIT_INDEX(LANDLOCK_ACCESS_FS_READ_FILE)] = BIT_ULL(0),
+ const struct layer_access_masks rx0 = {
+ .access[0] = LANDLOCK_ACCESS_FS_EXECUTE |
+ LANDLOCK_ACCESS_FS_READ_FILE,
};
- const layer_mask_t mx0[LANDLOCK_NUM_ACCESS_FS] = {
- [BIT_INDEX(LANDLOCK_ACCESS_FS_EXECUTE)] = BIT_ULL(0),
- [BIT_INDEX(LANDLOCK_ACCESS_FS_MAKE_REG)] = BIT_ULL(0),
+ const struct layer_access_masks mx0 = {
+ .access[0] = LANDLOCK_ACCESS_FS_EXECUTE |
+ LANDLOCK_ACCESS_FS_MAKE_REG,
};
- const layer_mask_t x0[LANDLOCK_NUM_ACCESS_FS] = {
- [BIT_INDEX(LANDLOCK_ACCESS_FS_EXECUTE)] = BIT_ULL(0),
+ const struct layer_access_masks x0 = {
+ .access[0] = LANDLOCK_ACCESS_FS_EXECUTE,
};
- const layer_mask_t x1[LANDLOCK_NUM_ACCESS_FS] = {
- [BIT_INDEX(LANDLOCK_ACCESS_FS_EXECUTE)] = BIT_ULL(1),
+ const struct layer_access_masks x1 = {
+ .access[1] = LANDLOCK_ACCESS_FS_EXECUTE,
};
- const layer_mask_t x01[LANDLOCK_NUM_ACCESS_FS] = {
- [BIT_INDEX(LANDLOCK_ACCESS_FS_EXECUTE)] = BIT_ULL(0) |
- BIT_ULL(1),
+ const struct layer_access_masks x01 = {
+ .access[0] = LANDLOCK_ACCESS_FS_EXECUTE,
+ .access[1] = LANDLOCK_ACCESS_FS_EXECUTE,
};
- const layer_mask_t allows_all[LANDLOCK_NUM_ACCESS_FS] = {};
+ const struct layer_access_masks allows_all = {};
/* Checks without restriction. */
NMA_TRUE(&x0, &allows_all, false, &allows_all, NULL, false);
@@ -564,31 +562,30 @@ static void test_no_more_access(struct kunit *const test)
#undef NMA_TRUE
#undef NMA_FALSE
-static bool is_layer_masks_allowed(
- layer_mask_t (*const layer_masks)[LANDLOCK_NUM_ACCESS_FS])
+static bool is_layer_masks_allowed(const struct layer_access_masks *masks)
{
- return !memchr_inv(layer_masks, 0, sizeof(*layer_masks));
+ return !memchr_inv(&masks->access, 0, sizeof(masks->access));
}
/*
- * Removes @layer_masks accesses that are not requested.
+ * Removes @masks accesses that are not requested.
*
* Returns true if the request is allowed, false otherwise.
*/
-static bool
-scope_to_request(const access_mask_t access_request,
- layer_mask_t (*const layer_masks)[LANDLOCK_NUM_ACCESS_FS])
+static bool scope_to_request(const access_mask_t access_request,
+ struct layer_access_masks *masks)
{
- const unsigned long access_req = access_request;
- unsigned long access_bit;
+ bool saw_unfulfilled_access = false;
- if (WARN_ON_ONCE(!layer_masks))
+ if (WARN_ON_ONCE(!masks))
return true;
- for_each_clear_bit(access_bit, &access_req, ARRAY_SIZE(*layer_masks))
- (*layer_masks)[access_bit] = 0;
-
- return is_layer_masks_allowed(layer_masks);
+ for (size_t i = 0; i < ARRAY_SIZE(masks->access); i++) {
+ masks->access[i] &= access_request;
+ if (masks->access[i])
+ saw_unfulfilled_access = true;
+ }
+ return !saw_unfulfilled_access;
}
#ifdef CONFIG_SECURITY_LANDLOCK_KUNIT_TEST
@@ -596,48 +593,41 @@ scope_to_request(const access_mask_t access_request,
static void test_scope_to_request_with_exec_none(struct kunit *const test)
{
/* Allows everything. */
- layer_mask_t layer_masks[LANDLOCK_NUM_ACCESS_FS] = {};
+ struct layer_access_masks masks = {};
/* Checks and scopes with execute. */
- KUNIT_EXPECT_TRUE(test, scope_to_request(LANDLOCK_ACCESS_FS_EXECUTE,
- &layer_masks));
- KUNIT_EXPECT_EQ(test, 0,
- layer_masks[BIT_INDEX(LANDLOCK_ACCESS_FS_EXECUTE)]);
- KUNIT_EXPECT_EQ(test, 0,
- layer_masks[BIT_INDEX(LANDLOCK_ACCESS_FS_WRITE_FILE)]);
+ KUNIT_EXPECT_TRUE(test,
+ scope_to_request(LANDLOCK_ACCESS_FS_EXECUTE, &masks));
+ KUNIT_EXPECT_EQ(test, 0, masks.access[0]);
}
static void test_scope_to_request_with_exec_some(struct kunit *const test)
{
/* Denies execute and write. */
- layer_mask_t layer_masks[LANDLOCK_NUM_ACCESS_FS] = {
- [BIT_INDEX(LANDLOCK_ACCESS_FS_EXECUTE)] = BIT_ULL(0),
- [BIT_INDEX(LANDLOCK_ACCESS_FS_WRITE_FILE)] = BIT_ULL(1),
+ struct layer_access_masks masks = {
+ .access[0] = LANDLOCK_ACCESS_FS_EXECUTE,
+ .access[1] = LANDLOCK_ACCESS_FS_WRITE_FILE,
};
/* Checks and scopes with execute. */
KUNIT_EXPECT_FALSE(test, scope_to_request(LANDLOCK_ACCESS_FS_EXECUTE,
- &layer_masks));
- KUNIT_EXPECT_EQ(test, BIT_ULL(0),
- layer_masks[BIT_INDEX(LANDLOCK_ACCESS_FS_EXECUTE)]);
- KUNIT_EXPECT_EQ(test, 0,
- layer_masks[BIT_INDEX(LANDLOCK_ACCESS_FS_WRITE_FILE)]);
+ &masks));
+ KUNIT_EXPECT_EQ(test, LANDLOCK_ACCESS_FS_EXECUTE, masks.access[0]);
+ KUNIT_EXPECT_EQ(test, 0, masks.access[1]);
}
static void test_scope_to_request_without_access(struct kunit *const test)
{
/* Denies execute and write. */
- layer_mask_t layer_masks[LANDLOCK_NUM_ACCESS_FS] = {
- [BIT_INDEX(LANDLOCK_ACCESS_FS_EXECUTE)] = BIT_ULL(0),
- [BIT_INDEX(LANDLOCK_ACCESS_FS_WRITE_FILE)] = BIT_ULL(1),
+ struct layer_access_masks masks = {
+ .access[0] = LANDLOCK_ACCESS_FS_EXECUTE,
+ .access[1] = LANDLOCK_ACCESS_FS_WRITE_FILE,
};
/* Checks and scopes without access request. */
- KUNIT_EXPECT_TRUE(test, scope_to_request(0, &layer_masks));
- KUNIT_EXPECT_EQ(test, 0,
- layer_masks[BIT_INDEX(LANDLOCK_ACCESS_FS_EXECUTE)]);
- KUNIT_EXPECT_EQ(test, 0,
- layer_masks[BIT_INDEX(LANDLOCK_ACCESS_FS_WRITE_FILE)]);
+ KUNIT_EXPECT_TRUE(test, scope_to_request(0, &masks));
+ KUNIT_EXPECT_EQ(test, 0, masks.access[0]);
+ KUNIT_EXPECT_EQ(test, 0, masks.access[1]);
}
#endif /* CONFIG_SECURITY_LANDLOCK_KUNIT_TEST */
@@ -646,20 +636,16 @@ static void test_scope_to_request_without_access(struct kunit *const test)
* Returns true if there is at least one access right different than
* LANDLOCK_ACCESS_FS_REFER.
*/
-static bool
-is_eacces(const layer_mask_t (*const layer_masks)[LANDLOCK_NUM_ACCESS_FS],
- const access_mask_t access_request)
+static bool is_eacces(const struct layer_access_masks *masks,
+ const access_mask_t access_request)
{
- unsigned long access_bit;
- /* LANDLOCK_ACCESS_FS_REFER alone must return -EXDEV. */
- const unsigned long access_check = access_request &
- ~LANDLOCK_ACCESS_FS_REFER;
-
- if (!layer_masks)
+ if (!masks)
return false;
- for_each_set_bit(access_bit, &access_check, ARRAY_SIZE(*layer_masks)) {
- if ((*layer_masks)[access_bit])
+ for (size_t i = 0; i < ARRAY_SIZE(masks->access); i++) {
+ /* LANDLOCK_ACCESS_FS_REFER alone must return -EXDEV. */
+ if (masks->access[i] & access_request &
+ ~LANDLOCK_ACCESS_FS_REFER)
return true;
}
return false;
@@ -672,37 +658,37 @@ is_eacces(const layer_mask_t (*const layer_masks)[LANDLOCK_NUM_ACCESS_FS],
static void test_is_eacces_with_none(struct kunit *const test)
{
- const layer_mask_t layer_masks[LANDLOCK_NUM_ACCESS_FS] = {};
+ const struct layer_access_masks masks = {};
- IE_FALSE(&layer_masks, 0);
- IE_FALSE(&layer_masks, LANDLOCK_ACCESS_FS_REFER);
- IE_FALSE(&layer_masks, LANDLOCK_ACCESS_FS_EXECUTE);
- IE_FALSE(&layer_masks, LANDLOCK_ACCESS_FS_WRITE_FILE);
+ IE_FALSE(&masks, 0);
+ IE_FALSE(&masks, LANDLOCK_ACCESS_FS_REFER);
+ IE_FALSE(&masks, LANDLOCK_ACCESS_FS_EXECUTE);
+ IE_FALSE(&masks, LANDLOCK_ACCESS_FS_WRITE_FILE);
}
static void test_is_eacces_with_refer(struct kunit *const test)
{
- const layer_mask_t layer_masks[LANDLOCK_NUM_ACCESS_FS] = {
- [BIT_INDEX(LANDLOCK_ACCESS_FS_REFER)] = BIT_ULL(0),
+ const struct layer_access_masks masks = {
+ .access[0] = LANDLOCK_ACCESS_FS_REFER,
};
- IE_FALSE(&layer_masks, 0);
- IE_FALSE(&layer_masks, LANDLOCK_ACCESS_FS_REFER);
- IE_FALSE(&layer_masks, LANDLOCK_ACCESS_FS_EXECUTE);
- IE_FALSE(&layer_masks, LANDLOCK_ACCESS_FS_WRITE_FILE);
+ IE_FALSE(&masks, 0);
+ IE_FALSE(&masks, LANDLOCK_ACCESS_FS_REFER);
+ IE_FALSE(&masks, LANDLOCK_ACCESS_FS_EXECUTE);
+ IE_FALSE(&masks, LANDLOCK_ACCESS_FS_WRITE_FILE);
}
static void test_is_eacces_with_write(struct kunit *const test)
{
- const layer_mask_t layer_masks[LANDLOCK_NUM_ACCESS_FS] = {
- [BIT_INDEX(LANDLOCK_ACCESS_FS_WRITE_FILE)] = BIT_ULL(0),
+ const struct layer_access_masks masks = {
+ .access[0] = LANDLOCK_ACCESS_FS_WRITE_FILE,
};
- IE_FALSE(&layer_masks, 0);
- IE_FALSE(&layer_masks, LANDLOCK_ACCESS_FS_REFER);
- IE_FALSE(&layer_masks, LANDLOCK_ACCESS_FS_EXECUTE);
+ IE_FALSE(&masks, 0);
+ IE_FALSE(&masks, LANDLOCK_ACCESS_FS_REFER);
+ IE_FALSE(&masks, LANDLOCK_ACCESS_FS_EXECUTE);
- IE_TRUE(&layer_masks, LANDLOCK_ACCESS_FS_WRITE_FILE);
+ IE_TRUE(&masks, LANDLOCK_ACCESS_FS_WRITE_FILE);
}
#endif /* CONFIG_SECURITY_LANDLOCK_KUNIT_TEST */
@@ -752,26 +738,25 @@ static void test_is_eacces_with_write(struct kunit *const test)
* - true if the access request is granted;
* - false otherwise.
*/
-static bool is_access_to_paths_allowed(
- const struct landlock_ruleset *const domain,
- const struct path *const path,
- const access_mask_t access_request_parent1,
- layer_mask_t (*const layer_masks_parent1)[LANDLOCK_NUM_ACCESS_FS],
- struct landlock_request *const log_request_parent1,
- struct dentry *const dentry_child1,
- const access_mask_t access_request_parent2,
- layer_mask_t (*const layer_masks_parent2)[LANDLOCK_NUM_ACCESS_FS],
- struct landlock_request *const log_request_parent2,
- struct dentry *const dentry_child2)
+static bool
+is_access_to_paths_allowed(const struct landlock_ruleset *const domain,
+ const struct path *const path,
+ const access_mask_t access_request_parent1,
+ struct layer_access_masks *layer_masks_parent1,
+ struct landlock_request *const log_request_parent1,
+ struct dentry *const dentry_child1,
+ const access_mask_t access_request_parent2,
+ struct layer_access_masks *layer_masks_parent2,
+ struct landlock_request *const log_request_parent2,
+ struct dentry *const dentry_child2)
{
bool allowed_parent1 = false, allowed_parent2 = false, is_dom_check,
child1_is_directory = true, child2_is_directory = true;
struct path walker_path;
access_mask_t access_masked_parent1, access_masked_parent2;
- layer_mask_t _layer_masks_child1[LANDLOCK_NUM_ACCESS_FS],
- _layer_masks_child2[LANDLOCK_NUM_ACCESS_FS];
- layer_mask_t(*layer_masks_child1)[LANDLOCK_NUM_ACCESS_FS] = NULL,
- (*layer_masks_child2)[LANDLOCK_NUM_ACCESS_FS] = NULL;
+ struct layer_access_masks _layer_masks_child1, _layer_masks_child2;
+ struct layer_access_masks *layer_masks_child1 = NULL,
+ *layer_masks_child2 = NULL;
if (!access_request_parent1 && !access_request_parent2)
return true;
@@ -811,22 +796,20 @@ static bool is_access_to_paths_allowed(
}
if (unlikely(dentry_child1)) {
- landlock_unmask_layers(
- find_rule(domain, dentry_child1),
- landlock_init_layer_masks(
- domain, LANDLOCK_MASK_ACCESS_FS,
- &_layer_masks_child1, LANDLOCK_KEY_INODE),
- &_layer_masks_child1, ARRAY_SIZE(_layer_masks_child1));
+ if (landlock_init_layer_masks(domain, LANDLOCK_MASK_ACCESS_FS,
+ &_layer_masks_child1,
+ LANDLOCK_KEY_INODE))
+ landlock_unmask_layers(find_rule(domain, dentry_child1),
+ &_layer_masks_child1);
layer_masks_child1 = &_layer_masks_child1;
child1_is_directory = d_is_dir(dentry_child1);
}
if (unlikely(dentry_child2)) {
- landlock_unmask_layers(
- find_rule(domain, dentry_child2),
- landlock_init_layer_masks(
- domain, LANDLOCK_MASK_ACCESS_FS,
- &_layer_masks_child2, LANDLOCK_KEY_INODE),
- &_layer_masks_child2, ARRAY_SIZE(_layer_masks_child2));
+ if (landlock_init_layer_masks(domain, LANDLOCK_MASK_ACCESS_FS,
+ &_layer_masks_child2,
+ LANDLOCK_KEY_INODE))
+ landlock_unmask_layers(find_rule(domain, dentry_child2),
+ &_layer_masks_child2);
layer_masks_child2 = &_layer_masks_child2;
child2_is_directory = d_is_dir(dentry_child2);
}
@@ -881,16 +864,12 @@ static bool is_access_to_paths_allowed(
}
rule = find_rule(domain, walker_path.dentry);
- allowed_parent1 = allowed_parent1 ||
- landlock_unmask_layers(
- rule, access_masked_parent1,
- layer_masks_parent1,
- ARRAY_SIZE(*layer_masks_parent1));
- allowed_parent2 = allowed_parent2 ||
- landlock_unmask_layers(
- rule, access_masked_parent2,
- layer_masks_parent2,
- ARRAY_SIZE(*layer_masks_parent2));
+ allowed_parent1 =
+ allowed_parent1 ||
+ landlock_unmask_layers(rule, layer_masks_parent1);
+ allowed_parent2 =
+ allowed_parent2 ||
+ landlock_unmask_layers(rule, layer_masks_parent2);
/* Stops when a rule from each layer grants access. */
if (allowed_parent1 && allowed_parent2)
@@ -949,9 +928,7 @@ static bool is_access_to_paths_allowed(
log_request_parent1->audit.type = LSM_AUDIT_DATA_PATH;
log_request_parent1->audit.u.path = *path;
log_request_parent1->access = access_masked_parent1;
- log_request_parent1->layer_masks = layer_masks_parent1;
- log_request_parent1->layer_masks_size =
- ARRAY_SIZE(*layer_masks_parent1);
+ log_request_parent1->masks = layer_masks_parent1;
}
if (!allowed_parent2 && log_request_parent2) {
@@ -959,9 +936,7 @@ static bool is_access_to_paths_allowed(
log_request_parent2->audit.type = LSM_AUDIT_DATA_PATH;
log_request_parent2->audit.u.path = *path;
log_request_parent2->access = access_masked_parent2;
- log_request_parent2->layer_masks = layer_masks_parent2;
- log_request_parent2->layer_masks_size =
- ARRAY_SIZE(*layer_masks_parent2);
+ log_request_parent2->masks = layer_masks_parent2;
}
#endif /* CONFIG_AUDIT */
@@ -976,7 +951,7 @@ static int current_check_access_path(const struct path *const path,
};
const struct landlock_cred_security *const subject =
landlock_get_applicable_subject(current_cred(), masks, NULL);
- layer_mask_t layer_masks[LANDLOCK_NUM_ACCESS_FS] = {};
+ struct layer_access_masks layer_masks;
struct landlock_request request = {};
if (!subject)
@@ -1051,12 +1026,11 @@ static access_mask_t maybe_remove(const struct dentry *const dentry)
* - true if all the domain access rights are allowed for @dir;
* - false if the walk reached @mnt_root.
*/
-static bool collect_domain_accesses(
- const struct landlock_ruleset *const domain,
- const struct dentry *const mnt_root, struct dentry *dir,
- layer_mask_t (*const layer_masks_dom)[LANDLOCK_NUM_ACCESS_FS])
+static bool collect_domain_accesses(const struct landlock_ruleset *const domain,
+ const struct dentry *const mnt_root,
+ struct dentry *dir,
+ struct layer_access_masks *layer_masks_dom)
{
- unsigned long access_dom;
bool ret = false;
if (WARN_ON_ONCE(!domain || !mnt_root || !dir || !layer_masks_dom))
@@ -1064,18 +1038,17 @@ static bool collect_domain_accesses(
if (is_nouser_or_private(dir))
return true;
- access_dom = landlock_init_layer_masks(domain, LANDLOCK_MASK_ACCESS_FS,
- layer_masks_dom,
- LANDLOCK_KEY_INODE);
+ if (!landlock_init_layer_masks(domain, LANDLOCK_MASK_ACCESS_FS,
+ layer_masks_dom, LANDLOCK_KEY_INODE))
+ return true;
dget(dir);
while (true) {
struct dentry *parent_dentry;
/* Gets all layers allowing all domain accesses. */
- if (landlock_unmask_layers(find_rule(domain, dir), access_dom,
- layer_masks_dom,
- ARRAY_SIZE(*layer_masks_dom))) {
+ if (landlock_unmask_layers(find_rule(domain, dir),
+ layer_masks_dom)) {
/*
* Stops when all handled accesses are allowed by at
* least one rule in each layer.
@@ -1163,8 +1136,8 @@ static int current_check_refer_path(struct dentry *const old_dentry,
access_mask_t access_request_parent1, access_request_parent2;
struct path mnt_dir;
struct dentry *old_parent;
- layer_mask_t layer_masks_parent1[LANDLOCK_NUM_ACCESS_FS] = {},
- layer_masks_parent2[LANDLOCK_NUM_ACCESS_FS] = {};
+ struct layer_access_masks layer_masks_parent1 = {},
+ layer_masks_parent2 = {};
struct landlock_request request1 = {}, request2 = {};
if (!subject)
@@ -1640,7 +1613,7 @@ static bool is_device(const struct file *const file)
static int hook_file_open(struct file *const file)
{
- layer_mask_t layer_masks[LANDLOCK_NUM_ACCESS_FS] = {};
+ struct layer_access_masks layer_masks = {};
access_mask_t open_access_request, full_access_request, allowed_access,
optional_access;
const struct landlock_cred_security *const subject =
@@ -1675,20 +1648,14 @@ static int hook_file_open(struct file *const file)
&layer_masks, &request, NULL, 0, NULL, NULL, NULL)) {
allowed_access = full_access_request;
} else {
- unsigned long access_bit;
- const unsigned long access_req = full_access_request;
-
/*
* Calculate the actual allowed access rights from layer_masks.
- * Add each access right to allowed_access which has not been
- * vetoed by any layer.
+ * Remove the access rights from the full access request which
+ * are still unfulfilled in any of the layers.
*/
- allowed_access = 0;
- for_each_set_bit(access_bit, &access_req,
- ARRAY_SIZE(layer_masks)) {
- if (!layer_masks[access_bit])
- allowed_access |= BIT_ULL(access_bit);
- }
+ allowed_access = full_access_request;
+ for (size_t i = 0; i < ARRAY_SIZE(layer_masks.access); i++)
+ allowed_access &= ~layer_masks.access[i];
}
/*
@@ -1700,8 +1667,7 @@ static int hook_file_open(struct file *const file)
landlock_file(file)->allowed_access = allowed_access;
#ifdef CONFIG_AUDIT
landlock_file(file)->deny_masks = landlock_get_deny_masks(
- _LANDLOCK_ACCESS_FS_OPTIONAL, optional_access, &layer_masks,
- ARRAY_SIZE(layer_masks));
+ _LANDLOCK_ACCESS_FS_OPTIONAL, optional_access, &layer_masks);
#endif /* CONFIG_AUDIT */
if (access_mask_subset(open_access_request, allowed_access))
diff --git a/security/landlock/net.c b/security/landlock/net.c
index e6367e30e5b0..2a66b69b77a9 100644
--- a/security/landlock/net.c
+++ b/security/landlock/net.c
@@ -47,7 +47,7 @@ static int current_check_access_socket(struct socket *const sock,
access_mask_t access_request)
{
__be16 port;
- layer_mask_t layer_masks[LANDLOCK_NUM_ACCESS_NET] = {};
+ struct layer_access_masks layer_masks = {};
const struct landlock_rule *rule;
struct landlock_id id = {
.type = LANDLOCK_KEY_NET_PORT,
@@ -194,8 +194,10 @@ static int current_check_access_socket(struct socket *const sock,
access_request = landlock_init_layer_masks(subject->domain,
access_request, &layer_masks,
LANDLOCK_KEY_NET_PORT);
- if (landlock_unmask_layers(rule, access_request, &layer_masks,
- ARRAY_SIZE(layer_masks)))
+ if (!access_request)
+ return 0;
+
+ if (landlock_unmask_layers(rule, &layer_masks))
return 0;
audit_net.family = address->sa_family;
@@ -205,8 +207,7 @@ static int current_check_access_socket(struct socket *const sock,
.audit.type = LSM_AUDIT_DATA_NET,
.audit.u.net = &audit_net,
.access = access_request,
- .layer_masks = &layer_masks,
- .layer_masks_size = ARRAY_SIZE(layer_masks),
+ .masks = &layer_masks,
});
return -EACCES;
}
diff --git a/security/landlock/ruleset.c b/security/landlock/ruleset.c
index 0a5b0c76b3f7..8c40f319f4d3 100644
--- a/security/landlock/ruleset.c
+++ b/security/landlock/ruleset.c
@@ -612,22 +612,23 @@ landlock_find_rule(const struct landlock_ruleset *const ruleset,
return NULL;
}
-/*
- * @layer_masks is read and may be updated according to the access request and
- * the matching rule.
- * @masks_array_size must be equal to ARRAY_SIZE(*layer_masks).
+/**
+ * landlock_unmask_layers - Cross off access rights granted in @rule in @masks
*
- * Returns true if the request is allowed (i.e. relevant layer masks for the
- * request are empty).
+ * Updates the set of (per-layer) unfulfilled access rights @masks
+ * so that all the access rights granted in @rule are removed from it
+ * (because they are now fulfilled).
+ *
+ * @rule: A rule that grants a set of access rights for each layer
+ * @masks: A matrix of unfulfilled access rights for each layer
+ *
+ * Returns true if the request is allowed (i.e. the access rights granted all
+ * remaining unfulfilled access rights and masks has no leftover set bits).
*/
bool landlock_unmask_layers(const struct landlock_rule *const rule,
- const access_mask_t access_request,
- layer_mask_t (*const layer_masks)[],
- const size_t masks_array_size)
+ struct layer_access_masks *masks)
{
- size_t layer_level;
-
- if (!access_request || !layer_masks)
+ if (!masks)
return true;
if (!rule)
return false;
@@ -642,28 +643,18 @@ bool landlock_unmask_layers(const struct landlock_rule *const rule,
* by only one rule, but by the union (binary OR) of multiple rules.
* E.g. /a/b <execute> + /a <read> => /a/b <execute + read>
*/
- for (layer_level = 0; layer_level < rule->num_layers; layer_level++) {
- const struct landlock_layer *const layer =
- &rule->layers[layer_level];
- const layer_mask_t layer_bit = BIT_ULL(layer->level - 1);
- const unsigned long access_req = access_request;
- unsigned long access_bit;
- bool is_empty;
+ for (size_t i = 0; i < rule->num_layers; i++) {
+ const struct landlock_layer *const layer = &rule->layers[i];
- /*
- * Records in @layer_masks which layer grants access to each requested
- * access: bit cleared if the related layer grants access.
- */
- is_empty = true;
- for_each_set_bit(access_bit, &access_req, masks_array_size) {
- if (layer->access & BIT_ULL(access_bit))
- (*layer_masks)[access_bit] &= ~layer_bit;
- is_empty = is_empty && !(*layer_masks)[access_bit];
- }
- if (is_empty)
- return true;
+ /* Clear the bits where the layer in the rule grants access. */
+ masks->access[layer->level - 1] &= ~layer->access;
}
- return false;
+
+ for (size_t i = 0; i < ARRAY_SIZE(masks->access); i++) {
+ if (masks->access[i])
+ return false;
+ }
+ return true;
}
typedef access_mask_t
@@ -673,13 +664,12 @@ get_access_mask_t(const struct landlock_ruleset *const ruleset,
/**
* landlock_init_layer_masks - Initialize layer masks from an access request
*
- * Populates @layer_masks such that for each access right in @access_request,
+ * Populates @masks such that for each access right in @access_request,
* the bits for all the layers are set where this access right is handled.
*
* @domain: The domain that defines the current restrictions.
* @access_request: The requested access rights to check.
- * @layer_masks: It must contain %LANDLOCK_NUM_ACCESS_FS or
- * %LANDLOCK_NUM_ACCESS_NET elements according to @key_type.
+ * @masks: Layer access masks to populate.
* @key_type: The key type to switch between access masks of different types.
*
* Returns: An access mask where each access right bit is set which is handled
@@ -688,23 +678,20 @@ get_access_mask_t(const struct landlock_ruleset *const ruleset,
access_mask_t
landlock_init_layer_masks(const struct landlock_ruleset *const domain,
const access_mask_t access_request,
- layer_mask_t (*const layer_masks)[],
+ struct layer_access_masks *const masks,
const enum landlock_key_type key_type)
{
access_mask_t handled_accesses = 0;
- size_t layer_level, num_access;
get_access_mask_t *get_access_mask;
switch (key_type) {
case LANDLOCK_KEY_INODE:
get_access_mask = landlock_get_fs_access_mask;
- num_access = LANDLOCK_NUM_ACCESS_FS;
break;
#if IS_ENABLED(CONFIG_INET)
case LANDLOCK_KEY_NET_PORT:
get_access_mask = landlock_get_net_access_mask;
- num_access = LANDLOCK_NUM_ACCESS_NET;
break;
#endif /* IS_ENABLED(CONFIG_INET) */
@@ -713,27 +700,18 @@ landlock_init_layer_masks(const struct landlock_ruleset *const domain,
return 0;
}
- memset(layer_masks, 0,
- array_size(sizeof((*layer_masks)[0]), num_access));
-
/* An empty access request can happen because of O_WRONLY | O_RDWR. */
if (!access_request)
return 0;
- /* Saves all handled accesses per layer. */
- for (layer_level = 0; layer_level < domain->num_layers; layer_level++) {
- const unsigned long access_req = access_request;
- const access_mask_t access_mask =
- get_access_mask(domain, layer_level);
- unsigned long access_bit;
+ for (size_t i = 0; i < domain->num_layers; i++) {
+ const access_mask_t handled = get_access_mask(domain, i);
- for_each_set_bit(access_bit, &access_req, num_access) {
- if (BIT_ULL(access_bit) & access_mask) {
- (*layer_masks)[access_bit] |=
- BIT_ULL(layer_level);
- handled_accesses |= BIT_ULL(access_bit);
- }
- }
+ masks->access[i] = access_request & handled;
+ handled_accesses |= masks->access[i];
}
+ for (size_t i = domain->num_layers; i < ARRAY_SIZE(masks->access); i++)
+ masks->access[i] = 0;
+
return handled_accesses;
}
diff --git a/security/landlock/ruleset.h b/security/landlock/ruleset.h
index 1a78cba662b2..1ceb5fd674c9 100644
--- a/security/landlock/ruleset.h
+++ b/security/landlock/ruleset.h
@@ -301,15 +301,28 @@ landlock_get_scope_mask(const struct landlock_ruleset *const ruleset,
return ruleset->access_masks[layer_level].scope;
}
+/**
+ * struct layer_access_masks - A boolean matrix of layers and access rights
+ *
+ * This has a bit for each combination of layer numbers and access rights.
+ * During access checks, it is used to represent the access rights for each
+ * layer which still need to be fulfilled. When all bits are 0, the access
+ * request is considered to be fulfilled.
+ */
+struct layer_access_masks {
+ /**
+ * @access: The unfulfilled access rights for each layer.
+ */
+ access_mask_t access[LANDLOCK_MAX_NUM_LAYERS];
+};
+
bool landlock_unmask_layers(const struct landlock_rule *const rule,
- const access_mask_t access_request,
- layer_mask_t (*const layer_masks)[],
- const size_t masks_array_size);
+ struct layer_access_masks *masks);
access_mask_t
landlock_init_layer_masks(const struct landlock_ruleset *const domain,
const access_mask_t access_request,
- layer_mask_t (*const layer_masks)[],
+ struct layer_access_masks *masks,
const enum landlock_key_type key_type);
#endif /* _SECURITY_LANDLOCK_RULESET_H */
--
2.52.0
^ permalink raw reply related
* [PATCH v2 2/3] landlock: access_mask_subset() helper
From: Günther Noack @ 2026-01-25 19:58 UTC (permalink / raw)
To: Mickaël Salaün
Cc: linux-security-module, Tingmao Wang, Justin Suess,
Samasth Norway Ananda, Matthieu Buffet, Mikhail Ivanov,
konstantin.meskhidze, Randy Dunlap, Günther Noack
In-Reply-To: <20260125195853.109967-1-gnoack3000@gmail.com>
This helper function checks whether an access_mask_t has a subset of the
bits enabled than another one. This expresses the intent a bit smoother
in the code and does not cost us anything when it gets inlined.
Signed-off-by: Günther Noack <gnoack3000@gmail.com>
---
security/landlock/access.h | 6 ++++++
security/landlock/fs.c | 2 +-
2 files changed, 7 insertions(+), 1 deletion(-)
diff --git a/security/landlock/access.h b/security/landlock/access.h
index 7961c6630a2d..5c0caef9eaf6 100644
--- a/security/landlock/access.h
+++ b/security/landlock/access.h
@@ -97,4 +97,10 @@ landlock_upgrade_handled_access_masks(struct access_masks access_masks)
return access_masks;
}
+/** access_mask_subset - true iff a has a subset of the bits of b. */
+static inline bool access_mask_subset(access_mask_t a, access_mask_t b)
+{
+ return (a | b) == b;
+}
+
#endif /* _SECURITY_LANDLOCK_ACCESS_H */
diff --git a/security/landlock/fs.c b/security/landlock/fs.c
index 8205673c8b1c..bf8e37fcc7c0 100644
--- a/security/landlock/fs.c
+++ b/security/landlock/fs.c
@@ -1704,7 +1704,7 @@ static int hook_file_open(struct file *const file)
ARRAY_SIZE(layer_masks));
#endif /* CONFIG_AUDIT */
- if ((open_access_request & allowed_access) == open_access_request)
+ if (access_mask_subset(open_access_request, allowed_access))
return 0;
/* Sets access to reflect the actual request. */
--
2.52.0
^ permalink raw reply related
* [PATCH v2 1/3] selftests/landlock: Add filesystem access benchmark
From: Günther Noack @ 2026-01-25 19:58 UTC (permalink / raw)
To: Mickaël Salaün
Cc: linux-security-module, Tingmao Wang, Justin Suess,
Samasth Norway Ananda, Matthieu Buffet, Mikhail Ivanov,
konstantin.meskhidze, Randy Dunlap, Günther Noack
In-Reply-To: <20260125195853.109967-1-gnoack3000@gmail.com>
fs_bench benchmarks the performance of Landlock's path walk
by exercising it in a scenario that amplifies Landlock's overhead:
* Create a large number of nested directories
* Enforce a Landlock policy in which a rule is associated with each of
these subdirectories
* Benchmark openat() applied to the deepest directory,
forcing Landlock to walk the entire path.
Signed-off-by: Günther Noack <gnoack3000@gmail.com>
---
tools/testing/selftests/landlock/.gitignore | 1 +
tools/testing/selftests/landlock/Makefile | 1 +
tools/testing/selftests/landlock/fs_bench.c | 161 ++++++++++++++++++++
3 files changed, 163 insertions(+)
create mode 100644 tools/testing/selftests/landlock/fs_bench.c
diff --git a/tools/testing/selftests/landlock/.gitignore b/tools/testing/selftests/landlock/.gitignore
index a820329cae0d..1974e17a2611 100644
--- a/tools/testing/selftests/landlock/.gitignore
+++ b/tools/testing/selftests/landlock/.gitignore
@@ -1,4 +1,5 @@
/*_test
+/fs_bench
/sandbox-and-launch
/true
/wait-pipe
diff --git a/tools/testing/selftests/landlock/Makefile b/tools/testing/selftests/landlock/Makefile
index 044b83bde16e..fc43225d319a 100644
--- a/tools/testing/selftests/landlock/Makefile
+++ b/tools/testing/selftests/landlock/Makefile
@@ -9,6 +9,7 @@ LOCAL_HDRS += $(wildcard *.h)
src_test := $(wildcard *_test.c)
TEST_GEN_PROGS := $(src_test:.c=)
+TEST_GEN_PROGS += fs_bench
TEST_GEN_PROGS_EXTENDED := \
true \
diff --git a/tools/testing/selftests/landlock/fs_bench.c b/tools/testing/selftests/landlock/fs_bench.c
new file mode 100644
index 000000000000..a3b686418bc5
--- /dev/null
+++ b/tools/testing/selftests/landlock/fs_bench.c
@@ -0,0 +1,161 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Landlock filesystem benchmark
+ */
+
+#define _GNU_SOURCE
+#include <err.h>
+#include <fcntl.h>
+#include <linux/landlock.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/prctl.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/times.h>
+#include <time.h>
+#include <unistd.h>
+
+void usage(const char *argv0)
+{
+ printf("Usage:\n");
+ printf(" %s [OPTIONS]\n", argv0);
+ printf("\n");
+ printf(" Benchmark expensive Landlock checks for D nested dirs\n");
+ printf("\n");
+ printf("Options:\n");
+ printf(" -h help\n");
+ printf(" -L disable Landlock (as a baseline)\n");
+ printf(" -d D set directory depth to D\n");
+ printf(" -n N set number of benchmark iterations to N\n");
+}
+
+/*
+ * Build a deep directory, enforce Landlock and return the FD to the
+ * deepest dir. On any failure, exit the process with an error.
+ */
+int build_directory(size_t depth, bool use_landlock)
+{
+ const char *path = "d"; /* directory name */
+ int abi, ruleset_fd, current, previous;
+
+ if (use_landlock) {
+ abi = syscall(SYS_landlock_create_ruleset, NULL, 0,
+ LANDLOCK_CREATE_RULESET_VERSION);
+ if (abi < 7)
+ err(1, "Landlock ABI too low: got %d, wanted 7+", abi);
+ }
+
+ ruleset_fd = -1;
+ if (use_landlock) {
+ struct landlock_ruleset_attr attr = {
+ .handled_access_fs =
+ 0xffff, /* All FS access rights as of 2026-01 */
+ };
+ ruleset_fd = syscall(SYS_landlock_create_ruleset, &attr,
+ sizeof(attr), 0U);
+ if (ruleset_fd < 0)
+ err(1, "landlock_create_ruleset");
+ }
+
+ current = open(".", O_PATH);
+ if (current < 0)
+ err(1, "open(.)");
+
+ while (depth--) {
+ if (use_landlock) {
+ struct landlock_path_beneath_attr attr = {
+ .allowed_access = LANDLOCK_ACCESS_FS_IOCTL_DEV,
+ .parent_fd = current,
+ };
+ if (syscall(SYS_landlock_add_rule, ruleset_fd,
+ LANDLOCK_RULE_PATH_BENEATH, &attr, 0) < 0)
+ err(1, "landlock_add_rule");
+ }
+
+ if (mkdirat(current, path, 0700) < 0)
+ err(1, "mkdirat(%s)", path);
+
+ previous = current;
+ current = openat(current, path, O_PATH);
+ if (current < 0)
+ err(1, "open(%s)", path);
+
+ close(previous);
+ }
+
+ if (use_landlock) {
+ if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0)
+ err(1, "prctl");
+
+ if (syscall(SYS_landlock_restrict_self, ruleset_fd, 0) < 0)
+ err(1, "landlock_restrict_self");
+ }
+
+ close(ruleset_fd);
+ return current;
+}
+
+int main(int argc, char *argv[])
+{
+ bool use_landlock = true;
+ size_t num_iterations = 100000;
+ size_t num_subdirs = 10000;
+ int c, current, fd;
+ struct tms start_time, end_time;
+
+ setbuf(stdout, NULL);
+ while ((c = getopt(argc, argv, "hLd:n:")) != -1) {
+ switch (c) {
+ case 'h':
+ usage(argv[0]);
+ return EXIT_SUCCESS;
+ case 'L':
+ use_landlock = false;
+ break;
+ case 'd':
+ num_subdirs = atoi(optarg);
+ break;
+ case 'n':
+ num_iterations = atoi(optarg);
+ break;
+ default:
+ usage(argv[0]);
+ return EXIT_FAILURE;
+ }
+ }
+
+ printf("*** Benchmark ***\n");
+ printf("%zu dirs, %zu iterations, %s landlock\n", num_subdirs,
+ num_iterations, use_landlock ? "with" : "without");
+
+ if (times(&start_time) == -1)
+ err(1, "times");
+
+ current = build_directory(num_subdirs, use_landlock);
+
+ for (int i = 0; i < num_iterations; i++) {
+ fd = openat(current, ".", O_DIRECTORY);
+ if (fd != -1) {
+ if (use_landlock)
+ errx(1, "openat succeeded, expected error");
+
+ close(fd);
+ }
+ }
+
+ if (times(&end_time) == -1)
+ err(1, "times");
+
+ printf("*** Benchmark concluded ***\n");
+ printf("System: %ld clocks\n",
+ end_time.tms_stime - start_time.tms_stime);
+ printf("User : %ld clocks\n",
+ end_time.tms_utime - start_time.tms_utime);
+ printf("Clocks per second: %ld\n", CLOCKS_PER_SEC);
+
+ close(current);
+}
--
2.52.0
^ permalink raw reply related
* [PATCH v2 0/3] landlock: Refactor layer masks
From: Günther Noack @ 2026-01-25 19:58 UTC (permalink / raw)
To: Mickaël Salaün
Cc: linux-security-module, Tingmao Wang, Justin Suess,
Samasth Norway Ananda, Matthieu Buffet, Mikhail Ivanov,
konstantin.meskhidze, Randy Dunlap, Günther Noack
Hello!
This patch set "transposes" the layer masks matrix, which was
previously modeled as a access-max-sized array of layer masks, and
changes it to be a layer-max-sized array of access masks instead.
(It is a pure refactoring, there are no user-visible changes.)
This unlocks a few code simplifications and in multiple places it
removes the need for loops and branches that deal with individual
bits. Instead, the changed data structure now lends itself for more
bitwise operations. The underlying hypothesis for me was that by
using more bitwise operations and fewer branches, we would get an
overall speedup even when the data structure size increases slightly
in some cases.
Tentative results with and without this patch set show that the
hypothesis likely holds true. The benchmark I used exercises a "worst
case" scenario that attempts to be bottlenecked on the affected code:
constructs a large number of nested directories, with one "path
beneath" rule each and then tries to open the innermost directory many
times. The benchmark is intentionally unrealistic to amplify the
amount of time used for the path walk logic and forces Landlock to
walk the full path (eventually failing the open syscall). (I'll send
the benchmark program in a reply to this mail for full transparency.)
Measured with the benchmark program, the patch set results in a
speedup of about -10%. The benchmark results are only tentative and
have been produced in Qemu:
With the patch, the benchmark runs in 6046 clocks (measured with
times(3)):
*** Benchmark ***
10000 dirs, 100000 iterations, with landlock
*** Benchmark concluded ***
System: 6046 clocks
User : 1 clocks
Clocks per second: 1000000
Without the patch, we get 6713 clocks, which is 11% more
*** Benchmark ***
10000 dirs, 100000 iterations, with landlock
*** Benchmark concluded ***
System: 6713 clocks
User : 0 clocks
Clocks per second: 1000000
The base revision used for benchmarking was commit 7a51784da76d
("tools/sched_ext: update scx_show_state.py for scx_aborting change")
In real-life scenarios, the speed improvement from this patch set will
be less pronounced than in the artificial benchmark, as people do not
usually stack directories that deeply and attach so many rules to
them, and the EACCES error should also be the exception rather than
the norm.
I am looking forward to your feedback.
P.S.: I am open to suggestions on what the "layer masks" variables
should be called, because the name "layer masks" might be less
appropriate after this change. I have not fixed up the name
everywhere because fixing up the code took priority for now.
---
Changes since previous versions:
V2: (This patch set)
* Remove the refactoring around the deny_mask_t type,
it is better to send that as a separate patch (mic review)
* Added the benchmark program to the selftests
* Fix unused variable report for "access_dom":
https://lore.kernel.org/all/202601200900.wonk9M0m-lkp@intel.com/
* Use size_t and ARRAY_SIZE to loop over the layers (mic review)
* Documentation
* Fixing up and adding back documentaiton (mic review)
* Documented landlock_unmask_layers()
* Fixed up kernel docs in a place where it was improperly updated
(Spotted by Randy Dunlap
https://lore.kernel.org/all/20260123025121.3713403-1-rdunlap@infradead.org/)
* Minor
* Const, some newlines (mic review)
V1: (Initial version)
https://lore.kernel.org/all/20251230103917.10549-3-gnoack3000@gmail.com/
Günther Noack (3):
selftests/landlock: Add filesystem access benchmark
landlock: access_mask_subset() helper
landlock: transpose the layer masks data structure
security/landlock/access.h | 16 +-
security/landlock/audit.c | 84 ++---
security/landlock/audit.h | 3 +-
security/landlock/domain.c | 45 +--
security/landlock/domain.h | 4 +-
security/landlock/fs.c | 354 +++++++++-----------
security/landlock/net.c | 11 +-
security/landlock/ruleset.c | 88 ++---
security/landlock/ruleset.h | 21 +-
tools/testing/selftests/landlock/.gitignore | 1 +
tools/testing/selftests/landlock/Makefile | 1 +
tools/testing/selftests/landlock/fs_bench.c | 161 +++++++++
12 files changed, 444 insertions(+), 345 deletions(-)
create mode 100644 tools/testing/selftests/landlock/fs_bench.c
--
2.52.0
^ permalink raw reply
* Re: [PATCH] rust: security: use `pin_init::zeroed()` for LSM context initialization
From: Miguel Ojeda @ 2026-01-25 19:27 UTC (permalink / raw)
To: Atharv Dubey, Benno Lossin
Cc: paul, jmorris, serge, ojeda, alex.gaynor, boqun.feng, gary,
bjorn3_gh, a.hindborg, aliceryhl, tmgross, dakr,
linux-security-module, rust-for-linux, linux-kernel
In-Reply-To: <20251129135657.36144-1-atharvd440@gmail.com>
On Sat, Nov 29, 2025 at 2:57 PM Atharv Dubey <atharvd440@gmail.com> wrote:
>
> Replace the previous `unsafe { core::mem::zeroed() }` initialization of
> `bindings::lsm_context` with `pin_init::zeroed()`.
>
> Link: https://github.com/Rust-for-Linux/linux/issues/1189
> Signed-off-by: Atharv Dubey <atharvd440@gmail.com>
These were also sent by Benno, and in the issue he mentions
"re-sending", which usually means just adding your Signed-off-by below
his:
https://lore.kernel.org/all/20250814093046.2071971-1-lossin@kernel.org/
Otherwise, if he is OK without authorship, this should most likely have:
Suggested-by: Benno Lossin <lossin@kernel.org>
Cheers,
Miguel
^ permalink raw reply
* [PATCH v9 11/11] tpm-buf: Implement managed allocations
From: Jarkko Sakkinen @ 2026-01-25 19:25 UTC (permalink / raw)
To: linux-integrity
Cc: Jarkko Sakkinen, Ross Philipson, Stefan Berger, James Bottomley,
Jarkko Sakkinen, Mimi Zohar, David Howells, Paul Moore,
James Morris, Serge E. Hallyn, open list:KEYS-TRUSTED,
open list:SECURITY SUBSYSTEM, open list
In-Reply-To: <20260125192526.782202-1-jarkko@kernel.org>
From: Jarkko Sakkinen <jarkko.sakkinen@opinsys.com>
Decouple kzalloc from buffer creation, so that a managed allocation can be
used:
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));
This is achieved by embedding buffer's header inside the allocated blob,
instead of having an outer wrapper.
Cc: Ross Philipson <ross.philipson@oracle.com>
Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@opinsys.com>
Reviewed-by: Stefan Berger <stefanb@linux.ibm.com>
---
drivers/char/tpm/tpm-buf.c | 123 +++++----
drivers/char/tpm/tpm-sysfs.c | 21 +-
drivers/char/tpm/tpm.h | 1 -
drivers/char/tpm/tpm1-cmd.c | 163 +++++-------
drivers/char/tpm/tpm2-cmd.c | 298 ++++++++++------------
drivers/char/tpm/tpm2-sessions.c | 142 +++++------
drivers/char/tpm/tpm2-space.c | 44 ++--
drivers/char/tpm/tpm_vtpm_proxy.c | 30 +--
include/linux/tpm.h | 20 +-
security/keys/trusted-keys/trusted_tpm1.c | 36 +--
security/keys/trusted-keys/trusted_tpm2.c | 173 ++++++-------
11 files changed, 499 insertions(+), 552 deletions(-)
diff --git a/drivers/char/tpm/tpm-buf.c b/drivers/char/tpm/tpm-buf.c
index 11b6bcf2f43d..042da92f94e1 100644
--- a/drivers/char/tpm/tpm-buf.c
+++ b/drivers/char/tpm/tpm-buf.c
@@ -7,81 +7,107 @@
#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);
head->tag = cpu_to_be16(tag);
head->length = cpu_to_be32(sizeof(*head));
head->ordinal = cpu_to_be32(ordinal);
}
-EXPORT_SYMBOL_GPL(tpm_buf_reset);
+
+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->data[0] = 0;
+ buf->data[1] = 0;
+}
/**
- * 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
@@ -89,8 +115,11 @@ EXPORT_SYMBOL_GPL(tpm_buf_destroy);
*
* Return: The number of bytes consumed by the buffer
*/
-u32 tpm_buf_length(struct tpm_buf *buf)
+u16 tpm_buf_length(struct tpm_buf *buf)
{
+ if (buf->flags & TPM_BUF_INVALID)
+ return 0;
+
return buf->length;
}
EXPORT_SYMBOL_GPL(tpm_buf_length);
@@ -103,10 +132,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 4a6a27ee295d..0f321c307bc6 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 02c07fef41ba..5395927c62fc 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 3fe94f596f97..a04abe13a764 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,47 @@ 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;
-
- 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;
+ 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_PCR_EXTEND);
+ 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);
@@ -526,74 +517,61 @@ struct tpm1_get_random_out {
int tpm1_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
{
struct tpm1_get_random_out *resp;
- struct tpm_buf buf;
u32 recd;
int rc;
if (!dest || !max || max > TPM_MAX_RNG_DATA)
return -EINVAL;
- 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_append_u32(&buf, max);
+ tpm_buf_init(buf, TPM_BUFSIZE);
+ tpm_buf_reset(buf, TPM_TAG_RQU_COMMAND, TPM_ORD_GET_RANDOM);
+ tpm_buf_append_u32(buf, max);
- rc = tpm_transmit_cmd(chip, &buf, sizeof(resp->rng_data_len), "TPM_GetRandom");
+ rc = tpm_transmit_cmd(chip, buf, sizeof(resp->rng_data_len), "TPM_GetRandom");
if (rc) {
if (rc > 0)
rc = -EIO;
- goto out;
+ return rc;
}
- resp = (struct tpm1_get_random_out *)&buf.data[TPM_HEADER_SIZE];
+ resp = (struct tpm1_get_random_out *)&buf->data[TPM_HEADER_SIZE];
recd = be32_to_cpu(resp->rng_data_len);
- if (recd > max) {
- rc = -EIO;
- goto out;
- }
+ if (recd > max)
+ return -EIO;
- if (buf.length < TPM_HEADER_SIZE + sizeof(resp->rng_data_len) + recd) {
- rc = -EIO;
- goto out;
- }
+ if (buf->length < TPM_HEADER_SIZE + sizeof(resp->rng_data_len) + recd)
+ return -EIO;
memcpy(dest, resp->rng_data, recd);
- tpm_buf_destroy(&buf);
return recd;
-
-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;
}
@@ -607,16 +585,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");
}
/**
@@ -730,22 +705,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
@@ -760,7 +737,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)
@@ -770,8 +747,6 @@ 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 39ef6b7a0fbc..7455c492fc66 100644
--- a/drivers/char/tpm/tpm2-cmd.c
+++ b/drivers/char/tpm/tpm2-cmd.c
@@ -119,12 +119,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;
@@ -139,36 +142,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 -EINVAL;
if (digest_size_ptr)
*digest_size_ptr = digest_size;
memcpy(digest->digest, out->digest, digest_size);
-out:
- tpm_buf_destroy(&buf);
return rc;
}
@@ -184,57 +182,53 @@ 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) {
- rc = tpm_buf_append_name(chip, &buf, pcr_idx, (u8 *)&pcr_idx,
+ rc = tpm_buf_append_name(chip, buf, pcr_idx, (u8 *)&pcr_idx,
sizeof(u32));
- if (rc) {
- tpm_buf_destroy(&buf);
+ if (rc)
return rc;
- }
- tpm_buf_append_hmac_session(chip, &buf, 0, NULL, 0);
+ tpm_buf_append_hmac_session(chip, buf, 0, NULL, 0);
} else {
- tpm_buf_append_u32(&buf, pcr_idx);
- tpm_buf_append_auth(chip, &buf, NULL, 0);
+ tpm_buf_append_u32(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) {
- rc = tpm_buf_fill_hmac_session(chip, &buf);
- if (rc) {
- tpm_buf_destroy(&buf);
+ rc = tpm_buf_fill_hmac_session(chip, buf);
+ if (rc)
return rc;
- }
}
- rc = tpm_transmit_cmd(chip, &buf, 0, "attempting extend a PCR value");
- if (!disable_pcr_integrity)
- rc = tpm_buf_check_hmac_response(chip, &buf, rc);
+ rc = tpm_transmit_cmd(chip, buf, 0, "TPM2_PCR_Extend");
- tpm_buf_destroy(&buf);
+ if (!disable_pcr_integrity)
+ rc = tpm_buf_check_hmac_response(chip, buf, rc);
return rc;
}
@@ -254,7 +248,6 @@ int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
{
struct tpm2_get_random_out *resp;
struct tpm_header *head;
- struct tpm_buf buf;
off_t offset;
u32 recd;
int err;
@@ -266,32 +259,32 @@ int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
if (err)
return err;
- err = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_GET_RANDOM);
- if (err) {
+ struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+ if (!buf) {
tpm2_end_auth_session(chip);
- return err;
+ return -ENOMEM;
}
+ tpm_buf_init(buf, TPM_BUFSIZE);
+ 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 {
- head = (struct tpm_header *)buf.data;
+ head = (struct tpm_header *)buf->data;
head->tag = cpu_to_be16(TPM2_ST_NO_SESSIONS);
}
- tpm_buf_append_u16(&buf, max);
+ tpm_buf_append_u16(buf, max);
- err = tpm_buf_fill_hmac_session(chip, &buf);
- if (err) {
- tpm_buf_destroy(&buf);
+ err = tpm_buf_fill_hmac_session(chip, buf);
+ if (err)
return err;
- }
- err = tpm_transmit_cmd(chip, &buf, offsetof(struct tpm2_get_random_out, buffer),
+ err = tpm_transmit_cmd(chip, buf, offsetof(struct tpm2_get_random_out, buffer),
"TPM2_GetRandom");
- err = tpm_buf_check_hmac_response(chip, &buf, err);
+ err = tpm_buf_check_hmac_response(chip, buf, err);
if (err) {
if (err > 0)
err = -EIO;
@@ -299,17 +292,17 @@ int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
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;
- resp = (struct tpm2_get_random_out *)&buf.data[offset];
+ resp = (struct tpm2_get_random_out *)&buf->data[offset];
recd = min_t(u32, be16_to_cpu(resp->size), max);
- if (tpm_buf_length(&buf) <
+ if (tpm_buf_length(buf) <
TPM_HEADER_SIZE + offsetof(struct tpm2_get_random_out, buffer) + recd) {
err = -EIO;
goto out;
@@ -319,7 +312,6 @@ int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
return recd;
out:
- tpm_buf_destroy(&buf);
tpm2_end_auth_session(chip);
return err;
}
@@ -331,20 +323,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);
@@ -371,19 +361,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
@@ -395,7 +386,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);
@@ -412,15 +402,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");
}
/**
@@ -438,20 +427,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)
@@ -476,23 +464,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);
@@ -532,7 +521,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;
@@ -544,39 +532,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: out of bank capacity: %u > %u\n",
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);
@@ -588,7 +574,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++;
}
@@ -600,21 +586,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;
@@ -631,30 +617,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;
@@ -666,8 +646,6 @@ int tpm2_get_cc_attrs_tbl(struct tpm_chip *chip)
}
}
- tpm_buf_destroy(&buf);
-
out:
if (rc > 0)
rc = -ENODEV;
@@ -688,20 +666,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 016bed63a755..0926da478932 100644
--- a/drivers/char/tpm/tpm2-sessions.c
+++ b/drivers/char/tpm/tpm2-sessions.c
@@ -175,7 +175,6 @@ int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name)
u32 mso = tpm2_handle_mso(handle);
off_t offset = TPM_HEADER_SIZE;
int rc, name_size_alg;
- struct tpm_buf buf;
if (mso != TPM2_MSO_PERSISTENT && mso != TPM2_MSO_VOLATILE &&
mso != TPM2_MSO_NVRAM) {
@@ -183,47 +182,41 @@ int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name)
return sizeof(u32);
}
- 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);
+ 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) {
- tpm_buf_destroy(&buf);
+ rc = tpm_transmit_cmd(chip, buf, 0, "TPM2_ReadPublic");
+ if (rc)
return tpm_ret_to_err(rc);
- }
/* Skip TPMT_PUBLIC: */
- offset += tpm_buf_read_u16(&buf, &offset);
+ offset += tpm_buf_read_u16(buf, &offset);
/*
* Ensure space for the length field of TPM2B_NAME and hashAlg field of
* TPMT_HA (the extra four bytes).
*/
- if (offset + 4 > tpm_buf_length(&buf)) {
- tpm_buf_destroy(&buf);
+ if (offset + 4 > tpm_buf_length(buf))
return -EIO;
- }
- rc = tpm_buf_read_u16(&buf, &offset);
- name_size_alg = name_size(&buf.data[offset]);
+ rc = tpm_buf_read_u16(buf, &offset);
+ name_size_alg = name_size(&buf->data[offset]);
if (name_size_alg < 0)
return name_size_alg;
- if (rc != name_size_alg) {
- tpm_buf_destroy(&buf);
+ if (rc != name_size_alg)
return -EIO;
- }
- if (offset + rc > tpm_buf_length(&buf)) {
- tpm_buf_destroy(&buf);
+ if (offset + rc > tpm_buf_length(buf))
return -EIO;
- }
- memcpy(name, &buf.data[offset], rc);
+ memcpy(name, &buf->data[offset], rc);
return name_size_alg;
}
EXPORT_SYMBOL_GPL(tpm2_read_public);
@@ -929,7 +922,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;
@@ -938,6 +930,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;
@@ -948,41 +944,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;
@@ -1204,18 +1196,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
@@ -1227,75 +1219,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 60354cd53b5c..cbf86ff5931f 100644
--- a/drivers/char/tpm/tpm2-space.c
+++ b/drivers/char/tpm/tpm2-space.c
@@ -71,24 +71,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) {
@@ -103,64 +104,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 5a5d8e584e7b..01216156a1ec 100644
--- a/include/linux/tpm.h
+++ b/include/linux/tpm.h
@@ -25,7 +25,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
/*
* SHA-512 is, as of today, the largest digest in the TCG algorithm repository.
@@ -389,12 +390,14 @@ 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;
+ u16 length;
+ u16 capacity;
+ u8 data[];
};
enum tpm2_object_attributes {
@@ -425,12 +428,11 @@ 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);
+u16 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);
void tpm_buf_append_u16(struct tpm_buf *buf, const u16 value);
diff --git a/security/keys/trusted-keys/trusted_tpm1.c b/security/keys/trusted-keys/trusted_tpm1.c
index 3d75bb6f9689..368e420c18af 100644
--- a/security/keys/trusted-keys/trusted_tpm1.c
+++ b/security/keys/trusted-keys/trusted_tpm1.c
@@ -320,9 +320,10 @@ 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 cmd_len)
{
- struct tpm_buf buf;
+ u8 buf_data[512];
+ struct tpm_buf *buf = (struct tpm_buf *)buf_data;
int rc;
if (!chip)
@@ -332,11 +333,10 @@ static int trusted_tpm_send(unsigned char *cmd, size_t buflen)
if (rc)
return rc;
- buf.flags = 0;
- buf.length = buflen;
- buf.data = cmd;
+ tpm_buf_init(buf, sizeof(buf_data));
+ tpm_buf_append(buf, cmd, cmd_len);
dump_tpm_buf(cmd);
- rc = tpm_transmit_cmd(chip, &buf, 4, "sending data");
+ rc = tpm_transmit_cmd(chip, buf, 4, "sending data");
dump_tpm_buf(cmd);
if (rc > 0)
@@ -622,23 +622,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;
}
@@ -648,14 +648,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);
@@ -663,7 +664,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 8e694318b200..05c255e67e06 100644
--- a/security/keys/trusted-keys/trusted_tpm2.c
+++ b/security/keys/trusted-keys/trusted_tpm2.c
@@ -205,7 +205,6 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
{
u8 parent_name[TPM2_MAX_NAME_SIZE];
off_t offset = TPM_HEADER_SIZE;
- struct tpm_buf buf, sized;
u16 parent_name_size;
int blob_len = 0;
int hash;
@@ -233,98 +232,100 @@ 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) {
+ struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+ if (!buf) {
+ rc = -ENOMEM;
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_init(buf, TPM_BUFSIZE);
+ tpm_buf_reset(buf, TPM2_ST_SESSIONS, TPM2_CC_CREATE);
- rc = tpm_buf_append_name(chip, &buf, options->keyhandle, parent_name,
+ rc = tpm_buf_append_name(chip, buf, options->keyhandle, parent_name,
parent_name_size);
if (rc)
- goto out;
+ goto out_put;
- tpm_buf_append_hmac_session(chip, &buf, TPM2_SA_DECRYPT,
+ tpm_buf_append_hmac_session(chip, buf, TPM2_SA_DECRYPT,
options->keyauth, TPM_DIGEST_SIZE);
+ struct tpm_buf *sized __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+ if (!sized) {
+ rc = -ENOMEM;
+ tpm2_end_auth_session(chip);
+ goto out_put;
+ }
+
/* sensitive */
- tpm_buf_append_u16(&sized, options->blobauth_len);
+ tpm_buf_init_sized(sized, TPM_BUFSIZE);
+ 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;
}
- rc = tpm_buf_fill_hmac_session(chip, &buf);
+ rc = tpm_buf_fill_hmac_session(chip, buf);
if (rc)
goto out;
- rc = tpm_transmit_cmd(chip, &buf, 4, "sealing data");
- rc = tpm_buf_check_hmac_response(chip, &buf, rc);
+ 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;
@@ -356,7 +357,6 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
u32 *blob_handle)
{
u8 *blob_ref __free(kfree) = NULL;
- struct tpm_buf buf;
unsigned int private_len;
unsigned int public_len;
unsigned int blob_len;
@@ -396,40 +396,39 @@ 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) {
+ struct tpm_buf *buf __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+ if (!buf) {
tpm2_end_auth_session(chip);
- return rc;
+ return -ENOMEM;
}
- rc = tpm_buf_append_name(chip, &buf, options->keyhandle, parent_name,
+ tpm_buf_init(buf, TPM_BUFSIZE);
+ tpm_buf_reset(buf, TPM2_ST_SESSIONS, TPM2_CC_LOAD);
+
+ rc = tpm_buf_append_name(chip, buf, options->keyhandle, parent_name,
parent_name_size);
if (rc)
- goto out;
+ return rc;
- tpm_buf_append_hmac_session(chip, &buf, 0, options->keyauth,
+ 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) {
- rc = -E2BIG;
+ if (buf->flags & TPM_BUF_INVALID) {
tpm2_end_auth_session(chip);
- goto out;
+ return -E2BIG;
}
- rc = tpm_buf_fill_hmac_session(chip, &buf);
+ rc = tpm_buf_fill_hmac_session(chip, buf);
if (rc)
- goto out;
+ return rc;
- rc = tpm_transmit_cmd(chip, &buf, 4, "loading blob");
- rc = tpm_buf_check_hmac_response(chip, &buf, rc);
+ 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]);
-
-out:
- tpm_buf_destroy(&buf);
+ (__be32 *)&buf->data[TPM_HEADER_SIZE]);
return tpm_ret_to_err(rc);
}
@@ -454,28 +453,28 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
u16 parent_name_size,
u32 blob_handle)
{
- struct tpm_buf buf;
u16 data_len;
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_init(buf, TPM_BUFSIZE);
+ tpm_buf_reset(buf, TPM2_ST_SESSIONS, TPM2_CC_UNSEAL);
- rc = tpm_buf_append_name(chip, &buf, blob_handle, parent_name,
+ rc = tpm_buf_append_name(chip, buf, blob_handle, parent_name,
parent_name_size);
if (rc)
- goto out;
+ return rc;
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 {
@@ -490,37 +489,33 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
* could repeat our actions with the exfiltrated
* password.
*/
- 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);
}
- rc = tpm_buf_fill_hmac_session(chip, &buf);
+ rc = tpm_buf_fill_hmac_session(chip, buf);
if (rc)
- goto out;
+ return rc;
- rc = tpm_transmit_cmd(chip, &buf, 6, "unsealing");
- rc = tpm_buf_check_hmac_response(chip, &buf, rc);
+ 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 */
@@ -537,8 +532,6 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
}
}
-out:
- tpm_buf_destroy(&buf);
return tpm_ret_to_err(rc);
}
--
2.52.0
^ permalink raw reply related
* [PATCH v9 10/11] tpm-buf: Merge TPM_BUF_BOUNDARY_ERROR and TPM_BUF_OVERFLOW
From: Jarkko Sakkinen @ 2026-01-25 19:25 UTC (permalink / raw)
To: linux-integrity
Cc: Jarkko Sakkinen, Jonathan McDowell, James Bottomley,
Jarkko Sakkinen, Mimi Zohar, David Howells, Paul Moore,
James Morris, Serge E. Hallyn, open list:KEYS-TRUSTED,
open list:SECURITY SUBSYSTEM, open list
In-Reply-To: <20260125192526.782202-1-jarkko@kernel.org>
From: Jarkko Sakkinen <jarkko.sakkinen@opinsys.com>
Merge TPM_BUF_BOUNDARY_ERROR and TPM_BUF_OVERFLOW into TPM_BUF_INVALID,
given that they are identical. The only difference are the log messages.
In addition, add a missing TPM_BUF_INVALID check to 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>
---
drivers/char/tpm/tpm-buf.c | 10 ++++------
include/linux/tpm.h | 8 +++-----
security/keys/trusted-keys/trusted_tpm2.c | 6 +++---
3 files changed, 10 insertions(+), 14 deletions(-)
diff --git a/drivers/char/tpm/tpm-buf.c b/drivers/char/tpm/tpm-buf.c
index 1d2b87455d3a..11b6bcf2f43d 100644
--- a/drivers/char/tpm/tpm-buf.c
+++ b/drivers/char/tpm/tpm-buf.c
@@ -103,13 +103,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;
}
@@ -156,14 +155,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 e8339f0c9a2e..5a5d8e584e7b 100644
--- a/include/linux/tpm.h
+++ b/include/linux/tpm.h
@@ -382,12 +382,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 dbacebe7af56..8e694318b200 100644
--- a/security/keys/trusted-keys/trusted_tpm2.c
+++ b/security/keys/trusted-keys/trusted_tpm2.c
@@ -292,7 +292,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;
}
@@ -412,7 +412,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.52.0
^ permalink raw reply related
* [PATCH v9 09/11] tpm-buf: Remove tpm_buf_append_handle
From: Jarkko Sakkinen @ 2026-01-25 19:25 UTC (permalink / raw)
To: linux-integrity
Cc: Jarkko Sakkinen, James Bottomley, Mimi Zohar, David Howells,
Paul Moore, James Morris, Serge E. Hallyn, open list:KEYS-TRUSTED,
open list:SECURITY SUBSYSTEM, open list
In-Reply-To: <20260125192526.782202-1-jarkko@kernel.org>
Since the number of handles is fixed to a single handle, eliminate all uses
of buf->handles and deduce values during compile-time.
Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
---
v2:
- Streamline the code change and remove dead code.
---
drivers/char/tpm/tpm-buf.c | 23 -----------------------
drivers/char/tpm/tpm2-cmd.c | 2 +-
drivers/char/tpm/tpm2-sessions.c | 14 ++------------
include/linux/tpm.h | 2 --
4 files changed, 3 insertions(+), 38 deletions(-)
diff --git a/drivers/char/tpm/tpm-buf.c b/drivers/char/tpm/tpm-buf.c
index dc882fc9fa9e..1d2b87455d3a 100644
--- a/drivers/char/tpm/tpm-buf.c
+++ b/drivers/char/tpm/tpm-buf.c
@@ -44,7 +44,6 @@ void tpm_buf_reset(struct tpm_buf *buf, u16 tag, u32 ordinal)
head->tag = cpu_to_be16(tag);
head->length = cpu_to_be32(sizeof(*head));
head->ordinal = cpu_to_be32(ordinal);
- buf->handles = 0;
}
EXPORT_SYMBOL_GPL(tpm_buf_reset);
@@ -146,26 +145,6 @@ void tpm_buf_append_u32(struct tpm_buf *buf, const u32 value)
}
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)
-{
- if (buf->flags & TPM_BUF_TPM2B) {
- dev_err(&chip->dev, "Invalid buffer type (TPM2B)\n");
- return;
- }
-
- tpm_buf_append_u32(buf, handle);
- buf->handles++;
-}
-
/**
* tpm_buf_read() - Read from a TPM buffer
* @buf: &tpm_buf instance
@@ -242,5 +221,3 @@ u32 tpm_buf_read_u32(struct tpm_buf *buf, off_t *offset)
return be32_to_cpu(value);
}
EXPORT_SYMBOL_GPL(tpm_buf_read_u32);
-
-
diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c
index 96a45391036c..39ef6b7a0fbc 100644
--- a/drivers/char/tpm/tpm2-cmd.c
+++ b/drivers/char/tpm/tpm2-cmd.c
@@ -210,7 +210,7 @@ int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
}
tpm_buf_append_hmac_session(chip, &buf, 0, NULL, 0);
} else {
- tpm_buf_append_handle(chip, &buf, pcr_idx);
+ tpm_buf_append_u32(&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 98cea20040cf..016bed63a755 100644
--- a/drivers/char/tpm/tpm2-sessions.c
+++ b/drivers/char/tpm/tpm2-sessions.c
@@ -268,7 +268,7 @@ int tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
}
if (!tpm2_chip_auth(chip)) {
- tpm_buf_append_handle(chip, buf, handle);
+ tpm_buf_append_u32(buf, handle);
return 0;
}
@@ -295,17 +295,7 @@ EXPORT_SYMBOL_GPL(tpm_buf_append_name);
void tpm_buf_append_auth(struct tpm_chip *chip, struct tpm_buf *buf,
u8 *passphrase, int passphrase_len)
{
- /* offset tells us where the sessions area begins */
- int offset = buf->handles * 4 + TPM_HEADER_SIZE;
- u32 len = 9 + passphrase_len;
-
- if (tpm_buf_length(buf) != offset) {
- /* not the first session so update the existing length */
- len += get_unaligned_be32(&buf->data[offset]);
- put_unaligned_be32(len, &buf->data[offset]);
- } else {
- tpm_buf_append_u32(buf, len);
- }
+ tpm_buf_append_u32(buf, 9 + passphrase_len);
/* auth handle */
tpm_buf_append_u32(buf, TPM2_RS_PW);
/* nonce */
diff --git a/include/linux/tpm.h b/include/linux/tpm.h
index 7468709ad245..e8339f0c9a2e 100644
--- a/include/linux/tpm.h
+++ b/include/linux/tpm.h
@@ -397,7 +397,6 @@ struct tpm_buf {
u32 flags;
u32 length;
u8 *data;
- u8 handles;
};
enum tpm2_object_attributes {
@@ -441,7 +440,6 @@ 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);
/*
* Check if TPM device is in the firmware upgrade mode.
--
2.52.0
^ permalink raw reply related
* [PATCH v9 08/11] tpm2-sessions: Remove the support for more than one authorization
From: Jarkko Sakkinen @ 2026-01-25 19:25 UTC (permalink / raw)
To: linux-integrity
Cc: Jarkko Sakkinen, James Bottomley, Mimi Zohar, David Howells,
Paul Moore, James Morris, Serge E. Hallyn, open list:KEYS-TRUSTED,
open list:SECURITY SUBSYSTEM, open list
In-Reply-To: <20260125192526.782202-1-jarkko@kernel.org>
Kernel uses at most a single HMAC authorization at a time.
From that basis, remove the unused machinery for managing multiple
authorizations.
Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
---
v8:
- Rewrote the commit message.
- Added an inline comment explaining why unconditional sha256_update() call
for the name is safe.
---
drivers/char/tpm/tpm2-sessions.c | 40 ++++++++++++++------------------
1 file changed, 18 insertions(+), 22 deletions(-)
diff --git a/drivers/char/tpm/tpm2-sessions.c b/drivers/char/tpm/tpm2-sessions.c
index 3bc3c31cf512..98cea20040cf 100644
--- a/drivers/char/tpm/tpm2-sessions.c
+++ b/drivers/char/tpm/tpm2-sessions.c
@@ -72,9 +72,6 @@
#include <crypto/sha2.h>
#include <crypto/utils.h>
-/* maximum number of names the TPM must remember for authorization */
-#define AUTH_MAX_NAMES 3
-
#define AES_KEY_BYTES AES_KEYSIZE_128
#define AES_KEY_BITS (AES_KEY_BYTES*8)
@@ -136,8 +133,8 @@ struct tpm2_auth {
* handle, but they are part of the session by name, which
* we must compute and remember
*/
- u8 name[AUTH_MAX_NAMES][TPM2_MAX_NAME_SIZE];
- u16 name_size_tbl[AUTH_MAX_NAMES];
+ u8 name[TPM2_MAX_NAME_SIZE];
+ u16 name_size;
};
#ifdef CONFIG_TCG_TPM2_HMAC
@@ -261,11 +258,14 @@ EXPORT_SYMBOL_GPL(tpm2_read_public);
int tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
u32 handle, u8 *name, u16 name_size)
{
-#ifdef CONFIG_TCG_TPM2_HMAC
struct tpm2_auth *auth;
- int slot;
int ret;
-#endif
+
+ if (tpm_buf_length(buf) != TPM_HEADER_SIZE) {
+ dev_err(&chip->dev, "too many handles\n");
+ ret = -EIO;
+ goto err;
+ }
if (!tpm2_chip_auth(chip)) {
tpm_buf_append_handle(chip, buf, handle);
@@ -273,12 +273,6 @@ int tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
}
#ifdef CONFIG_TCG_TPM2_HMAC
- slot = (tpm_buf_length(buf) - TPM_HEADER_SIZE) / 4;
- if (slot >= AUTH_MAX_NAMES) {
- dev_err(&chip->dev, "too many handles\n");
- ret = -EIO;
- goto err;
- }
auth = chip->auth;
if (auth->session != tpm_buf_length(buf)) {
dev_err(&chip->dev, "session state malformed");
@@ -287,16 +281,14 @@ int tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
}
tpm_buf_append_u32(buf, handle);
auth->session += 4;
- memcpy(auth->name[slot], name, name_size);
- auth->name_size_tbl[slot] = name_size;
+ memcpy(auth->name, name, name_size);
+ auth->name_size = name_size;
#endif
return 0;
-#ifdef CONFIG_TCG_TPM2_HMAC
err:
tpm2_end_auth_session(chip);
return ret;
-#endif
}
EXPORT_SYMBOL_GPL(tpm_buf_append_name);
@@ -662,14 +654,18 @@ int tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
}
sha256_init(&sctx);
- /* ordinal is already BE */
sha256_update(&sctx, (u8 *)&head->ordinal, sizeof(head->ordinal));
- /* add the handle names */
- for (i = 0; i < handles; i++)
- sha256_update(&sctx, auth->name[i], auth->name_size_tbl[i]);
+
+ /*
+ * If tpm2_buf_append_name() has not been called, this is a no-op, as
+ * auth->name_size is zero.
+ */
+ sha256_update(&sctx, auth->name, auth->name_size);
+
if (offset_s != tpm_buf_length(buf))
sha256_update(&sctx, &buf->data[offset_s],
tpm_buf_length(buf) - offset_s);
+
sha256_final(&sctx, cphash);
/* now calculate the hmac */
--
2.52.0
^ permalink raw reply related
* [PATCH v9 07/11] KEYS: trusted: Re-orchestrate tpm2_read_public() calls
From: Jarkko Sakkinen @ 2026-01-25 19:25 UTC (permalink / raw)
To: linux-integrity
Cc: Jarkko Sakkinen, James Bottomley, Mimi Zohar, David Howells,
Paul Moore, James Morris, Serge E. Hallyn, open list:KEYS-TRUSTED,
open list:SECURITY SUBSYSTEM, open list
In-Reply-To: <20260125192526.782202-1-jarkko@kernel.org>
tpm2_load_cmd() and tpm2_unseal_cmd() use the same parent, and calls to
tpm_buf_append_name() cause the exact same TPM2_ReadPublic command to be
sent to the chip, causing unnecessary traffic.
1. Export tpm2_read_public in order to make it callable from
'trusted_tpm2'.
2. Re-orchestrate tpm2_seal_trusted() and tpm2_unseal_trusted() in order to
halve the name resolutions required:
2a. Move tpm2_read_public() calls into trusted_tpm2.
2b. Pass TPM name to tpm_buf_append_name().
2c. Rework tpm_buf_append_name() to use the pre-resolved name.
Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
---
drivers/char/tpm/tpm2-cmd.c | 3 +-
drivers/char/tpm/tpm2-sessions.c | 95 +++++------------
include/linux/tpm.h | 10 +-
security/keys/trusted-keys/trusted_tpm2.c | 122 ++++++++++++++--------
4 files changed, 117 insertions(+), 113 deletions(-)
diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c
index 5f15d8a93e23..96a45391036c 100644
--- a/drivers/char/tpm/tpm2-cmd.c
+++ b/drivers/char/tpm/tpm2-cmd.c
@@ -202,7 +202,8 @@ int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
}
if (!disable_pcr_integrity) {
- rc = tpm_buf_append_name(chip, &buf, pcr_idx, NULL);
+ rc = tpm_buf_append_name(chip, &buf, pcr_idx, (u8 *)&pcr_idx,
+ sizeof(u32));
if (rc) {
tpm_buf_destroy(&buf);
return rc;
diff --git a/drivers/char/tpm/tpm2-sessions.c b/drivers/char/tpm/tpm2-sessions.c
index 525b8622d1c3..3bc3c31cf512 100644
--- a/drivers/char/tpm/tpm2-sessions.c
+++ b/drivers/char/tpm/tpm2-sessions.c
@@ -136,8 +136,8 @@ struct tpm2_auth {
* handle, but they are part of the session by name, which
* we must compute and remember
*/
- u32 name_h[AUTH_MAX_NAMES];
u8 name[AUTH_MAX_NAMES][TPM2_MAX_NAME_SIZE];
+ u16 name_size_tbl[AUTH_MAX_NAMES];
};
#ifdef CONFIG_TCG_TPM2_HMAC
@@ -163,7 +163,17 @@ static int name_size(const u8 *name)
}
}
-static int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name)
+/**
+ * tpm2_read_public: Resolve TPM name for a handle
+ * @chip: TPM chip to use.
+ * @handle: TPM handle.
+ * @name: A buffer for returning the name blob. Must have a
+ * capacity of 'SHA512_DIGET_SIZE + 2' bytes at minimum
+ *
+ * Returns size of TPM handle name of success.
+ * Returns tpm_transmit_cmd error codes when TPM2_ReadPublic fails.
+ */
+int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name)
{
u32 mso = tpm2_handle_mso(handle);
off_t offset = TPM_HEADER_SIZE;
@@ -219,14 +229,16 @@ static int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name)
memcpy(name, &buf.data[offset], rc);
return name_size_alg;
}
+EXPORT_SYMBOL_GPL(tpm2_read_public);
#endif /* CONFIG_TCG_TPM2_HMAC */
/**
- * tpm_buf_append_name() - add a handle area to the buffer
- * @chip: the TPM chip structure
- * @buf: The buffer to be appended
- * @handle: The handle to be appended
- * @name: The name of the handle (may be NULL)
+ * tpm_buf_append_name() - Append a handle and store TPM name
+ * @chip: TPM chip to use.
+ * @buf: TPM buffer containing the TPM command in-transit.
+ * @handle: TPM handle to be appended.
+ * @name: TPM name of the handle
+ * @name_size: Size of the TPM name.
*
* In order to compute session HMACs, we need to know the names of the
* objects pointed to by the handles. For most objects, this is simply
@@ -243,15 +255,14 @@ static int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name)
* will be caused by an incorrect programming model and indicated by a
* kernel message.
*
- * Ends the authorization session on failure.
+ * Returns zero on success.
+ * Returns -EIO when the authorization area state is malformed.
*/
int tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
- u32 handle, u8 *name)
+ u32 handle, u8 *name, u16 name_size)
{
#ifdef CONFIG_TCG_TPM2_HMAC
- enum tpm2_mso_type mso = tpm2_handle_mso(handle);
struct tpm2_auth *auth;
- u16 name_size_alg;
int slot;
int ret;
#endif
@@ -276,36 +287,15 @@ int tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
}
tpm_buf_append_u32(buf, handle);
auth->session += 4;
-
- if (mso == TPM2_MSO_PERSISTENT ||
- mso == TPM2_MSO_VOLATILE ||
- mso == TPM2_MSO_NVRAM) {
- if (!name) {
- ret = tpm2_read_public(chip, handle, auth->name[slot]);
- if (ret < 0)
- goto err;
-
- name_size_alg = ret;
- }
- } else {
- if (name) {
- dev_err(&chip->dev, "handle 0x%08x does not use a name\n",
- handle);
- ret = -EIO;
- goto err;
- }
- }
-
- auth->name_h[slot] = handle;
- if (name)
- memcpy(auth->name[slot], name, name_size_alg);
+ memcpy(auth->name[slot], name, name_size);
+ auth->name_size_tbl[slot] = name_size;
#endif
return 0;
#ifdef CONFIG_TCG_TPM2_HMAC
err:
tpm2_end_auth_session(chip);
- return tpm_ret_to_err(ret);
+ return ret;
#endif
}
EXPORT_SYMBOL_GPL(tpm_buf_append_name);
@@ -613,22 +603,8 @@ int tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
attrs = chip->cc_attrs_tbl[i];
handles = (attrs >> TPM2_CC_ATTR_CHANDLES) & GENMASK(2, 0);
+ offset_s += handles * sizeof(u32);
- /*
- * just check the names, it's easy to make mistakes. This
- * would happen if someone added a handle via
- * tpm_buf_append_u32() instead of tpm_buf_append_name()
- */
- for (i = 0; i < handles; i++) {
- u32 handle = tpm_buf_read_u32(buf, &offset_s);
-
- if (auth->name_h[i] != handle) {
- dev_err(&chip->dev, "invalid handle 0x%08x\n", handle);
- ret = -EIO;
- goto err;
- }
- }
- /* point offset_s to the start of the sessions */
val = tpm_buf_read_u32(buf, &offset_s);
/* point offset_p to the start of the parameters */
offset_p = offset_s + val;
@@ -689,23 +665,8 @@ int tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
/* ordinal is already BE */
sha256_update(&sctx, (u8 *)&head->ordinal, sizeof(head->ordinal));
/* add the handle names */
- for (i = 0; i < handles; i++) {
- enum tpm2_mso_type mso = tpm2_handle_mso(auth->name_h[i]);
-
- if (mso == TPM2_MSO_PERSISTENT ||
- mso == TPM2_MSO_VOLATILE ||
- mso == TPM2_MSO_NVRAM) {
- ret = name_size(auth->name[i]);
- if (ret < 0)
- goto err;
-
- sha256_update(&sctx, auth->name[i], ret);
- } else {
- __be32 h = cpu_to_be32(auth->name_h[i]);
-
- sha256_update(&sctx, (u8 *)&h, 4);
- }
- }
+ for (i = 0; i < handles; i++)
+ sha256_update(&sctx, auth->name[i], auth->name_size_tbl[i]);
if (offset_s != tpm_buf_length(buf))
sha256_update(&sctx, &buf->data[offset_s],
tpm_buf_length(buf) - offset_s);
diff --git a/include/linux/tpm.h b/include/linux/tpm.h
index 8b82290de99f..7468709ad245 100644
--- a/include/linux/tpm.h
+++ b/include/linux/tpm.h
@@ -543,7 +543,7 @@ static inline struct tpm2_auth *tpm2_chip_auth(struct tpm_chip *chip)
}
int tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
- u32 handle, u8 *name);
+ u32 handle, u8 *name, u16 name_size);
void tpm_buf_append_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf,
u8 attributes, u8 *passphrase,
int passphraselen);
@@ -557,6 +557,7 @@ int tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf);
int tpm_buf_check_hmac_response(struct tpm_chip *chip, struct tpm_buf *buf,
int rc);
void tpm2_end_auth_session(struct tpm_chip *chip);
+int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name);
#else
#include <linux/unaligned.h>
@@ -580,6 +581,13 @@ static inline int tpm_buf_check_hmac_response(struct tpm_chip *chip,
{
return rc;
}
+
+static inline int tpm2_read_public(struct tpm_chip *chip, u32 handle,
+ void *name)
+{
+ memcpy(name, &handle, sizeof(u32));
+ return sizeof(u32);
+}
#endif /* CONFIG_TCG_TPM2_HMAC */
#endif
diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
index ec84ea945d3b..dbacebe7af56 100644
--- a/security/keys/trusted-keys/trusted_tpm2.c
+++ b/security/keys/trusted-keys/trusted_tpm2.c
@@ -203,8 +203,10 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
struct trusted_key_payload *payload,
struct trusted_key_options *options)
{
+ u8 parent_name[TPM2_MAX_NAME_SIZE];
off_t offset = TPM_HEADER_SIZE;
struct tpm_buf buf, sized;
+ u16 parent_name_size;
int blob_len = 0;
int hash;
u32 flags;
@@ -221,6 +223,12 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
if (rc)
return rc;
+ rc = tpm2_read_public(chip, options->keyhandle, parent_name);
+ if (rc < 0)
+ goto out_put;
+
+ parent_name_size = rc;
+
rc = tpm2_start_auth_session(chip);
if (rc)
goto out_put;
@@ -238,7 +246,8 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
goto out_put;
}
- rc = tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
+ rc = tpm_buf_append_name(chip, &buf, options->keyhandle, parent_name,
+ parent_name_size);
if (rc)
goto out;
@@ -325,21 +334,25 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
}
/**
- * tpm2_load_cmd() - execute a TPM2_Load command
- *
- * @chip: TPM chip to use
- * @payload: the key data in clear and encrypted form
- * @options: authentication values and other options
- * @blob_handle: returned blob handle
+ * tpm2_load_cmd() - Execute TPM2_Load
+ * @chip: TPM chip to use.
+ * @payload: Key data in clear text.
+ * @options: Trusted key options.
+ * @parent_name: A cryptographic name, i.e. a TPMT_HA blob, of the
+ * parent key.
+ * @blob: The decoded payload for the key.
+ * @blob_handle: On success, will contain handle to the loaded keyedhash
+ * blob.
*
- * Return: 0 on success.
- * -E2BIG on wrong payload size.
- * -EPERM on tpm error status.
- * < 0 error from tpm_send.
+ * Return -E2BIG when the blob size is too small for all the data.
+ * Returns tpm_transmit_cmd() error codes when either TPM2_Load fails.
*/
static int tpm2_load_cmd(struct tpm_chip *chip,
struct trusted_key_payload *payload,
struct trusted_key_options *options,
+ u8 *parent_name,
+ u16 parent_name_size,
+ const u8 *blob,
u32 *blob_handle)
{
u8 *blob_ref __free(kfree) = NULL;
@@ -347,27 +360,13 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
unsigned int private_len;
unsigned int public_len;
unsigned int blob_len;
- u8 *blob, *pub;
+ const u8 *pub;
int rc;
u32 attrs;
- rc = tpm2_key_decode(payload, options, &blob);
- if (rc) {
- /* old form */
- blob = payload->blob;
- payload->old_format = 1;
- } else {
- /* Bind for cleanup: */
- blob_ref = blob;
- }
-
- /* new format carries keyhandle but old format doesn't */
- if (!options->keyhandle)
- return -EINVAL;
-
/* must be big enough for at least the two be16 size counts */
if (payload->blob_len < 4)
- return -EINVAL;
+ return -E2BIG;
private_len = get_unaligned_be16(blob);
@@ -403,7 +402,8 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
return rc;
}
- rc = tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
+ rc = tpm_buf_append_name(chip, &buf, options->keyhandle, parent_name,
+ parent_name_size);
if (rc)
goto out;
@@ -437,18 +437,21 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
/**
* tpm2_unseal_cmd() - execute a TPM2_Unseal command
*
- * @chip: TPM chip to use
- * @payload: the key data in clear and encrypted form
- * @options: authentication values and other options
- * @blob_handle: blob handle
+ * @chip: TPM chip to use
+ * @payload: Key data in clear text.
+ * @options: Trusted key options.
+ * @parent_name: A cryptographic name, i.e. a TPMT_HA blob, of the
+ * parent key.
+ * @blob_handle: Handle to the loaded keyedhash blob.
*
- * Return: 0 on success
- * -EPERM on tpm error status
- * < 0 error from tpm_send
+ * Return -E2BIG when the blob size is too small for all the data.
+ * Returns tpm_transmit_cmd() error codes when either TPM2_Load fails.
*/
static int tpm2_unseal_cmd(struct tpm_chip *chip,
struct trusted_key_payload *payload,
struct trusted_key_options *options,
+ u8 *parent_name,
+ u16 parent_name_size,
u32 blob_handle)
{
struct tpm_buf buf;
@@ -466,7 +469,8 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
return rc;
}
- rc = tpm_buf_append_name(chip, &buf, blob_handle, NULL);
+ rc = tpm_buf_append_name(chip, &buf, blob_handle, parent_name,
+ parent_name_size);
if (rc)
goto out;
@@ -539,30 +543,60 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
}
/**
- * tpm2_unseal_trusted() - unseal the payload of a trusted key
+ * tpm2_unseal_trusted() - Unseal a trusted key
+ * @chip: TPM chip to use.
+ * @payload: Key data in clear text.
+ * @options: Trusted key options.
*
- * @chip: TPM chip to use
- * @payload: the key data in clear and encrypted form
- * @options: authentication values and other options
- *
- * Return: Same as with tpm_send.
+ * Return -E2BIG when the blob size is too small for all the data.
+ * Return -EINVAL when parent's key handle has not been set.
+ * Returns tpm_transmit_cmd() error codes when either TPM2_Load or TPM2_Unseal
+ * fails.
*/
int tpm2_unseal_trusted(struct tpm_chip *chip,
struct trusted_key_payload *payload,
struct trusted_key_options *options)
{
+ u8 *blob_ref __free(kfree) = NULL;
+ u8 parent_name[TPM2_MAX_NAME_SIZE];
+ u16 parent_name_size;
u32 blob_handle;
+ u8 *blob;
int rc;
+ /*
+ * Try to decode the provided blob as an ASN.1 blob. Assume that the
+ * blob is in the legacy format if decoding does not end successfully.
+ */
+ rc = tpm2_key_decode(payload, options, &blob);
+ if (rc) {
+ blob = payload->blob;
+ payload->old_format = 1;
+ } else {
+ blob_ref = blob;
+ }
+
+ if (!options->keyhandle)
+ return -EINVAL;
+
rc = tpm_try_get_ops(chip);
if (rc)
return rc;
- rc = tpm2_load_cmd(chip, payload, options, &blob_handle);
+ rc = tpm2_read_public(chip, options->keyhandle, parent_name);
+ if (rc < 0)
+ goto out;
+
+ parent_name_size = rc;
+
+ rc = tpm2_load_cmd(chip, payload, options, parent_name,
+ parent_name_size, blob, &blob_handle);
if (rc)
goto out;
- rc = tpm2_unseal_cmd(chip, payload, options, blob_handle);
+ rc = tpm2_unseal_cmd(chip, payload, options, parent_name,
+ parent_name_size, blob_handle);
+
tpm2_flush_context(chip, blob_handle);
out:
--
2.52.0
^ permalink raw reply related
* [PATCH v9 06/11] KEYS: trusted: Remove dead branch from tpm2_unseal_cmd
From: Jarkko Sakkinen @ 2026-01-25 19:25 UTC (permalink / raw)
To: linux-integrity
Cc: Jarkko Sakkinen, Jonathan McDowell, James Bottomley, Mimi Zohar,
David Howells, Paul Moore, James Morris, Serge E. Hallyn,
open list:KEYS-TRUSTED, open list:SECURITY SUBSYSTEM, open list
In-Reply-To: <20260125192526.782202-1-jarkko@kernel.org>
TPM2_Unseal requires TPM2_ST_SESSIONS, and tpm2_unseal_cmd() always does
set up either password or HMAC session.
Remove the branch in tpm2_unseal_cmd() conditionally setting
TPM2_ST_NO_SESSIONS. It is faulty but luckily it is never exercised at
run-time, and thus does not cause regressions.
Reviewed-by: Jonathan McDowell <noodles@meta.com>
Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
---
security/keys/trusted-keys/trusted_tpm2.c | 10 +---------
1 file changed, 1 insertion(+), 9 deletions(-)
diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
index 4894aae6ef70..ec84ea945d3b 100644
--- a/security/keys/trusted-keys/trusted_tpm2.c
+++ b/security/keys/trusted-keys/trusted_tpm2.c
@@ -451,10 +451,8 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
struct trusted_key_options *options,
u32 blob_handle)
{
- struct tpm_header *head;
struct tpm_buf buf;
u16 data_len;
- int offset;
u8 *data;
int rc;
@@ -495,14 +493,8 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
tpm_buf_append_u16(&buf, options->blobauth_len);
tpm_buf_append(&buf, options->blobauth, options->blobauth_len);
- if (tpm2_chip_auth(chip)) {
+ if (tpm2_chip_auth(chip))
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)
- head->tag = cpu_to_be16(TPM2_ST_NO_SESSIONS);
- }
}
rc = tpm_buf_fill_hmac_session(chip, &buf);
--
2.52.0
^ permalink raw reply related
* [PATCH v9 05/11] KEYS: trusted: Open code tpm2_buf_append()
From: Jarkko Sakkinen @ 2026-01-25 19:25 UTC (permalink / raw)
To: linux-integrity
Cc: Jarkko Sakkinen, Jonathan McDowell, James Bottomley,
Jarkko Sakkinen, Mimi Zohar, David Howells, Paul Moore,
James Morris, Serge E. Hallyn, open list:KEYS-TRUSTED,
open list:SECURITY SUBSYSTEM, open list
In-Reply-To: <20260125192526.782202-1-jarkko@kernel.org>
From: Jarkko Sakkinen <jarkko.sakkinen@opinsys.com>
tpm2_buf_append_auth() has a single call site and most of its parameters
are redundant. Open code it to the call site so that less cross-referencing
is required while browsing the source code.
Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@opinsys.com>
Reviewed-by: Jonathan McDowell <noodles@earth.li>
---
v6:
- Trimmed the patch by removing comment update as it is out of scope.
---
security/keys/trusted-keys/trusted_tpm2.c | 40 ++++-------------------
1 file changed, 7 insertions(+), 33 deletions(-)
diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
index 6340823f8b53..4894aae6ef70 100644
--- a/security/keys/trusted-keys/trusted_tpm2.c
+++ b/security/keys/trusted-keys/trusted_tpm2.c
@@ -190,36 +190,6 @@ int tpm2_key_priv(void *context, size_t hdrlen,
return 0;
}
-/**
- * tpm2_buf_append_auth() - append TPMS_AUTH_COMMAND to the buffer.
- *
- * @buf: an allocated tpm_buf instance
- * @session_handle: session handle
- * @nonce: the session nonce, may be NULL if not used
- * @nonce_len: the session nonce length, may be 0 if not used
- * @attributes: the session attributes
- * @hmac: the session HMAC or password, may be NULL if not used
- * @hmac_len: the session HMAC or password length, maybe 0 if not used
- */
-static void tpm2_buf_append_auth(struct tpm_buf *buf, u32 session_handle,
- const u8 *nonce, u16 nonce_len,
- u8 attributes,
- const u8 *hmac, u16 hmac_len)
-{
- tpm_buf_append_u32(buf, 9 + nonce_len + hmac_len);
- tpm_buf_append_u32(buf, session_handle);
- tpm_buf_append_u16(buf, nonce_len);
-
- if (nonce && nonce_len)
- tpm_buf_append(buf, nonce, nonce_len);
-
- tpm_buf_append_u8(buf, attributes);
- tpm_buf_append_u16(buf, hmac_len);
-
- if (hmac && hmac_len)
- tpm_buf_append(buf, hmac, hmac_len);
-}
-
/**
* tpm2_seal_trusted() - seal the payload of a trusted key
*
@@ -518,9 +488,13 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
* could repeat our actions with the exfiltrated
* password.
*/
- tpm2_buf_append_auth(&buf, options->policyhandle,
- NULL /* nonce */, 0, 0,
- 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);
} else {
--
2.52.0
^ permalink raw reply related
* [PATCH v9 04/11] tpm2-sessions: Define TPM2_NAME_MAX_SIZE
From: Jarkko Sakkinen @ 2026-01-25 19:25 UTC (permalink / raw)
To: linux-integrity
Cc: Jarkko Sakkinen, Jonathan McDowell, James Bottomley, Mimi Zohar,
David Howells, Paul Moore, James Morris, Serge E. Hallyn,
open list:KEYS-TRUSTED, open list:SECURITY SUBSYSTEM, open list
In-Reply-To: <20260125192526.782202-1-jarkko@kernel.org>
Define TPM2_NAME_MAX_SIZE, which describes the maximum size for hashes
encoded as TPMT_HA, which the prime identifier used for persistent and
transient keys in TPM2 protocol.
Set its value to 'SHA512_DIGEST_SIZE + 2', as SHA512 has the largest
digest size of the algorithms in TCG algorithm repository.
In additionl, rename TPM2_NAME_SIZE as TPM2_NULL_NAME_SIZE in order to
avoid any possible confusion.
Reviewed-by: Jonathan McDowell <noodles@meta.com>
Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
---
v6:
- Rewrote the commit message.
v2:
- Rename TPM2_NAME_SIZE as TPM2_NULL_NAME_SIZE.
---
drivers/char/tpm/tpm-sysfs.c | 2 +-
drivers/char/tpm/tpm2-sessions.c | 2 +-
include/linux/tpm.h | 37 +++++++++++++++++++++-----------
3 files changed, 27 insertions(+), 14 deletions(-)
diff --git a/drivers/char/tpm/tpm-sysfs.c b/drivers/char/tpm/tpm-sysfs.c
index 94231f052ea7..4a6a27ee295d 100644
--- a/drivers/char/tpm/tpm-sysfs.c
+++ b/drivers/char/tpm/tpm-sysfs.c
@@ -314,7 +314,7 @@ static ssize_t null_name_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct tpm_chip *chip = to_tpm_chip(dev);
- int size = TPM2_NAME_SIZE;
+ int size = TPM2_NULL_NAME_SIZE;
bin2hex(buf, chip->null_key_name, size);
size *= 2;
diff --git a/drivers/char/tpm/tpm2-sessions.c b/drivers/char/tpm/tpm2-sessions.c
index 4149379665c4..525b8622d1c3 100644
--- a/drivers/char/tpm/tpm2-sessions.c
+++ b/drivers/char/tpm/tpm2-sessions.c
@@ -137,7 +137,7 @@ struct tpm2_auth {
* we must compute and remember
*/
u32 name_h[AUTH_MAX_NAMES];
- u8 name[AUTH_MAX_NAMES][2 + SHA512_DIGEST_SIZE];
+ u8 name[AUTH_MAX_NAMES][TPM2_MAX_NAME_SIZE];
};
#ifdef CONFIG_TCG_TPM2_HMAC
diff --git a/include/linux/tpm.h b/include/linux/tpm.h
index 202da079d500..8b82290de99f 100644
--- a/include/linux/tpm.h
+++ b/include/linux/tpm.h
@@ -27,9 +27,33 @@
#define TPM_DIGEST_SIZE 20 /* Max TPM v1.2 PCR size */
+/*
+ * SHA-512 is, as of today, the largest digest in the TCG algorithm repository.
+ */
#define TPM2_MAX_DIGEST_SIZE SHA512_DIGEST_SIZE
+
+/*
+ * A TPM name digest i.e., TPMT_HA, is a concatenation of TPM_ALG_ID of the
+ * name algorithm and hash of TPMT_PUBLIC.
+ */
+#define TPM2_MAX_NAME_SIZE (TPM2_MAX_DIGEST_SIZE + 2)
+
+/*
+ * The maximum number of PCR banks.
+ */
#define TPM2_MAX_PCR_BANKS 8
+/*
+ * Fixed define for the size of a name. This is actually HASHALG size
+ * plus 2, so 32 for SHA256
+ */
+#define TPM2_NULL_NAME_SIZE 34
+
+/*
+ * The maximum size for an object context
+ */
+#define TPM2_MAX_CONTEXT_SIZE 4096
+
struct tpm_chip;
struct trusted_key_payload;
struct trusted_key_options;
@@ -139,17 +163,6 @@ struct tpm_chip_seqops {
/* fixed define for the curve we use which is NIST_P256 */
#define EC_PT_SZ 32
-/*
- * fixed define for the size of a name. This is actually HASHALG size
- * plus 2, so 32 for SHA256
- */
-#define TPM2_NAME_SIZE 34
-
-/*
- * The maximum size for an object context
- */
-#define TPM2_MAX_CONTEXT_SIZE 4096
-
struct tpm_chip {
struct device dev;
struct device devs;
@@ -211,7 +224,7 @@ struct tpm_chip {
/* saved context for NULL seed */
u8 null_key_context[TPM2_MAX_CONTEXT_SIZE];
/* name of NULL seed */
- u8 null_key_name[TPM2_NAME_SIZE];
+ u8 null_key_name[TPM2_NULL_NAME_SIZE];
u8 null_ec_key_x[EC_PT_SZ];
u8 null_ec_key_y[EC_PT_SZ];
struct tpm2_auth *auth;
--
2.52.0
^ permalink raw reply related
* [PATCH v9 03/11] tpm: Change tpm_get_random() opportunistic
From: Jarkko Sakkinen @ 2026-01-25 19:25 UTC (permalink / raw)
To: linux-integrity
Cc: Jarkko Sakkinen, David S. Miller, Herbert Xu, Eric Biggers,
James Bottomley, Mimi Zohar, David Howells, Paul Moore,
James Morris, Serge E. Hallyn, open list:KEYS-TRUSTED,
open list:SECURITY SUBSYSTEM, open list
In-Reply-To: <20260125192526.782202-1-jarkko@kernel.org>
hwrng framework does not have a requirement that the all bytes requested
need to be provided. By enforcing such a requirement internally, TPM driver
can cause unpredictability in latency, as a single tpm_get_random() call
can result multiple TPM commands.
Especially, when TCG_TPM2_HMAC is enabled, extra roundtrips could have
significant effect to the system latency.
Thus, send TPM command only once and return bytes received instead of
committing to the number of requested bytes.
Cc: David S. Miller <davem@davemloft.net>
Cc: Herbert Xu <herbert@gondor.apana.org.au>
Cc: Eric Biggers <ebiggers@kernel.org>
Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
---
v9:
- Merge orchestration.
v7:
- Given that hwrng is now only caller for tpm_get_random(), remove the
wait parameter.
v4:
- Fixed grammar mistakes.
---
drivers/char/tpm/tpm-interface.c | 20 ++++--
drivers/char/tpm/tpm1-cmd.c | 66 ++++++++----------
drivers/char/tpm/tpm2-cmd.c | 115 +++++++++++++------------------
3 files changed, 88 insertions(+), 113 deletions(-)
diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c
index f745a098908b..d44bd8c44612 100644
--- a/drivers/char/tpm/tpm-interface.c
+++ b/drivers/char/tpm/tpm-interface.c
@@ -487,18 +487,24 @@ int tpm_pm_resume(struct device *dev)
EXPORT_SYMBOL_GPL(tpm_pm_resume);
/**
- * tpm_get_random() - get random bytes from the TPM's RNG
- * @chip: a &struct tpm_chip instance, %NULL for the default chip
- * @out: destination buffer for the random bytes
- * @max: the max number of bytes to write to @out
+ * tpm_get_random() - Get random bytes from the TPM's RNG
+ * @chip: A &tpm_chip instance. When set to %NULL, the default chip is used.
+ * @out: Destination buffer for the acquired random bytes.
+ * @max: The maximum number of bytes to write to @out.
+ *
+ * Tries to pull bytes from the TPM. At most, @max bytes are returned.
*
- * Return: number of random bytes read or a negative error value.
+ * Returns the number of random bytes read on success.
+ * Returns -EINVAL when @out is NULL, or @max is not between zero and
+ * %TPM_MAX_RNG_DATA.
+ * Returns tpm_transmit_cmd() error codes when the TPM command results an
+ * error.
*/
int tpm_get_random(struct tpm_chip *chip, u8 *out, size_t max)
{
int rc;
- if (!out || max > TPM_MAX_RNG_DATA)
+ if (!out || !max || max > TPM_MAX_RNG_DATA)
return -EINVAL;
if (!chip)
@@ -514,7 +520,7 @@ int tpm_get_random(struct tpm_chip *chip, u8 *out, size_t max)
rc = tpm1_get_random(chip, out, max);
tpm_put_ops(chip);
- return rc;
+ return rc != 0 ? rc : -EIO;
}
EXPORT_SYMBOL_GPL(tpm_get_random);
diff --git a/drivers/char/tpm/tpm1-cmd.c b/drivers/char/tpm/tpm1-cmd.c
index b49a790f1bd5..3fe94f596f97 100644
--- a/drivers/char/tpm/tpm1-cmd.c
+++ b/drivers/char/tpm/tpm1-cmd.c
@@ -518,63 +518,51 @@ struct tpm1_get_random_out {
} __packed;
/**
- * tpm1_get_random() - get random bytes from the TPM's RNG
- * @chip: a &struct tpm_chip instance
- * @dest: destination buffer for the random bytes
- * @max: the maximum number of bytes to write to @dest
+ * tpm1_get_random() - Get random bytes from the TPM's RNG
*
- * Return:
- * * number of bytes read
- * * -errno (positive TPM return codes are masked to -EIO)
+ * This is an internal function for tpm_get_random(). See its documentation
+ * for more information.
*/
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 tpm1_get_random_out *resp;
struct tpm_buf buf;
- u32 total = 0;
- int retries = 5;
u32 recd;
int rc;
+ if (!dest || !max || max > TPM_MAX_RNG_DATA)
+ return -EINVAL;
+
rc = tpm_buf_init(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_GET_RANDOM);
if (rc)
return rc;
- do {
- tpm_buf_append_u32(&buf, num_bytes);
-
- rc = tpm_transmit_cmd(chip, &buf, sizeof(out->rng_data_len),
- "attempting get random");
- if (rc) {
- if (rc > 0)
- rc = -EIO;
- goto out;
- }
+ tpm_buf_append_u32(&buf, max);
- out = (struct tpm1_get_random_out *)&buf.data[TPM_HEADER_SIZE];
+ rc = tpm_transmit_cmd(chip, &buf, sizeof(resp->rng_data_len), "TPM_GetRandom");
+ if (rc) {
+ if (rc > 0)
+ rc = -EIO;
+ goto out;
+ }
- recd = be32_to_cpu(out->rng_data_len);
- if (recd > num_bytes) {
- rc = -EFAULT;
- goto out;
- }
+ resp = (struct tpm1_get_random_out *)&buf.data[TPM_HEADER_SIZE];
- if (tpm_buf_length(&buf) < TPM_HEADER_SIZE +
- sizeof(out->rng_data_len) + recd) {
- rc = -EFAULT;
- goto out;
- }
- memcpy(dest, out->rng_data, recd);
+ recd = be32_to_cpu(resp->rng_data_len);
+ if (recd > max) {
+ rc = -EIO;
+ goto out;
+ }
- dest += recd;
- total += recd;
- num_bytes -= recd;
+ if (buf.length < TPM_HEADER_SIZE + sizeof(resp->rng_data_len) + recd) {
+ rc = -EIO;
+ goto out;
+ }
- tpm_buf_reset(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_GET_RANDOM);
- } while (retries-- && total < max);
+ memcpy(dest, resp->rng_data, recd);
+ tpm_buf_destroy(&buf);
+ return recd;
- rc = total ? (int)total : -EIO;
out:
tpm_buf_destroy(&buf);
return rc;
diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c
index 3a77be7ebf4a..5f15d8a93e23 100644
--- a/drivers/char/tpm/tpm2-cmd.c
+++ b/drivers/char/tpm/tpm2-cmd.c
@@ -244,98 +244,79 @@ struct tpm2_get_random_out {
} __packed;
/**
- * tpm2_get_random() - get random bytes from the TPM RNG
+ * tpm2_get_random() - Get random bytes from the TPM's RNG
*
- * @chip: a &tpm_chip instance
- * @dest: destination buffer
- * @max: the max number of random bytes to pull
- *
- * Return:
- * size of the buffer on success,
- * -errno otherwise (positive TPM return codes are masked to -EIO)
+ * This is an internal function for tpm_get_random(). See its documentation
+ * for more information.
*/
int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
{
- struct tpm2_get_random_out *out;
+ struct tpm2_get_random_out *resp;
struct tpm_header *head;
struct tpm_buf buf;
+ off_t offset;
u32 recd;
- u32 num_bytes = max;
int err;
- int total = 0;
- int retries = 5;
- u8 *dest_ptr = dest;
- off_t offset;
- if (!num_bytes || max > TPM_MAX_RNG_DATA)
+ if (!dest || !max || max > TPM_MAX_RNG_DATA)
return -EINVAL;
err = tpm2_start_auth_session(chip);
if (err)
return err;
- err = tpm_buf_init(&buf, 0, 0);
+ err = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_GET_RANDOM);
if (err) {
tpm2_end_auth_session(chip);
return err;
}
- do {
- tpm_buf_reset(&buf, TPM2_ST_SESSIONS, TPM2_CC_GET_RANDOM);
- if (tpm2_chip_auth(chip)) {
- 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)
- head->tag = cpu_to_be16(TPM2_ST_NO_SESSIONS);
- }
- tpm_buf_append_u16(&buf, num_bytes);
- err = tpm_buf_fill_hmac_session(chip, &buf);
- if (err) {
- tpm_buf_destroy(&buf);
- return err;
- }
+ if (tpm2_chip_auth(chip)) {
+ tpm_buf_append_hmac_session(chip, &buf,
+ TPM2_SA_ENCRYPT | TPM2_SA_CONTINUE_SESSION,
+ NULL, 0);
+ } else {
+ head = (struct tpm_header *)buf.data;
+ head->tag = cpu_to_be16(TPM2_ST_NO_SESSIONS);
+ }
+ tpm_buf_append_u16(&buf, max);
- 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);
- if (err) {
- if (err > 0)
- err = -EIO;
- goto out;
- }
+ err = tpm_buf_fill_hmac_session(chip, &buf);
+ if (err) {
+ tpm_buf_destroy(&buf);
+ return err;
+ }
- 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];
- recd = min_t(u32, be16_to_cpu(out->size), num_bytes);
- if (tpm_buf_length(&buf) <
- TPM_HEADER_SIZE +
- offsetof(struct tpm2_get_random_out, buffer) +
- recd) {
- err = -EFAULT;
- goto out;
- }
- memcpy(dest_ptr, out->buffer, recd);
+ err = tpm_transmit_cmd(chip, &buf, offsetof(struct tpm2_get_random_out, buffer),
+ "TPM2_GetRandom");
- dest_ptr += recd;
- total += recd;
- num_bytes -= recd;
- } while (retries-- && total < max);
+ err = tpm_buf_check_hmac_response(chip, &buf, err);
+ if (err) {
+ if (err > 0)
+ err = -EIO;
- tpm_buf_destroy(&buf);
+ goto out;
+ }
+
+ 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;
+
+ resp = (struct tpm2_get_random_out *)&buf.data[offset];
+ recd = min_t(u32, be16_to_cpu(resp->size), max);
+
+ if (tpm_buf_length(&buf) <
+ TPM_HEADER_SIZE + offsetof(struct tpm2_get_random_out, buffer) + recd) {
+ err = -EIO;
+ goto out;
+ }
+
+ memcpy(dest, resp->buffer, recd);
+ return recd;
- return total ? total : -EIO;
out:
tpm_buf_destroy(&buf);
tpm2_end_auth_session(chip);
--
2.52.0
^ permalink raw reply related
* [PATCH v9 02/11] KEYS: trusted: Use get_random_bytes_wait() instead of tpm_get_random()
From: Jarkko Sakkinen @ 2026-01-25 19:25 UTC (permalink / raw)
To: linux-integrity
Cc: Jarkko Sakkinen, Jonathan McDowell, James Bottomley, Mimi Zohar,
David Howells, Paul Moore, James Morris, Serge E. Hallyn,
open list:KEYS-TRUSTED, open list:SECURITY SUBSYSTEM, open list
In-Reply-To: <20260125192526.782202-1-jarkko@kernel.org>
Substitute the remaining tpm_get_random() calls in trusted_tpm1.c with
get_random_bytes_wait() thus aligning random number generation for TPM 1.2
with the removal of '.get_random' callback.
Reviewed-by: Jonathan McDowell <noodles@meta.com>
Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
---
security/keys/trusted-keys/trusted_tpm1.c | 18 +++---------------
1 file changed, 3 insertions(+), 15 deletions(-)
diff --git a/security/keys/trusted-keys/trusted_tpm1.c b/security/keys/trusted-keys/trusted_tpm1.c
index 7ce7e31bcdfb..3d75bb6f9689 100644
--- a/security/keys/trusted-keys/trusted_tpm1.c
+++ b/security/keys/trusted-keys/trusted_tpm1.c
@@ -371,13 +371,10 @@ static int osap(struct tpm_buf *tb, struct osapsess *s,
unsigned char ononce[TPM_NONCE_SIZE];
int ret;
- ret = tpm_get_random(chip, ononce, TPM_NONCE_SIZE);
+ ret = get_random_bytes_wait(ononce, TPM_NONCE_SIZE);
if (ret < 0)
return ret;
- if (ret != TPM_NONCE_SIZE)
- return -EIO;
-
tpm_buf_reset(tb, TPM_TAG_RQU_COMMAND, TPM_ORD_OSAP);
tpm_buf_append_u16(tb, type);
tpm_buf_append_u32(tb, handle);
@@ -464,15 +461,10 @@ static int tpm_seal(struct tpm_buf *tb, uint16_t keytype,
memcpy(td->xorwork + SHA1_DIGEST_SIZE, sess.enonce, SHA1_DIGEST_SIZE);
sha1(td->xorwork, SHA1_DIGEST_SIZE * 2, td->xorhash);
- ret = tpm_get_random(chip, td->nonceodd, TPM_NONCE_SIZE);
+ ret = get_random_bytes_wait(td->nonceodd, TPM_NONCE_SIZE);
if (ret < 0)
goto out;
- if (ret != TPM_NONCE_SIZE) {
- ret = -EIO;
- goto out;
- }
-
ordinal = htonl(TPM_ORD_SEAL);
datsize = htonl(datalen);
pcrsize = htonl(pcrinfosize);
@@ -575,14 +567,10 @@ static int tpm_unseal(struct tpm_buf *tb,
}
ordinal = htonl(TPM_ORD_UNSEAL);
- ret = tpm_get_random(chip, nonceodd, TPM_NONCE_SIZE);
+ ret = get_random_bytes_wait(nonceodd, TPM_NONCE_SIZE);
if (ret < 0)
return ret;
- if (ret != TPM_NONCE_SIZE) {
- pr_info("tpm_get_random failed (%d)\n", ret);
- return -EIO;
- }
ret = TSS_authhmac(authdata1, keyauth, TPM_NONCE_SIZE,
enonce1, nonceodd, cont, sizeof(uint32_t),
&ordinal, bloblen, blob, 0, 0);
--
2.52.0
^ permalink raw reply related
* [PATCH v9 01/11] KEYS: trusted: Use get_random-fallback for TPM
From: Jarkko Sakkinen @ 2026-01-25 19:25 UTC (permalink / raw)
To: linux-integrity
Cc: Jarkko Sakkinen, Eric Biggers, James Bottomley, Mimi Zohar,
David Howells, Paul Moore, James Morris, Serge E. Hallyn,
open list:KEYS-TRUSTED, open list:SECURITY SUBSYSTEM, open list
In-Reply-To: <20260125192526.782202-1-jarkko@kernel.org>
1. tpm2_get_random() is costly when TCG_TPM2_HMAC is enabled and thus its
use should be pooled rather than directly used. This both reduces
latency and improves its predictability.
2. Linux is better off overall if every subsystem uses the same source for
generating the random numbers required.
Thus, unset '.get_random', which causes fallback to kernel_get_random().
One might argue that TPM RNG should be used for the generated trusted keys,
so that they have matching entropy with the TPM internally generated
objects.
This argument does have some weight into it but as far cryptography goes,
FIPS certification sets the exact bar, not which exact FIPS certified RNG
will be used. Thus, the rational choice is obviously to pick the lowest
latency path, which is kernel RNG.
Finally, there is an actual defence in depth benefit when using kernel RNG
as it helps to mitigate TPM firmware bugs concerning RNG implementation,
given the obfuscation by the other entropy sources.
Reviewed-by: Eric Biggers <ebiggers@kernel.org>
Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
---
v7:
- A new patch. Simplifies follow up patches.
---
security/keys/trusted-keys/trusted_tpm1.c | 16 ++++++++++------
1 file changed, 10 insertions(+), 6 deletions(-)
diff --git a/security/keys/trusted-keys/trusted_tpm1.c b/security/keys/trusted-keys/trusted_tpm1.c
index 636acb66a4f6..7ce7e31bcdfb 100644
--- a/security/keys/trusted-keys/trusted_tpm1.c
+++ b/security/keys/trusted-keys/trusted_tpm1.c
@@ -6,6 +6,16 @@
* See Documentation/security/keys/trusted-encrypted.rst
*/
+/**
+ * DOC: Random Number Generation
+ *
+ * tpm_get_random() was previously used here as the RNG in order to have equal
+ * entropy with the objects fully inside the TPM. However, as far as goes,
+ * kernel RNG is equally fine, as long as long as it is FIPS certified. Also,
+ * using kernel RNG has the benefit of mitigating bugs in the TPM firmware
+ * associated with the RNG.
+ */
+
#include <crypto/hash_info.h>
#include <crypto/sha1.h>
#include <crypto/utils.h>
@@ -936,11 +946,6 @@ static int trusted_tpm_unseal(struct trusted_key_payload *p, char *datablob)
return ret;
}
-static int trusted_tpm_get_random(unsigned char *key, size_t key_len)
-{
- return tpm_get_random(chip, key, key_len);
-}
-
static int __init init_digests(void)
{
int i;
@@ -992,6 +997,5 @@ struct trusted_key_ops trusted_key_tpm_ops = {
.init = trusted_tpm_init,
.seal = trusted_tpm_seal,
.unseal = trusted_tpm_unseal,
- .get_random = trusted_tpm_get_random,
.exit = trusted_tpm_exit,
};
--
2.52.0
^ permalink raw reply related
* [PATCH v9 00/11] Streamline TPM2 HMAC sessions
From: Jarkko Sakkinen @ 2026-01-25 19:25 UTC (permalink / raw)
To: linux-integrity
Cc: Jarkko Sakkinen, James Bottomley, Mimi Zohar, David Howells,
Paul Moore, James Morris, Serge E. Hallyn, open list:KEYS-TRUSTED,
open list:SECURITY SUBSYSTEM, open list
This patch set contains accumulated patches, which gradually improve
TPM2 HMAC session management and TPM driver memory management.
RNG test
========
I run this test both TPM1 and TPM2 chips using QEMU and swtpm:
#!/bin/sh
ctrl_c() {
set +e
echo 0 > tracing_on
echo nop > current_tracer
echo BYE
exit
}
trap ctrl_c EXIT INT
mount -t tracefs none /sys/kernel/tracing
set -e
cd /sys/kernel/tracing
echo function > current_tracer
echo p:tpm_get_random tpm_get_random > kprobe_events
echo tpm_get_random > set_ftrace_filter
echo 1 > tracing_on
cat /dev/hwrng > /dev/null &
echo > trace
cat trace_pipe &
sleep 10
Change Log
==========
v9:
- Simplified hwrng patches.
- Minor tweaks.
v8:
- Patch was a bit out-of-sync after piling new stuff. Now it is somewhat
sane: RNG patches first, then tpm2-sessions and finally managed
tpm_buf allocations.
- I added inline comment on explaining why unconditional sha256_update()
call is safe to do when managing only single authorization handle.
v7:
- Updated cover letter to match better the current state of the patch
set.
v6:
- OK, so I decided to send one more update with managed allocations
moved to the tail so that it does not block reviewing more trivial
patches.
- Trimmed some of the patches and improved commit messages.
v5:
- I decided to add the managed allocation patch to this and take it from
the master branch for the time being, as it needs more eyes despite
having already one reviewed-by tag (especially tested-by tags).
Jarkko Sakkinen (11):
KEYS: trusted: Use get_random-fallback for TPM
KEYS: trusted: Use get_random_bytes_wait() instead of tpm_get_random()
tpm: Change tpm_get_random() opportunistic
tpm2-sessions: Define TPM2_NAME_MAX_SIZE
KEYS: trusted: Open code tpm2_buf_append()
KEYS: trusted: Remove dead branch from tpm2_unseal_cmd
KEYS: trusted: Re-orchestrate tpm2_read_public() calls
tpm2-sessions: Remove the support for more than one authorization
tpm-buf: Remove tpm_buf_append_handle
tpm-buf: Merge TPM_BUF_BOUNDARY_ERROR and TPM_BUF_OVERFLOW
tpm-buf: Implement managed allocations
drivers/char/tpm/tpm-buf.c | 156 ++++-----
drivers/char/tpm/tpm-interface.c | 20 +-
drivers/char/tpm/tpm-sysfs.c | 23 +-
drivers/char/tpm/tpm.h | 1 -
drivers/char/tpm/tpm1-cmd.c | 201 +++++------
drivers/char/tpm/tpm2-cmd.c | 386 ++++++++++------------
drivers/char/tpm/tpm2-sessions.c | 281 ++++++----------
drivers/char/tpm/tpm2-space.c | 44 +--
drivers/char/tpm/tpm_vtpm_proxy.c | 30 +-
include/linux/tpm.h | 77 +++--
security/keys/trusted-keys/trusted_tpm1.c | 70 ++--
security/keys/trusted-keys/trusted_tpm2.c | 327 +++++++++---------
12 files changed, 735 insertions(+), 881 deletions(-)
--
2.52.0
^ permalink raw reply
* [GIT PULL] KEYS: trusted: keys-trusted-next-6.19-rc7
From: Jarkko Sakkinen @ 2026-01-25 17:14 UTC (permalink / raw)
To: Linus Torvalds
Cc: David Howells, James Bottomley, Mimi Zohar, keyrings,
linux-integrity, linux-security-module
The following changes since commit d91a46d6805af41e7f2286e0fc22d498f45a682b:
Merge tag 'riscv-for-linus-6.19-rc7' of git://git.kernel.org/pub/scm/linux/kernel/git/riscv/linux (2026-01-24 18:55:48 -0800)
are available in the Git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/jarkko/linux-tpmdd.git tags/keys-trusted-next-6.19-rc7
for you to fetch changes up to 6342969dafbc63597cfc221aa13c3b123c2800c5:
keys/trusted_keys: fix handle passed to tpm_buf_append_name during unseal (2026-01-25 19:03:45 +0200)
----------------------------------------------------------------
Hi,
This a late fix for v6.19.
BR, Jarkko
----------------------------------------------------------------
Srish Srinivasan (1):
keys/trusted_keys: fix handle passed to tpm_buf_append_name during unseal
security/keys/trusted-keys/trusted_tpm2.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
^ permalink raw reply
* Re: [PATCH 2/2] keys/trusted_keys: move TPM-specific fields into trusted_tpm_options
From: Jarkko Sakkinen @ 2026-01-25 17:00 UTC (permalink / raw)
To: Srish Srinivasan
Cc: linux-integrity, keyrings, linuxppc-dev, maddy, mpe, npiggin,
christophe.leroy, James.Bottomley, zohar, nayna, stefanb,
rnsastry, linux-kernel, linux-security-module
In-Reply-To: <20260123165504.461607-3-ssrish@linux.ibm.com>
On Fri, Jan 23, 2026 at 10:25:04PM +0530, Srish Srinivasan wrote:
> The trusted_key_options struct contains TPM-specific fields (keyhandle,
> keyauth, blobauth_len, blobauth, pcrinfo_len, pcrinfo, pcrlock, hash,
> policydigest_len, policydigest, and policyhandle). This leads to the
> accumulation of backend-specific fields in the generic options structure.
>
> Define trusted_tpm_options structure and move the TPM-specific fields
> there. Store a pointer to trusted_tpm_options in trusted_key_options's
> private.
>
> No functional change intended.
>
> Signed-off-by: Srish Srinivasan <ssrish@linux.ibm.com>
> Reviewed-by: Stefan Berger <stefanb@linux.ibm.com>
> ---
> include/keys/trusted-type.h | 11 ---
> include/keys/trusted_tpm.h | 14 +++
> security/keys/trusted-keys/trusted_tpm1.c | 103 ++++++++++++++--------
> security/keys/trusted-keys/trusted_tpm2.c | 62 ++++++++-----
> 4 files changed, 121 insertions(+), 69 deletions(-)
>
> diff --git a/include/keys/trusted-type.h b/include/keys/trusted-type.h
> index 03527162613f..b80f250305b8 100644
> --- a/include/keys/trusted-type.h
> +++ b/include/keys/trusted-type.h
> @@ -39,17 +39,6 @@ struct trusted_key_payload {
>
> struct trusted_key_options {
> uint16_t keytype;
> - uint32_t keyhandle;
> - unsigned char keyauth[TPM_DIGEST_SIZE];
> - uint32_t blobauth_len;
> - unsigned char blobauth[TPM_DIGEST_SIZE];
> - uint32_t pcrinfo_len;
> - unsigned char pcrinfo[MAX_PCRINFO_SIZE];
> - int pcrlock;
> - uint32_t hash;
> - uint32_t policydigest_len;
> - unsigned char policydigest[MAX_DIGEST_SIZE];
> - uint32_t policyhandle;
> void *private;
> };
>
> diff --git a/include/keys/trusted_tpm.h b/include/keys/trusted_tpm.h
> index 0fadc6a4f166..355ebd36cbfd 100644
> --- a/include/keys/trusted_tpm.h
> +++ b/include/keys/trusted_tpm.h
> @@ -7,6 +7,20 @@
>
> extern struct trusted_key_ops trusted_key_tpm_ops;
>
> +struct trusted_tpm_options {
> + uint32_t keyhandle;
> + unsigned char keyauth[TPM_DIGEST_SIZE];
> + uint32_t blobauth_len;
> + unsigned char blobauth[TPM_DIGEST_SIZE];
> + uint32_t pcrinfo_len;
> + unsigned char pcrinfo[MAX_PCRINFO_SIZE];
> + int pcrlock;
> + uint32_t hash;
> + uint32_t policydigest_len;
> + unsigned char policydigest[MAX_DIGEST_SIZE];
> + uint32_t policyhandle;
> +};
> +
> int tpm2_seal_trusted(struct tpm_chip *chip,
> struct trusted_key_payload *payload,
> struct trusted_key_options *options);
> diff --git a/security/keys/trusted-keys/trusted_tpm1.c b/security/keys/trusted-keys/trusted_tpm1.c
> index 636acb66a4f6..0ab0234ebe37 100644
> --- a/security/keys/trusted-keys/trusted_tpm1.c
> +++ b/security/keys/trusted-keys/trusted_tpm1.c
> @@ -50,12 +50,14 @@ enum {
> #if TPM_DEBUG
> static inline void dump_options(struct trusted_key_options *o)
> {
> + struct trusted_tpm_options *tpm_opts = o->private;
TPM context is obvious i.e., actually private would be a better name.
> +
> pr_info("sealing key type %d\n", o->keytype);
> - pr_info("sealing key handle %0X\n", o->keyhandle);
> - pr_info("pcrlock %d\n", o->pcrlock);
> - pr_info("pcrinfo %d\n", o->pcrinfo_len);
> + pr_info("sealing key handle %0X\n", tpm_opts->keyhandle);
> + pr_info("pcrlock %d\n", tpm_opts->pcrlock);
> + pr_info("pcrinfo %d\n", tpm_opts->pcrinfo_len);
> print_hex_dump(KERN_INFO, "pcrinfo ", DUMP_PREFIX_NONE,
> - 16, 1, o->pcrinfo, o->pcrinfo_len, 0);
> + 16, 1, tpm_opts->pcrinfo, tpm_opts->pcrinfo_len, 0);
> }
Should be replaced with pr_debug() and KERN_DEBUG as precursory patch
(and remove TPM_DEBUG).
>
> static inline void dump_sess(struct osapsess *s)
> @@ -624,6 +626,7 @@ static int tpm_unseal(struct tpm_buf *tb,
> static int key_seal(struct trusted_key_payload *p,
> struct trusted_key_options *o)
> {
> + struct trusted_tpm_options *tpm_opts;
> struct tpm_buf tb;
> int ret;
>
> @@ -634,9 +637,12 @@ static int key_seal(struct trusted_key_payload *p,
> /* 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,
> + tpm_opts = o->private;
Not sure why this is not done in the declaration.
> +
> + ret = tpm_seal(&tb, o->keytype, tpm_opts->keyhandle, tpm_opts->keyauth,
> p->key, p->key_len + 1, p->blob, &p->blob_len,
> - o->blobauth, o->pcrinfo, o->pcrinfo_len);
> + tpm_opts->blobauth, tpm_opts->pcrinfo,
> + tpm_opts->pcrinfo_len);
> if (ret < 0)
> pr_info("srkseal failed (%d)\n", ret);
>
> @@ -650,6 +656,7 @@ static int key_seal(struct trusted_key_payload *p,
> static int key_unseal(struct trusted_key_payload *p,
> struct trusted_key_options *o)
> {
> + struct trusted_tpm_options *tpm_opts;
> struct tpm_buf tb;
> int ret;
>
> @@ -657,8 +664,10 @@ static int key_unseal(struct trusted_key_payload *p,
> if (ret)
> return ret;
>
> - ret = tpm_unseal(&tb, o->keyhandle, o->keyauth, p->blob, p->blob_len,
> - o->blobauth, p->key, &p->key_len);
> + tpm_opts = o->private;
> +
> + ret = tpm_unseal(&tb, tpm_opts->keyhandle, tpm_opts->keyauth, p->blob,
> + p->blob_len, tpm_opts->blobauth, p->key, &p->key_len);
> if (ret < 0)
> pr_info("srkunseal failed (%d)\n", ret);
> else
> @@ -695,6 +704,7 @@ static const match_table_t key_tokens = {
> static int getoptions(char *c, struct trusted_key_payload *pay,
> struct trusted_key_options *opt)
> {
> + struct trusted_tpm_options *tpm_opts;
> substring_t args[MAX_OPT_ARGS];
> char *p = c;
> int token;
> @@ -710,7 +720,9 @@ static int getoptions(char *c, struct trusted_key_payload *pay,
> if (tpm2 < 0)
> return tpm2;
>
> - opt->hash = tpm2 ? HASH_ALGO_SHA256 : HASH_ALGO_SHA1;
> + tpm_opts = opt->private;
> +
I'd remove this empty line.
> + tpm_opts->hash = tpm2 ? HASH_ALGO_SHA256 : HASH_ALGO_SHA1;
>
> if (!c)
> return 0;
> @@ -724,11 +736,11 @@ static int getoptions(char *c, struct trusted_key_payload *pay,
>
> switch (token) {
> case Opt_pcrinfo:
> - opt->pcrinfo_len = strlen(args[0].from) / 2;
> - if (opt->pcrinfo_len > MAX_PCRINFO_SIZE)
> + tpm_opts->pcrinfo_len = strlen(args[0].from) / 2;
> + if (tpm_opts->pcrinfo_len > MAX_PCRINFO_SIZE)
> return -EINVAL;
> - res = hex2bin(opt->pcrinfo, args[0].from,
> - opt->pcrinfo_len);
> + res = hex2bin(tpm_opts->pcrinfo, args[0].from,
> + tpm_opts->pcrinfo_len);
> if (res < 0)
> return -EINVAL;
> break;
> @@ -737,12 +749,12 @@ static int getoptions(char *c, struct trusted_key_payload *pay,
> if (res < 0)
> return -EINVAL;
> opt->keytype = SEAL_keytype;
> - opt->keyhandle = handle;
> + tpm_opts->keyhandle = handle;
> break;
> case Opt_keyauth:
> if (strlen(args[0].from) != 2 * SHA1_DIGEST_SIZE)
> return -EINVAL;
> - res = hex2bin(opt->keyauth, args[0].from,
> + res = hex2bin(tpm_opts->keyauth, args[0].from,
> SHA1_DIGEST_SIZE);
> if (res < 0)
> return -EINVAL;
> @@ -753,21 +765,23 @@ static int getoptions(char *c, struct trusted_key_payload *pay,
> * hex strings. TPM 2.0 authorizations are simple
> * passwords (although it can take a hash as well)
> */
> - opt->blobauth_len = strlen(args[0].from);
> + tpm_opts->blobauth_len = strlen(args[0].from);
>
> - if (opt->blobauth_len == 2 * TPM_DIGEST_SIZE) {
> - res = hex2bin(opt->blobauth, args[0].from,
> + if (tpm_opts->blobauth_len == 2 * TPM_DIGEST_SIZE) {
> + res = hex2bin(tpm_opts->blobauth, args[0].from,
> TPM_DIGEST_SIZE);
> if (res < 0)
> return -EINVAL;
>
> - opt->blobauth_len = TPM_DIGEST_SIZE;
> + tpm_opts->blobauth_len = TPM_DIGEST_SIZE;
> break;
> }
>
> - if (tpm2 && opt->blobauth_len <= sizeof(opt->blobauth)) {
> - memcpy(opt->blobauth, args[0].from,
> - opt->blobauth_len);
> + if (tpm2 &&
> + tpm_opts->blobauth_len <=
> + sizeof(tpm_opts->blobauth)) {
> + memcpy(tpm_opts->blobauth, args[0].from,
> + tpm_opts->blobauth_len);
> break;
> }
>
> @@ -785,14 +799,14 @@ static int getoptions(char *c, struct trusted_key_payload *pay,
> res = kstrtoul(args[0].from, 10, &lock);
> if (res < 0)
> return -EINVAL;
> - opt->pcrlock = lock;
> + tpm_opts->pcrlock = lock;
> break;
> case Opt_hash:
> if (test_bit(Opt_policydigest, &token_mask))
> return -EINVAL;
> for (i = 0; i < HASH_ALGO__LAST; i++) {
> if (!strcmp(args[0].from, hash_algo_name[i])) {
> - opt->hash = i;
> + tpm_opts->hash = i;
> break;
> }
> }
> @@ -804,14 +818,14 @@ static int getoptions(char *c, struct trusted_key_payload *pay,
> }
> break;
> case Opt_policydigest:
> - digest_len = hash_digest_size[opt->hash];
> + digest_len = hash_digest_size[tpm_opts->hash];
> if (!tpm2 || strlen(args[0].from) != (2 * digest_len))
> return -EINVAL;
> - res = hex2bin(opt->policydigest, args[0].from,
> + res = hex2bin(tpm_opts->policydigest, args[0].from,
> digest_len);
> if (res < 0)
> return -EINVAL;
> - opt->policydigest_len = digest_len;
> + tpm_opts->policydigest_len = digest_len;
> break;
> case Opt_policyhandle:
> if (!tpm2)
> @@ -819,7 +833,7 @@ static int getoptions(char *c, struct trusted_key_payload *pay,
> res = kstrtoul(args[0].from, 16, &handle);
> if (res < 0)
> return -EINVAL;
> - opt->policyhandle = handle;
> + tpm_opts->policyhandle = handle;
> break;
> default:
> return -EINVAL;
> @@ -830,6 +844,7 @@ static int getoptions(char *c, struct trusted_key_payload *pay,
>
> static struct trusted_key_options *trusted_options_alloc(void)
> {
> + struct trusted_tpm_options *tpm_opts;
> struct trusted_key_options *options;
> int tpm2;
>
> @@ -842,14 +857,23 @@ static struct trusted_key_options *trusted_options_alloc(void)
> /* set any non-zero defaults */
> options->keytype = SRK_keytype;
>
> - if (!tpm2)
> - options->keyhandle = SRKHANDLE;
> + tpm_opts = kzalloc(sizeof(*tpm_opts), GFP_KERNEL);
> + if (!tpm_opts) {
> + kfree_sensitive(options);
> + options = NULL;
> + } else {
> + if (!tpm2)
> + tpm_opts->keyhandle = SRKHANDLE;
> +
> + options->private = tpm_opts;
> + }
> }
> return options;
> }
>
> static int trusted_tpm_seal(struct trusted_key_payload *p, char *datablob)
> {
> + struct trusted_tpm_options *tpm_opts = NULL;
> struct trusted_key_options *options = NULL;
> int ret = 0;
> int tpm2;
> @@ -867,7 +891,9 @@ static int trusted_tpm_seal(struct trusted_key_payload *p, char *datablob)
> goto out;
> dump_options(options);
>
> - if (!options->keyhandle && !tpm2) {
> + tpm_opts = options->private;
> +
> + if (!tpm_opts->keyhandle && !tpm2) {
> ret = -EINVAL;
> goto out;
> }
> @@ -881,20 +907,22 @@ static int trusted_tpm_seal(struct trusted_key_payload *p, char *datablob)
> goto out;
> }
>
> - if (options->pcrlock) {
> - ret = pcrlock(options->pcrlock);
> + if (tpm_opts->pcrlock) {
> + ret = pcrlock(tpm_opts->pcrlock);
> if (ret < 0) {
> pr_info("pcrlock failed (%d)\n", ret);
> goto out;
> }
> }
> out:
> + kfree_sensitive(options->private);
> kfree_sensitive(options);
> return ret;
> }
>
> static int trusted_tpm_unseal(struct trusted_key_payload *p, char *datablob)
> {
> + struct trusted_tpm_options *tpm_opts = NULL;
> struct trusted_key_options *options = NULL;
> int ret = 0;
> int tpm2;
> @@ -912,7 +940,9 @@ static int trusted_tpm_unseal(struct trusted_key_payload *p, char *datablob)
> goto out;
> dump_options(options);
>
> - if (!options->keyhandle && !tpm2) {
> + tpm_opts = options->private;
> +
> + if (!tpm_opts->keyhandle && !tpm2) {
> ret = -EINVAL;
> goto out;
> }
> @@ -924,14 +954,15 @@ static int trusted_tpm_unseal(struct trusted_key_payload *p, char *datablob)
> if (ret < 0)
> pr_info("key_unseal failed (%d)\n", ret);
>
> - if (options->pcrlock) {
> - ret = pcrlock(options->pcrlock);
> + if (tpm_opts->pcrlock) {
> + ret = pcrlock(tpm_opts->pcrlock);
> if (ret < 0) {
> pr_info("pcrlock failed (%d)\n", ret);
> goto out;
> }
> }
> out:
> + kfree_sensitive(options->private);
> kfree_sensitive(options);
> return ret;
> }
> diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
> index 6340823f8b53..568c4af9010c 100644
> --- a/security/keys/trusted-keys/trusted_tpm2.c
> +++ b/security/keys/trusted-keys/trusted_tpm2.c
> @@ -24,6 +24,7 @@ static int tpm2_key_encode(struct trusted_key_payload *payload,
> struct trusted_key_options *options,
> u8 *src, u32 len)
> {
> + struct trusted_tpm_options *tpm_opts;
> const int SCRATCH_SIZE = PAGE_SIZE;
> u8 *scratch = kmalloc(SCRATCH_SIZE, GFP_KERNEL);
> u8 *work = scratch, *work1;
> @@ -46,7 +47,9 @@ static int tpm2_key_encode(struct trusted_key_payload *payload,
> work = asn1_encode_oid(work, end_work, tpm2key_oid,
> asn1_oid_len(tpm2key_oid));
>
> - if (options->blobauth_len == 0) {
> + tpm_opts = options->private;
> +
> + if (tpm_opts->blobauth_len == 0) {
> unsigned char bool[3], *w = bool;
> /* tag 0 is emptyAuth */
> w = asn1_encode_boolean(w, w + sizeof(bool), true);
> @@ -69,7 +72,7 @@ static int tpm2_key_encode(struct trusted_key_payload *payload,
> goto err;
> }
>
> - work = asn1_encode_integer(work, end_work, options->keyhandle);
> + work = asn1_encode_integer(work, end_work, tpm_opts->keyhandle);
> work = asn1_encode_octet_string(work, end_work, pub, pub_len);
> work = asn1_encode_octet_string(work, end_work, priv, priv_len);
>
> @@ -102,6 +105,7 @@ static int tpm2_key_decode(struct trusted_key_payload *payload,
> struct trusted_key_options *options,
> u8 **buf)
> {
> + struct trusted_tpm_options *tpm_opts;
> int ret;
> struct tpm2_key_context ctx;
> u8 *blob;
> @@ -120,8 +124,10 @@ static int tpm2_key_decode(struct trusted_key_payload *payload,
> if (!blob)
> return -ENOMEM;
>
> + tpm_opts = options->private;
> +
> *buf = blob;
> - options->keyhandle = ctx.parent;
> + tpm_opts->keyhandle = ctx.parent;
>
> memcpy(blob, ctx.priv, ctx.priv_len);
> blob += ctx.priv_len;
> @@ -233,6 +239,7 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
> struct trusted_key_payload *payload,
> struct trusted_key_options *options)
> {
> + struct trusted_tpm_options *tpm_opts;
> off_t offset = TPM_HEADER_SIZE;
> struct tpm_buf buf, sized;
> int blob_len = 0;
> @@ -240,11 +247,13 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
> u32 flags;
> int rc;
>
> - hash = tpm2_find_hash_alg(options->hash);
> + tpm_opts = options->private;
> +
> + hash = tpm2_find_hash_alg(tpm_opts->hash);
> if (hash < 0)
> return hash;
>
> - if (!options->keyhandle)
> + if (!tpm_opts->keyhandle)
> return -EINVAL;
>
> rc = tpm_try_get_ops(chip);
> @@ -268,18 +277,19 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
> goto out_put;
> }
>
> - rc = tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
> + rc = tpm_buf_append_name(chip, &buf, tpm_opts->keyhandle, NULL);
> if (rc)
> goto out;
>
> tpm_buf_append_hmac_session(chip, &buf, TPM2_SA_DECRYPT,
> - options->keyauth, TPM_DIGEST_SIZE);
> + tpm_opts->keyauth, TPM_DIGEST_SIZE);
>
> /* sensitive */
> - tpm_buf_append_u16(&sized, options->blobauth_len);
> + tpm_buf_append_u16(&sized, tpm_opts->blobauth_len);
>
> - if (options->blobauth_len)
> - tpm_buf_append(&sized, options->blobauth, options->blobauth_len);
> + if (tpm_opts->blobauth_len)
> + tpm_buf_append(&sized, tpm_opts->blobauth,
> + tpm_opts->blobauth_len);
>
> tpm_buf_append_u16(&sized, payload->key_len);
> tpm_buf_append(&sized, payload->key, payload->key_len);
> @@ -292,14 +302,15 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
>
> /* key properties */
> flags = 0;
> - flags |= options->policydigest_len ? 0 : TPM2_OA_USER_WITH_AUTH;
> + flags |= tpm_opts->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);
>
> /* policy */
> - tpm_buf_append_u16(&sized, options->policydigest_len);
> - if (options->policydigest_len)
> - tpm_buf_append(&sized, options->policydigest, options->policydigest_len);
> + tpm_buf_append_u16(&sized, tpm_opts->policydigest_len);
> + if (tpm_opts->policydigest_len)
> + tpm_buf_append(&sized, tpm_opts->policydigest,
> + tpm_opts->policydigest_len);
>
> /* public parameters */
> tpm_buf_append_u16(&sized, TPM_ALG_NULL);
> @@ -373,6 +384,7 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
> u32 *blob_handle)
> {
> u8 *blob_ref __free(kfree) = NULL;
> + struct trusted_tpm_options *tpm_opts;
> struct tpm_buf buf;
> unsigned int private_len;
> unsigned int public_len;
> @@ -391,8 +403,10 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
> blob_ref = blob;
> }
>
> + tpm_opts = options->private;
> +
> /* new format carries keyhandle but old format doesn't */
> - if (!options->keyhandle)
> + if (!tpm_opts->keyhandle)
> return -EINVAL;
>
> /* must be big enough for at least the two be16 size counts */
> @@ -433,11 +447,11 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
> return rc;
> }
>
> - rc = tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
> + rc = tpm_buf_append_name(chip, &buf, tpm_opts->keyhandle, NULL);
> if (rc)
> goto out;
>
> - tpm_buf_append_hmac_session(chip, &buf, 0, options->keyauth,
> + tpm_buf_append_hmac_session(chip, &buf, 0, tpm_opts->keyauth,
> TPM_DIGEST_SIZE);
>
> tpm_buf_append(&buf, blob, blob_len);
> @@ -481,6 +495,7 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
> struct trusted_key_options *options,
> u32 blob_handle)
> {
> + struct trusted_tpm_options *tpm_opts;
> struct tpm_header *head;
> struct tpm_buf buf;
> u16 data_len;
> @@ -502,10 +517,12 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
> if (rc)
> goto out;
>
> - if (!options->policyhandle) {
> + tpm_opts = options->private;
> +
> + if (!tpm_opts->policyhandle) {
> tpm_buf_append_hmac_session(chip, &buf, TPM2_SA_ENCRYPT,
> - options->blobauth,
> - options->blobauth_len);
> + tpm_opts->blobauth,
> + tpm_opts->blobauth_len);
> } else {
> /*
> * FIXME: The policy session was generated outside the
> @@ -518,9 +535,10 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
> * could repeat our actions with the exfiltrated
> * password.
> */
> - tpm2_buf_append_auth(&buf, options->policyhandle,
> + tpm2_buf_append_auth(&buf, tpm_opts->policyhandle,
> NULL /* nonce */, 0, 0,
> - options->blobauth, options->blobauth_len);
> + tpm_opts->blobauth,
> + tpm_opts->blobauth_len);
> if (tpm2_chip_auth(chip)) {
> tpm_buf_append_hmac_session(chip, &buf, TPM2_SA_ENCRYPT, NULL, 0);
> } else {
> --
> 2.43.0
>
BR, Jarkko
^ permalink raw reply
* Re: [PATCH 1/2] keys/trusted_keys: fix handle passed to tpm_buf_append_name during unseal
From: Jarkko Sakkinen @ 2026-01-25 16:55 UTC (permalink / raw)
To: Srish Srinivasan
Cc: linux-integrity, keyrings, linuxppc-dev, maddy, mpe, npiggin,
christophe.leroy, James.Bottomley, zohar, nayna, stefanb,
rnsastry, linux-kernel, linux-security-module
In-Reply-To: <20260123165504.461607-2-ssrish@linux.ibm.com>
On Fri, Jan 23, 2026 at 10:25:03PM +0530, Srish Srinivasan wrote:
> TPM2_Unseal[1] expects the handle of a loaded data object, and not the
> handle of the parent key. But the tpm2_unseal_cmd provides the parent
> keyhandle instead of blob_handle for the session HMAC calculation. This
> causes unseal to fail.
>
> Fix this by passing blob_handle to tpm_buf_append_name().
>
> Fixes: 6e9722e9a7bf ("tpm2-sessions: Fix out of range indexing in name_size")
>
> References:
> [1] trustedcomputinggroup.org/wp-content/uploads/
> Trusted-Platform-Module-2.0-Library-Part-3-Version-184_pub.pdf
>
> Signed-off-by: Srish Srinivasan <ssrish@linux.ibm.com>
> Reviewed-by: Stefan Berger <stefanb@linux.ibm.com>
> ---
> security/keys/trusted-keys/trusted_tpm2.c | 4 ++--
> 1 file changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
> index a7ea4a1c3bed..6340823f8b53 100644
> --- a/security/keys/trusted-keys/trusted_tpm2.c
> +++ b/security/keys/trusted-keys/trusted_tpm2.c
> @@ -465,7 +465,7 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
> }
>
> /**
> - * tpm2_unseal_cmd() - execute a TPM2_Unload command
> + * tpm2_unseal_cmd() - execute a TPM2_Unseal command
> *
> * @chip: TPM chip to use
> * @payload: the key data in clear and encrypted form
> @@ -498,7 +498,7 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
> return rc;
> }
>
> - rc = tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
> + rc = tpm_buf_append_name(chip, &buf, blob_handle, NULL);
> if (rc)
> goto out;
>
> --
> 2.43.0
>
Reviewed-by: Jarkko Sakkinen <jarkko@kernel.org>
And applied. I also need to check what is wrong with my QA because
it should have catched this.
BR, Jarkko
^ permalink raw reply
* Re: [PATCH v2 08/13] mm: update shmem_[kernel]_file_*() functions to use vma_flags_t
From: Jarkko Sakkinen @ 2026-01-25 14:50 UTC (permalink / raw)
To: Lorenzo Stoakes
Cc: Andrew Morton, Dave Hansen, Thomas Gleixner, Ingo Molnar,
Borislav Petkov, x86, H . Peter Anvin, Arnd Bergmann,
Greg Kroah-Hartman, Dan Williams, Vishal Verma, Dave Jiang,
Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Jani Nikula, Joonas Lahtinen, Rodrigo Vivi,
Tvrtko Ursulin, Christian Koenig, Huang Rui, Matthew Auld,
Matthew Brost, Alexander Viro, Christian Brauner, Jan Kara,
Benjamin LaHaise, Gao Xiang, Chao Yu, Yue Hu, Jeffle Xu,
Sandeep Dhavale, Hongbo Li, Chunhai Guo, Theodore Ts'o,
Andreas Dilger, Muchun Song, Oscar Salvador, David Hildenbrand,
Konstantin Komarov, Mike Marshall, Martin Brandenburg, Tony Luck,
Reinette Chatre, Dave Martin, James Morse, Babu Moger,
Carlos Maiolino, Damien Le Moal, Naohiro Aota, Johannes Thumshirn,
Matthew Wilcox, Liam R . Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Hugh Dickins, Baolin Wang,
Zi Yan, Nico Pache, Ryan Roberts, Dev Jain, Barry Song,
Lance Yang, Jann Horn, Pedro Falcato, David Howells, Paul Moore,
James Morris, Serge E . Hallyn, Yury Norov, Rasmus Villemoes,
linux-sgx, linux-kernel, nvdimm, linux-cxl, dri-devel, intel-gfx,
linux-fsdevel, linux-aio, linux-erofs, linux-ext4, linux-mm,
ntfs3, devel, linux-xfs, keyrings, linux-security-module,
Jason Gunthorpe
In-Reply-To: <736febd280eb484d79cef5cf55b8a6f79ad832d2.1769097829.git.lorenzo.stoakes@oracle.com>
On Thu, Jan 22, 2026 at 04:06:17PM +0000, Lorenzo Stoakes wrote:
> In order to be able to use only vma_flags_t in vm_area_desc we must adjust
> shmem file setup functions to operate in terms of vma_flags_t rather than
> vm_flags_t.
>
> This patch makes this change and updates all callers to use the new
> functions.
>
> No functional changes intended.
>
> Signed-off-by: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
> ---
> arch/x86/kernel/cpu/sgx/ioctl.c | 2 +-
> drivers/gpu/drm/drm_gem.c | 5 +-
> drivers/gpu/drm/i915/gem/i915_gem_shmem.c | 2 +-
> drivers/gpu/drm/i915/gem/i915_gem_ttm.c | 3 +-
> drivers/gpu/drm/i915/gt/shmem_utils.c | 3 +-
> drivers/gpu/drm/ttm/tests/ttm_tt_test.c | 2 +-
> drivers/gpu/drm/ttm/ttm_backup.c | 3 +-
> drivers/gpu/drm/ttm/ttm_tt.c | 2 +-
> fs/xfs/scrub/xfile.c | 3 +-
> fs/xfs/xfs_buf_mem.c | 2 +-
> include/linux/shmem_fs.h | 8 ++-
> ipc/shm.c | 6 +--
> mm/memfd.c | 2 +-
> mm/memfd_luo.c | 2 +-
> mm/shmem.c | 59 +++++++++++++----------
> security/keys/big_key.c | 2 +-
> 16 files changed, 57 insertions(+), 49 deletions(-)
>
> diff --git a/arch/x86/kernel/cpu/sgx/ioctl.c b/arch/x86/kernel/cpu/sgx/ioctl.c
> index 9322a9287dc7..0bc36957979d 100644
> --- a/arch/x86/kernel/cpu/sgx/ioctl.c
> +++ b/arch/x86/kernel/cpu/sgx/ioctl.c
> @@ -83,7 +83,7 @@ static int sgx_encl_create(struct sgx_encl *encl, struct sgx_secs *secs)
> encl_size = secs->size + PAGE_SIZE;
>
> backing = shmem_file_setup("SGX backing", encl_size + (encl_size >> 5),
> - VM_NORESERVE);
> + mk_vma_flags(VMA_NORESERVE_BIT));
> if (IS_ERR(backing)) {
> ret = PTR_ERR(backing);
> goto err_out_shrink;
As per this diff:
Reviewed-by: Jarkko Sakkinen <jarkko@kernel.org>
> diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c
> index e4df43427394..be4dca2bc34e 100644
> --- a/drivers/gpu/drm/drm_gem.c
> +++ b/drivers/gpu/drm/drm_gem.c
> @@ -130,14 +130,15 @@ int drm_gem_object_init_with_mnt(struct drm_device *dev,
> struct vfsmount *gemfs)
> {
> struct file *filp;
> + const vma_flags_t flags = mk_vma_flags(VMA_NORESERVE_BIT);
>
> drm_gem_private_object_init(dev, obj, size);
>
> if (gemfs)
> filp = shmem_file_setup_with_mnt(gemfs, "drm mm object", size,
> - VM_NORESERVE);
> + flags);
> else
> - filp = shmem_file_setup("drm mm object", size, VM_NORESERVE);
> + filp = shmem_file_setup("drm mm object", size, flags);
>
> if (IS_ERR(filp))
> return PTR_ERR(filp);
> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_shmem.c b/drivers/gpu/drm/i915/gem/i915_gem_shmem.c
> index 26dda55a07ff..fe1843497b27 100644
> --- a/drivers/gpu/drm/i915/gem/i915_gem_shmem.c
> +++ b/drivers/gpu/drm/i915/gem/i915_gem_shmem.c
> @@ -496,7 +496,7 @@ static int __create_shmem(struct drm_i915_private *i915,
> struct drm_gem_object *obj,
> resource_size_t size)
> {
> - unsigned long flags = VM_NORESERVE;
> + const vma_flags_t flags = mk_vma_flags(VMA_NORESERVE_BIT);
> struct file *filp;
>
> drm_gem_private_object_init(&i915->drm, obj, size);
> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_ttm.c b/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
> index f65fe86c02b5..7b1a7d01db2b 100644
> --- a/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
> +++ b/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
> @@ -200,7 +200,8 @@ static int i915_ttm_tt_shmem_populate(struct ttm_device *bdev,
> struct address_space *mapping;
> gfp_t mask;
>
> - filp = shmem_file_setup("i915-shmem-tt", size, VM_NORESERVE);
> + filp = shmem_file_setup("i915-shmem-tt", size,
> + mk_vma_flags(VMA_NORESERVE_BIT));
> if (IS_ERR(filp))
> return PTR_ERR(filp);
>
> diff --git a/drivers/gpu/drm/i915/gt/shmem_utils.c b/drivers/gpu/drm/i915/gt/shmem_utils.c
> index 365c4b8b04f4..5f37c699a320 100644
> --- a/drivers/gpu/drm/i915/gt/shmem_utils.c
> +++ b/drivers/gpu/drm/i915/gt/shmem_utils.c
> @@ -19,7 +19,8 @@ struct file *shmem_create_from_data(const char *name, void *data, size_t len)
> struct file *file;
> int err;
>
> - file = shmem_file_setup(name, PAGE_ALIGN(len), VM_NORESERVE);
> + file = shmem_file_setup(name, PAGE_ALIGN(len),
> + mk_vma_flags(VMA_NORESERVE_BIT));
> if (IS_ERR(file))
> return file;
>
> diff --git a/drivers/gpu/drm/ttm/tests/ttm_tt_test.c b/drivers/gpu/drm/ttm/tests/ttm_tt_test.c
> index 61ec6f580b62..bd5f7d0b9b62 100644
> --- a/drivers/gpu/drm/ttm/tests/ttm_tt_test.c
> +++ b/drivers/gpu/drm/ttm/tests/ttm_tt_test.c
> @@ -143,7 +143,7 @@ static void ttm_tt_fini_shmem(struct kunit *test)
> err = ttm_tt_init(tt, bo, 0, caching, 0);
> KUNIT_ASSERT_EQ(test, err, 0);
>
> - shmem = shmem_file_setup("ttm swap", BO_SIZE, 0);
> + shmem = shmem_file_setup("ttm swap", BO_SIZE, EMPTY_VMA_FLAGS);
> tt->swap_storage = shmem;
>
> ttm_tt_fini(tt);
> diff --git a/drivers/gpu/drm/ttm/ttm_backup.c b/drivers/gpu/drm/ttm/ttm_backup.c
> index 32530c75f038..6bd4c123d94c 100644
> --- a/drivers/gpu/drm/ttm/ttm_backup.c
> +++ b/drivers/gpu/drm/ttm/ttm_backup.c
> @@ -178,5 +178,6 @@ EXPORT_SYMBOL_GPL(ttm_backup_bytes_avail);
> */
> struct file *ttm_backup_shmem_create(loff_t size)
> {
> - return shmem_file_setup("ttm shmem backup", size, 0);
> + return shmem_file_setup("ttm shmem backup", size,
> + EMPTY_VMA_FLAGS);
> }
> diff --git a/drivers/gpu/drm/ttm/ttm_tt.c b/drivers/gpu/drm/ttm/ttm_tt.c
> index 611d20ab966d..f73a5ce87645 100644
> --- a/drivers/gpu/drm/ttm/ttm_tt.c
> +++ b/drivers/gpu/drm/ttm/ttm_tt.c
> @@ -330,7 +330,7 @@ int ttm_tt_swapout(struct ttm_device *bdev, struct ttm_tt *ttm,
> struct page *to_page;
> int i, ret;
>
> - swap_storage = shmem_file_setup("ttm swap", size, 0);
> + swap_storage = shmem_file_setup("ttm swap", size, EMPTY_VMA_FLAGS);
> if (IS_ERR(swap_storage)) {
> pr_err("Failed allocating swap storage\n");
> return PTR_ERR(swap_storage);
> diff --git a/fs/xfs/scrub/xfile.c b/fs/xfs/scrub/xfile.c
> index c753c79df203..fe0584a39f16 100644
> --- a/fs/xfs/scrub/xfile.c
> +++ b/fs/xfs/scrub/xfile.c
> @@ -61,7 +61,8 @@ xfile_create(
> if (!xf)
> return -ENOMEM;
>
> - xf->file = shmem_kernel_file_setup(description, isize, VM_NORESERVE);
> + xf->file = shmem_kernel_file_setup(description, isize,
> + mk_vma_flags(VMA_NORESERVE_BIT));
> if (IS_ERR(xf->file)) {
> error = PTR_ERR(xf->file);
> goto out_xfile;
> diff --git a/fs/xfs/xfs_buf_mem.c b/fs/xfs/xfs_buf_mem.c
> index dcbfa274e06d..fd6f0a5bc0ea 100644
> --- a/fs/xfs/xfs_buf_mem.c
> +++ b/fs/xfs/xfs_buf_mem.c
> @@ -62,7 +62,7 @@ xmbuf_alloc(
> if (!btp)
> return -ENOMEM;
>
> - file = shmem_kernel_file_setup(descr, 0, 0);
> + file = shmem_kernel_file_setup(descr, 0, EMPTY_VMA_FLAGS);
> if (IS_ERR(file)) {
> error = PTR_ERR(file);
> goto out_free_btp;
> diff --git a/include/linux/shmem_fs.h b/include/linux/shmem_fs.h
> index e2069b3179c4..a8273b32e041 100644
> --- a/include/linux/shmem_fs.h
> +++ b/include/linux/shmem_fs.h
> @@ -102,12 +102,10 @@ static inline struct shmem_inode_info *SHMEM_I(struct inode *inode)
> extern const struct fs_parameter_spec shmem_fs_parameters[];
> extern void shmem_init(void);
> extern int shmem_init_fs_context(struct fs_context *fc);
> -extern struct file *shmem_file_setup(const char *name,
> - loff_t size, unsigned long flags);
> -extern struct file *shmem_kernel_file_setup(const char *name, loff_t size,
> - unsigned long flags);
> +struct file *shmem_file_setup(const char *name, loff_t size, vma_flags_t flags);
> +struct file *shmem_kernel_file_setup(const char *name, loff_t size, vma_flags_t vma_flags);
> extern struct file *shmem_file_setup_with_mnt(struct vfsmount *mnt,
> - const char *name, loff_t size, unsigned long flags);
> + const char *name, loff_t size, vma_flags_t flags);
> int shmem_zero_setup(struct vm_area_struct *vma);
> int shmem_zero_setup_desc(struct vm_area_desc *desc);
> extern unsigned long shmem_get_unmapped_area(struct file *, unsigned long addr,
> diff --git a/ipc/shm.c b/ipc/shm.c
> index 2c7379c4c647..e8c7d1924c50 100644
> --- a/ipc/shm.c
> +++ b/ipc/shm.c
> @@ -708,6 +708,7 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params)
> struct shmid_kernel *shp;
> size_t numpages = (size + PAGE_SIZE - 1) >> PAGE_SHIFT;
> const bool has_no_reserve = shmflg & SHM_NORESERVE;
> + vma_flags_t acctflag = EMPTY_VMA_FLAGS;
> struct file *file;
> char name[13];
>
> @@ -738,7 +739,6 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params)
>
> sprintf(name, "SYSV%08x", key);
> if (shmflg & SHM_HUGETLB) {
> - vma_flags_t acctflag = EMPTY_VMA_FLAGS;
> struct hstate *hs;
> size_t hugesize;
>
> @@ -755,14 +755,12 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params)
> file = hugetlb_file_setup(name, hugesize, acctflag,
> HUGETLB_SHMFS_INODE, (shmflg >> SHM_HUGE_SHIFT) & SHM_HUGE_MASK);
> } else {
> - vm_flags_t acctflag = 0;
> -
> /*
> * Do not allow no accounting for OVERCOMMIT_NEVER, even
> * if it's asked for.
> */
> if (has_no_reserve && sysctl_overcommit_memory != OVERCOMMIT_NEVER)
> - acctflag = VM_NORESERVE;
> + vma_flags_set(&acctflag, VMA_NORESERVE_BIT);
> file = shmem_kernel_file_setup(name, size, acctflag);
> }
> error = PTR_ERR(file);
> diff --git a/mm/memfd.c b/mm/memfd.c
> index 5f95f639550c..f3a8950850a2 100644
> --- a/mm/memfd.c
> +++ b/mm/memfd.c
> @@ -469,7 +469,7 @@ static struct file *alloc_file(const char *name, unsigned int flags)
> (flags >> MFD_HUGE_SHIFT) &
> MFD_HUGE_MASK);
> } else {
> - file = shmem_file_setup(name, 0, VM_NORESERVE);
> + file = shmem_file_setup(name, 0, mk_vma_flags(VMA_NORESERVE_BIT));
> }
> if (IS_ERR(file))
> return file;
> diff --git a/mm/memfd_luo.c b/mm/memfd_luo.c
> index 4f6ba63b4310..a2629dcfd0f1 100644
> --- a/mm/memfd_luo.c
> +++ b/mm/memfd_luo.c
> @@ -443,7 +443,7 @@ static int memfd_luo_retrieve(struct liveupdate_file_op_args *args)
> if (!ser)
> return -EINVAL;
>
> - file = shmem_file_setup("", 0, VM_NORESERVE);
> + file = shmem_file_setup("", 0, mk_vma_flags(VMA_NORESERVE_BIT));
>
> if (IS_ERR(file)) {
> pr_err("failed to setup file: %pe\n", file);
> diff --git a/mm/shmem.c b/mm/shmem.c
> index 0adde3f4df27..97a8f55c7296 100644
> --- a/mm/shmem.c
> +++ b/mm/shmem.c
> @@ -3057,9 +3057,9 @@ static struct offset_ctx *shmem_get_offset_ctx(struct inode *inode)
> }
>
> static struct inode *__shmem_get_inode(struct mnt_idmap *idmap,
> - struct super_block *sb,
> - struct inode *dir, umode_t mode,
> - dev_t dev, unsigned long flags)
> + struct super_block *sb,
> + struct inode *dir, umode_t mode,
> + dev_t dev, vma_flags_t flags)
> {
> struct inode *inode;
> struct shmem_inode_info *info;
> @@ -3087,7 +3087,8 @@ static struct inode *__shmem_get_inode(struct mnt_idmap *idmap,
> spin_lock_init(&info->lock);
> atomic_set(&info->stop_eviction, 0);
> info->seals = F_SEAL_SEAL;
> - info->flags = (flags & VM_NORESERVE) ? SHMEM_F_NORESERVE : 0;
> + info->flags = vma_flags_test(&flags, VMA_NORESERVE_BIT)
> + ? SHMEM_F_NORESERVE : 0;
> info->i_crtime = inode_get_mtime(inode);
> info->fsflags = (dir == NULL) ? 0 :
> SHMEM_I(dir)->fsflags & SHMEM_FL_INHERITED;
> @@ -3140,7 +3141,7 @@ static struct inode *__shmem_get_inode(struct mnt_idmap *idmap,
> #ifdef CONFIG_TMPFS_QUOTA
> static struct inode *shmem_get_inode(struct mnt_idmap *idmap,
> struct super_block *sb, struct inode *dir,
> - umode_t mode, dev_t dev, unsigned long flags)
> + umode_t mode, dev_t dev, vma_flags_t flags)
> {
> int err;
> struct inode *inode;
> @@ -3166,9 +3167,9 @@ static struct inode *shmem_get_inode(struct mnt_idmap *idmap,
> return ERR_PTR(err);
> }
> #else
> -static inline struct inode *shmem_get_inode(struct mnt_idmap *idmap,
> +static struct inode *shmem_get_inode(struct mnt_idmap *idmap,
> struct super_block *sb, struct inode *dir,
> - umode_t mode, dev_t dev, unsigned long flags)
> + umode_t mode, dev_t dev, vma_flags_t flags)
> {
> return __shmem_get_inode(idmap, sb, dir, mode, dev, flags);
> }
> @@ -3875,7 +3876,8 @@ shmem_mknod(struct mnt_idmap *idmap, struct inode *dir,
> if (!generic_ci_validate_strict_name(dir, &dentry->d_name))
> return -EINVAL;
>
> - inode = shmem_get_inode(idmap, dir->i_sb, dir, mode, dev, VM_NORESERVE);
> + inode = shmem_get_inode(idmap, dir->i_sb, dir, mode, dev,
> + mk_vma_flags(VMA_NORESERVE_BIT));
> if (IS_ERR(inode))
> return PTR_ERR(inode);
>
> @@ -3910,7 +3912,8 @@ shmem_tmpfile(struct mnt_idmap *idmap, struct inode *dir,
> struct inode *inode;
> int error;
>
> - inode = shmem_get_inode(idmap, dir->i_sb, dir, mode, 0, VM_NORESERVE);
> + inode = shmem_get_inode(idmap, dir->i_sb, dir, mode, 0,
> + mk_vma_flags(VMA_NORESERVE_BIT));
> if (IS_ERR(inode)) {
> error = PTR_ERR(inode);
> goto err_out;
> @@ -4107,7 +4110,7 @@ static int shmem_symlink(struct mnt_idmap *idmap, struct inode *dir,
> return -ENAMETOOLONG;
>
> inode = shmem_get_inode(idmap, dir->i_sb, dir, S_IFLNK | 0777, 0,
> - VM_NORESERVE);
> + mk_vma_flags(VMA_NORESERVE_BIT));
> if (IS_ERR(inode))
> return PTR_ERR(inode);
>
> @@ -5108,7 +5111,8 @@ static int shmem_fill_super(struct super_block *sb, struct fs_context *fc)
> #endif /* CONFIG_TMPFS_QUOTA */
>
> inode = shmem_get_inode(&nop_mnt_idmap, sb, NULL,
> - S_IFDIR | sbinfo->mode, 0, VM_NORESERVE);
> + S_IFDIR | sbinfo->mode, 0,
> + mk_vma_flags(VMA_NORESERVE_BIT));
> if (IS_ERR(inode)) {
> error = PTR_ERR(inode);
> goto failed;
> @@ -5808,7 +5812,7 @@ static inline void shmem_unacct_size(unsigned long flags, loff_t size)
>
> static inline struct inode *shmem_get_inode(struct mnt_idmap *idmap,
> struct super_block *sb, struct inode *dir,
> - umode_t mode, dev_t dev, unsigned long flags)
> + umode_t mode, dev_t dev, vma_flags_t flags)
> {
> struct inode *inode = ramfs_get_inode(sb, dir, mode, dev);
> return inode ? inode : ERR_PTR(-ENOSPC);
> @@ -5819,10 +5823,11 @@ static inline struct inode *shmem_get_inode(struct mnt_idmap *idmap,
> /* common code */
>
> static struct file *__shmem_file_setup(struct vfsmount *mnt, const char *name,
> - loff_t size, unsigned long vm_flags,
> + loff_t size, vma_flags_t flags,
> unsigned int i_flags)
> {
> - unsigned long flags = (vm_flags & VM_NORESERVE) ? SHMEM_F_NORESERVE : 0;
> + const unsigned long shmem_flags =
> + vma_flags_test(&flags, VMA_NORESERVE_BIT) ? SHMEM_F_NORESERVE : 0;
> struct inode *inode;
> struct file *res;
>
> @@ -5835,13 +5840,13 @@ static struct file *__shmem_file_setup(struct vfsmount *mnt, const char *name,
> if (is_idmapped_mnt(mnt))
> return ERR_PTR(-EINVAL);
>
> - if (shmem_acct_size(flags, size))
> + if (shmem_acct_size(shmem_flags, size))
> return ERR_PTR(-ENOMEM);
>
> inode = shmem_get_inode(&nop_mnt_idmap, mnt->mnt_sb, NULL,
> - S_IFREG | S_IRWXUGO, 0, vm_flags);
> + S_IFREG | S_IRWXUGO, 0, flags);
> if (IS_ERR(inode)) {
> - shmem_unacct_size(flags, size);
> + shmem_unacct_size(shmem_flags, size);
> return ERR_CAST(inode);
> }
> inode->i_flags |= i_flags;
> @@ -5864,9 +5869,10 @@ static struct file *__shmem_file_setup(struct vfsmount *mnt, const char *name,
> * checks are provided at the key or shm level rather than the inode.
> * @name: name for dentry (to be seen in /proc/<pid>/maps)
> * @size: size to be set for the file
> - * @flags: VM_NORESERVE suppresses pre-accounting of the entire object size
> + * @vma_flags: VMA_NORESERVE_BIT suppresses pre-accounting of the entire object size
> */
> -struct file *shmem_kernel_file_setup(const char *name, loff_t size, unsigned long flags)
> +struct file *shmem_kernel_file_setup(const char *name, loff_t size,
> + vma_flags_t flags)
> {
> return __shmem_file_setup(shm_mnt, name, size, flags, S_PRIVATE);
> }
> @@ -5878,7 +5884,7 @@ EXPORT_SYMBOL_GPL(shmem_kernel_file_setup);
> * @size: size to be set for the file
> * @flags: VM_NORESERVE suppresses pre-accounting of the entire object size
> */
> -struct file *shmem_file_setup(const char *name, loff_t size, unsigned long flags)
> +struct file *shmem_file_setup(const char *name, loff_t size, vma_flags_t flags)
> {
> return __shmem_file_setup(shm_mnt, name, size, flags, 0);
> }
> @@ -5889,16 +5895,17 @@ EXPORT_SYMBOL_GPL(shmem_file_setup);
> * @mnt: the tmpfs mount where the file will be created
> * @name: name for dentry (to be seen in /proc/<pid>/maps)
> * @size: size to be set for the file
> - * @flags: VM_NORESERVE suppresses pre-accounting of the entire object size
> + * @flags: VMA_NORESERVE_BIT suppresses pre-accounting of the entire object size
> */
> struct file *shmem_file_setup_with_mnt(struct vfsmount *mnt, const char *name,
> - loff_t size, unsigned long flags)
> + loff_t size, vma_flags_t flags)
> {
> return __shmem_file_setup(mnt, name, size, flags, 0);
> }
> EXPORT_SYMBOL_GPL(shmem_file_setup_with_mnt);
>
> -static struct file *__shmem_zero_setup(unsigned long start, unsigned long end, vm_flags_t vm_flags)
> +static struct file *__shmem_zero_setup(unsigned long start, unsigned long end,
> + vma_flags_t flags)
> {
> loff_t size = end - start;
>
> @@ -5908,7 +5915,7 @@ static struct file *__shmem_zero_setup(unsigned long start, unsigned long end, v
> * accessible to the user through its mapping, use S_PRIVATE flag to
> * bypass file security, in the same way as shmem_kernel_file_setup().
> */
> - return shmem_kernel_file_setup("dev/zero", size, vm_flags);
> + return shmem_kernel_file_setup("dev/zero", size, flags);
> }
>
> /**
> @@ -5918,7 +5925,7 @@ static struct file *__shmem_zero_setup(unsigned long start, unsigned long end, v
> */
> int shmem_zero_setup(struct vm_area_struct *vma)
> {
> - struct file *file = __shmem_zero_setup(vma->vm_start, vma->vm_end, vma->vm_flags);
> + struct file *file = __shmem_zero_setup(vma->vm_start, vma->vm_end, vma->flags);
>
> if (IS_ERR(file))
> return PTR_ERR(file);
> @@ -5939,7 +5946,7 @@ int shmem_zero_setup(struct vm_area_struct *vma)
> */
> int shmem_zero_setup_desc(struct vm_area_desc *desc)
> {
> - struct file *file = __shmem_zero_setup(desc->start, desc->end, desc->vm_flags);
> + struct file *file = __shmem_zero_setup(desc->start, desc->end, desc->vma_flags);
>
> if (IS_ERR(file))
> return PTR_ERR(file);
> diff --git a/security/keys/big_key.c b/security/keys/big_key.c
> index d46862ab90d6..268f702df380 100644
> --- a/security/keys/big_key.c
> +++ b/security/keys/big_key.c
> @@ -103,7 +103,7 @@ int big_key_preparse(struct key_preparsed_payload *prep)
> 0, enckey);
>
> /* save aligned data to file */
> - file = shmem_kernel_file_setup("", enclen, 0);
> + file = shmem_kernel_file_setup("", enclen, EMPTY_VMA_FLAGS);
> if (IS_ERR(file)) {
> ret = PTR_ERR(file);
> goto err_enckey;
> --
> 2.52.0
>
BR, Jarkko
^ permalink raw reply
* [PATCH] apparmor: add .kunitconfig
From: Ryota Sakamoto @ 2026-01-25 10:05 UTC (permalink / raw)
To: John Johansen, Paul Moore, James Morris, Serge E. Hallyn
Cc: linux-kernel, apparmor, linux-security-module, Ryota Sakamoto
Add .kunitconfig file to the AppArmor directory to enable easy execution of
KUnit tests.
AppArmor tests (CONFIG_SECURITY_APPARMOR_KUNIT_TEST) depend on
CONFIG_SECURITY_APPARMOR which also depends on CONFIG_SECURITY and
CONFIG_NET. Without explicitly enabling these configs in the .kunitconfig,
developers will need to specify config manually.
With the .kunitconfig, developers can run the tests:
$ ./tools/testing/kunit/kunit.py run --kunitconfig security/apparmor
Signed-off-by: Ryota Sakamoto <sakamo.ryota@gmail.com>
---
security/apparmor/.kunitconfig | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/security/apparmor/.kunitconfig b/security/apparmor/.kunitconfig
new file mode 100644
index 0000000000000000000000000000000000000000..aa842a0266e9d33c3333ec2ea180206187b0eb4c
--- /dev/null
+++ b/security/apparmor/.kunitconfig
@@ -0,0 +1,5 @@
+CONFIG_KUNIT=y
+CONFIG_NET=y
+CONFIG_SECURITY=y
+CONFIG_SECURITY_APPARMOR=y
+CONFIG_SECURITY_APPARMOR_KUNIT_TEST=y
---
base-commit: d91a46d6805af41e7f2286e0fc22d498f45a682b
change-id: 20260125-add-apparmor-kunitconfig-28aba43c1580
Best regards,
--
Ryota Sakamoto <sakamo.ryota@gmail.com>
^ permalink raw reply related
* Re: [RFC PATCH 2/2] landlock: transpose the layer masks data structure
From: Tingmao Wang @ 2026-01-25 1:52 UTC (permalink / raw)
To: Günther Noack
Cc: Mickaël Salaün, linux-security-module, Justin Suess,
Samasth Norway Ananda, Matthieu Buffet, Mikhail Ivanov,
konstantin.meskhidze
In-Reply-To: <20251230103917.10549-7-gnoack3000@gmail.com>
On 12/30/25 10:39, Günther Noack wrote:
> [...]
> diff --git a/security/landlock/audit.c b/security/landlock/audit.c
> index c52d079cdb77b..650bd7f5cb6be 100644
> --- a/security/landlock/audit.c
> +++ b/security/landlock/audit.c
> [...]
> -static size_t
> -get_layer_from_deny_masks(access_mask_t *const access_request,
> - const access_mask_t all_existing_optional_access,
> - const deny_masks_t deny_masks)
> +/*
> + * get_layer_from_fs_deny_masks - get the layer which denied the access request
> + *
> + * As a side effect, stores the denied access rights from that layer(!) in
> + * *access_request.
> + */
> +static size_t get_layer_from_fs_deny_masks(access_mask_t *const access_request,
> + const deny_masks_t deny_masks)
> {
> - const unsigned long access_opt = all_existing_optional_access;
> - const unsigned long access_req = *access_request;
> - access_mask_t missing = 0;
> + const access_mask_t access_req = *access_request;
> size_t youngest_layer = 0;
> - size_t access_index = 0;
> - unsigned long access_bit;
> + access_mask_t missing = 0;
>
> - /* This will require change with new object types. */
> - WARN_ON_ONCE(access_opt != _LANDLOCK_ACCESS_FS_OPTIONAL);
> + WARN_ON_ONCE((access_req | _LANDLOCK_ACCESS_FS_OPTIONAL) !=
> + _LANDLOCK_ACCESS_FS_OPTIONAL);
>
> - for_each_set_bit(access_bit, &access_opt,
> - BITS_PER_TYPE(access_mask_t)) {
> - if (access_req & BIT(access_bit)) {
> - const size_t layer =
> - (deny_masks >> (access_index * 4)) &
> - (LANDLOCK_MAX_NUM_LAYERS - 1);
> + if (access_req & LANDLOCK_ACCESS_FS_TRUNCATE) {
> + size_t layer = deny_masks & 0x0f;
>
> - if (layer > youngest_layer) {
> - youngest_layer = layer;
> - missing = BIT(access_bit);
> - } else if (layer == youngest_layer) {
> - missing |= BIT(access_bit);
> - }
> - }
> - access_index++;
> + missing |= LANDLOCK_ACCESS_FS_TRUNCATE;
> + youngest_layer = max(youngest_layer, layer);
> }
> + if (access_req & LANDLOCK_ACCESS_FS_IOCTL_DEV) {
> + size_t layer = (deny_masks & 0xf0) >> 4;
>
> + if (layer > youngest_layer)
> + missing = 0;
> +
> + missing |= LANDLOCK_ACCESS_FS_IOCTL_DEV;
I think this should be under a `if (layer == youngest_layer)`. If layer <
youngest_layer, because *access_request is only supposed to contain the
missing access from the youngest denying layer, this would be incorrect.
Although I think it probably won't make a difference in practice right now
since we don't currently have any access request that has truncate and
ioctl at the same time.
> + youngest_layer = max(youngest_layer, layer);
> + }
> *access_request = missing;
> return youngest_layer;
> }
>
> #ifdef CONFIG_SECURITY_LANDLOCK_KUNIT_TEST
>
> -static void test_get_layer_from_deny_masks(struct kunit *const test)
> +static void test_get_layer_from_fs_deny_masks(struct kunit *const test)
> {
> deny_masks_t deny_mask;
> access_mask_t access;
> [...]
^ permalink raw reply
* Re: [PATCH v4 4/6] pseries/plpks: add HCALLs for PowerVM Key Wrapping Module
From: Christophe Leroy (CS GROUP) @ 2026-01-24 10:03 UTC (permalink / raw)
To: Nayna Jain, Srish Srinivasan, linux-integrity, keyrings,
linuxppc-dev
Cc: maddy, mpe, npiggin, James.Bottomley, jarkko, zohar, rnsastry,
linux-kernel, linux-security-module
In-Reply-To: <5b29327e-9175-416f-b34b-da4f6ac03a96@linux.ibm.com>
Le 15/01/2026 à 21:45, Nayna Jain a écrit :
>
> On 1/15/26 5:05 AM, Srish Srinivasan wrote:
>> The hypervisor generated wrapping key is an AES-GCM-256 symmetric key
>> which
>> is stored in a non-volatile, secure, and encrypted storage called the
>> Power
>> LPAR Platform KeyStore. It has policy based protections that prevent it
>> from being read out or exposed to the user.
>>
>> Implement H_PKS_GEN_KEY, H_PKS_WRAP_OBJECT, and H_PKS_UNWRAP_OBJECT
>> HCALLs
>> to enable using the PowerVM Key Wrapping Module (PKWM) as a new trust
>> source for trusted keys. Disallow H_PKS_READ_OBJECT, H_PKS_SIGNED_UPDATE,
>> and H_PKS_WRITE_OBJECT for objects with the 'wrapping key' policy set.
>> Capture the availability status for the H_PKS_WRAP_OBJECT interface.
>
> Reviewed-by: Nayna Jain <nayna@linux.ibm.com>
>>
>> Signed-off-by: Srish Srinivasan <ssrish@linux.ibm.com>
>> ---
>> Documentation/arch/powerpc/papr_hcalls.rst | 43 +++
>> arch/powerpc/include/asm/plpks.h | 10 +
>> arch/powerpc/platforms/pseries/plpks.c | 342 ++++++++++++++++++++-
>> 3 files changed, 393 insertions(+), 2 deletions(-)
[...]
>> diff --git a/arch/powerpc/platforms/pseries/plpks.c b/arch/powerpc/
>> platforms/pseries/plpks.c
>> index 4a08f51537c8..b97b7750f6a8 100644
>> --- a/arch/powerpc/platforms/pseries/plpks.c
>> +++ b/arch/powerpc/platforms/pseries/plpks.c
>> @@ -9,6 +9,32 @@
>> #define pr_fmt(fmt) "plpks: " fmt
>> +#define PLPKS_WRAPKEY_COMPONENT "PLPKSWR"
>> +#define PLPKS_WRAPKEY_NAME "default-wrapping-key"
>> +
>> +/*
>> + * To 4K align the {input, output} buffers to the {UN}WRAP H_CALLs
>> + */
>> +#define PLPKS_WRAPPING_BUF_ALIGN 4096
>> +
>> +/*
>> + * To ensure the output buffer's length is at least 1024 bytes greater
>> + * than the input buffer's length during the WRAP H_CALL
>> + */
>> +#define PLPKS_WRAPPING_BUF_DIFF 1024
>> +
>> +#define PLPKS_WRAP_INTERFACE_BIT 3
>> +#define PLPKS_WRAPPING_KEY_LENGTH 32
>> +
>> +#define WRAPFLAG_BE_BIT_SET(be_bit) \
>> + BIT_ULL(63 - (be_bit))
>> +
>> +#define WRAPFLAG_BE_GENMASK(be_bit_hi, be_bit_lo) \
>> + GENMASK_ULL(63 - (be_bit_hi), 63 - (be_bit_lo))
>> +
>> +#define WRAPFLAG_BE_FIELD_PREP(be_bit_hi, be_bit_lo, val) \
>> + FIELD_PREP(WRAPFLAG_BE_GENMASK(be_bit_hi, be_bit_lo), (val))
I get following build failure:
CC arch/powerpc/platforms/pseries/plpks.o
arch/powerpc/platforms/pseries/plpks.c: In function 'plpks_wrap_object':
arch/powerpc/platforms/pseries/plpks.c:36:9: error: implicit declaration
of function 'FIELD_PREP' [-Werror=implicit-function-declaration]
36 | FIELD_PREP(WRAPFLAG_BE_GENMASK(be_bit_hi, be_bit_lo),
(val))
| ^~~~~~~~~~
arch/powerpc/platforms/pseries/plpks.c:1049:25: note: in expansion of
macro 'WRAPFLAG_BE_FIELD_PREP'
1049 | objwrapflags |= WRAPFLAG_BE_FIELD_PREP(60, 63, 0x1);
| ^~~~~~~~~~~~~~~~~~~~~~
cc1: all warnings being treated as errors
>> +
>> #include <linux/delay.h>
>> #include <linux/errno.h>
>> #include <linux/io.h>
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox