Skip to contentSkip to conversation

Extend PKWM to support user-created wrapping keys

PATCH In review Srish Srinivasan 3 participants 25 messages Nov 5 – Nov 10

Subsystems (8)
lore
Share & tools
lore

Srish Srinivasan

author maintainer
#0 lore raw

v6 cover Extend PKWM to support user-created wrapping keys

The PKWM trusted source currently uses a single default wrapping key per LPAR. This key is created during trusted source initialization, and all trusted keys backed by PKWM are sealed and unsealed using it.

Recent versions of PKWM allow users to create and manage their own wrapping keys through a set of lifecycle operations. This patch series brings these PKWM capabilities into the kernel, allowing users to create, manage, and select wrapping keys for sealing their trusted keys, rather than requiring all trusted keys to use the default wrapping key.

This series extends PKWM to support user-created wrapping keys. It adds the required HCALLs, allows users to select a wrapping key by label when creating PKWM-backed trusted keys, and introduces a sysfs interface for managing wrapping keys from userspace.

This patchset also includes six preparatory fixes. These correct double conversion of PLPKS write, remove, and wrapping key flush errors; align wrapping length types with the underlying HCALL interfaces; preserve the original error returned when default wrapping key generation fails; initialize PKWM output buffer pointers before cleanup; and clear sensitive PKWM seal and unseal buffers before freeing them.

Changelog:

v5 -> v6:
- Hardened wrapping key lifecycle HCALL helpers
- Default wrapping key check
- Component prefix and wrapping key handling
- Label length validation
- -ENXIO when revoke/unrevoke are unsupported
- Rejected non-ASCII characters in wrapping key labels
- Added stable prerequisite to the wrapping key flush error fix
- Re-ordered the stable fixes

v4 -> v5:
- Addressed issues reported by Sashiko
- Added HCALL-defined length validation for wrapping operations
- Cleared stale PLPKS sysfs kobject pointers on initialization failure
- Documented "default-wrapping-key" as a reserved label without assuming it exists
- Dropped Tested-by tags from modified patches

v3 -> v4:
- Addressed issues reported by Sashiko
- Fixed double conversion of PLPKS object flush errors
- Initialized PKWM output buffer pointers before cleanup
- Corrected the documented H_PKS_GET_OBJECTLABELS output field order
- Prevented stale policy use and caller data modification during revocation checks
- Fixed wrapping key sysfs kobject cleanup
- Restricted wrapping key sysfs initialization to pseries
- Dropped Tested-by tags from modified patches

v2 -> v3:
- Moved most of the refactoring and hardening patches into a separate series:
https://lore.kernel.org/all/[email protected]/
- Added a fix to clear sensitive PKWM seal and unseal buffers before freeing them
- Added Nageswara's Tested-by tag
- Refined error handling and documented default wrapping key restrictions

v1 -> v2:
- Addressed parameter validation issues reported by Nageswara
- Tweaked PLPKS_WRAPKEY_SYSFS Kconfig help text
- Added two preparatory bug-fix patches
- Cc'd [email protected] where applicable

Srish Srinivasan (9):
keys/trusted_keys: propagate wrapping key generation errors keys/trusted_keys: fix PKWM output buffer cleanup keys/trusted_keys: clear sensitive PKWM buffers pseries/plpks: fix double conversion of write and remove flush errors pseries/plpks: fix double conversion of wrapping key flush errors pseries/plpks: fix length types for wrapping operations pseries/plpks: add HCALLs for PKWM wrapping key life cycle management keys/trusted_keys: enable PKWM wrapping key selection by label pseries/plpks/wrapkey: expose PKWM wrapping key management to
userspace via sysfs

 .../ABI/testing/sysfs-firmware-plpks          | 113 ++++
 Documentation/arch/powerpc/papr_hcalls.rst    |  33 +
 .../security/keys/trusted-encrypted.rst       |   4 +-
 arch/powerpc/include/asm/hvcall.h             |   5 +-
 arch/powerpc/include/asm/plpks.h              |  40 +-
 arch/powerpc/platforms/pseries/Kconfig        |  15 +
 arch/powerpc/platforms/pseries/Makefile       |   1 +
 arch/powerpc/platforms/pseries/plpks-sysfs.c  |  14 +
 .../platforms/pseries/plpks-wrapkey-sysfs.c   | 408 +++++++++++++
 arch/powerpc/platforms/pseries/plpks.c        | 576 ++++++++++++++++--
 include/keys/trusted_pkwm.h                   |   3 +
 security/keys/trusted-keys/trusted_pkwm.c     |  52 +-
 12 files changed, 1203 insertions(+), 61 deletions(-)
 create mode 100644 arch/powerpc/platforms/pseries/plpks-wrapkey-sysfs.c
Signature · 1 line
2.52.0
author maintainer
#1 lore raw

PATCH v6 1/9 keys/trusted_keys: propagate wrapping key generation errors

plpks_gen_wrapping_key() returns a negative errno when default wrapping key generation fails. However, trusted_pkwm_init() returns -EINVAL for all such failures, discarding the original error.

Propagate the error returned by plpks_gen_wrapping_key() unchanged.

Fixes c99fcb0d735b ("keys/trusted_keys: establish PKWM as a trusted source")
---
 security/keys/trusted-keys/trusted_pkwm.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
security/keys/trusted-keys/trusted_pkwm.c +1 -1
index bf42c6679245..422f752598e1 100644
@@ -170,7 +170,7 @@ static int trusted_pkwm_init(void)
 	ret = plpks_gen_wrapping_key();
 	if (ret) {
 		pr_err("Failed to generate default wrapping key\n");
-		return -EINVAL;
+		return ret;
 	}
 
 	return register_key_type(&key_type_trusted);
Signature · 1 line
2.52.0
#2 lore raw

Re: [PATCH v6 1/9] keys/trusted_keys: propagate wrapping key generation errors

Sashiko has reviewed this patch and found no issues.
Quoted 9 lines

plpks_gen_wrapping_key() returns a negative errno when default wrapping key generation fails. However, trusted_pkwm_init() returns -EINVAL for all such failures, discarding the original error.

Propagate the error returned by plpks_gen_wrapping_key() unchanged.

Fixes c99fcb0d735b ("keys/trusted_keys: establish PKWM as a trusted source")

Sashiko has reviewed this patch and found no issues.

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=1
maintainer Reviewed-by on v6 1/9
#3 lore raw

Re: [PATCH v6 1/9] keys/trusted_keys: propagate wrapping key generation errors

In reply to Srish Srinivasan
Quoted 10 lines · Srish Srinivasan

plpks_gen_wrapping_key() returns a negative errno when default wrapping key generation fails. However, trusted_pkwm_init() returns -EINVAL for all such failures, discarding the original error.

Propagate the error returned by plpks_gen_wrapping_key() unchanged.

Fixes c99fcb0d735b ("keys/trusted_keys: establish PKWM as a trusted source")

Nit: I'd put the SOB as last.

Quoted 20 lines
---
 security/keys/trusted-keys/trusted_pkwm.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
security/keys/trusted-keys/trusted_pkwm.c +1 -1
index bf42c6679245..422f752598e1 100644
@@ -170,7 +170,7 @@ static int trusted_pkwm_init(void)
 	ret = plpks_gen_wrapping_key();
 	if (ret) {
 		pr_err("Failed to generate default wrapping key\n");
-		return -EINVAL;
+		return ret;
 	}
 
 	return register_key_type(&key_type_trusted);
Signature · 1 line
2.52.0

Br, Jarkko

author maintainer
#4 lore raw

Re: [PATCH v6 1/9] keys/trusted_keys: propagate wrapping key generation errors

Hi Jarkko,
Thanks for taking a look.

Quoted 5 lines · Jarkko Sakkinen
Quoted 4 lines · Srish Srinivasan

plpks_gen_wrapping_key() returns a negative errno when default wrapping key generation fails. However, trusted_pkwm_init() returns -EINVAL for all such failures, discarding the original error.

Quoted 7 lines
Quoted 6 lines

Propagate the error returned by plpks_gen_wrapping_key() unchanged.

Fixes c99fcb0d735b ("keys/trusted_keys: establish PKWM as a trusted source")

Nit: I'd put the SOB as last.

Sure, I will make this change.

Quoted 17 lines
Quoted 17 lines
---
  security/keys/trusted-keys/trusted_pkwm.c | 2 +-
  1 file changed, 1 insertion(+), 1 deletion(-)
security/keys/trusted-keys/trusted_pkwm.c +1 -1
index bf42c6679245..422f752598e1 100644
@@ -170,7 +170,7 @@ static int trusted_pkwm_init(void)
  	ret = plpks_gen_wrapping_key();
  	if (ret) {
  		pr_err("Failed to generate default wrapping key\n");
-		return -EINVAL;
+		return ret;
  	}
 
Quoted 5 lines
Quoted 4 lines
security/keys/trusted-keys/trusted_pkwm.c +0 -0
  	return register_key_type(&key_type_trusted);
Signature · 1 line
2.52.0

Thank you for the Rb.

Quoted 2 lines

Br, Jarkko

Thanks,
Srish.

author maintainer
#5 lore raw

PATCH v6 2/9 keys/trusted_keys: fix PKWM output buffer cleanup

In reply to Srish Srinivasan

The PKWM seal and unseal paths unconditionally free their output buffers, but the PLPKS helpers can return without initializing those pointers.

Initialize the output buffer pointers to NULL to make cleanup safe on early failure paths.

Fixes c99fcb0d735b ("keys/trusted_keys: establish PKWM as a trusted source")
---
 security/keys/trusted-keys/trusted_pkwm.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
security/keys/trusted-keys/trusted_pkwm.c +2 -2
index 422f752598e1..97ffec0ce1b7 100644
@@ -82,7 +82,7 @@ static int trusted_pkwm_seal(struct trusted_key_payload *p, char *datablob)
 {
 	struct trusted_key_options *options = NULL;
 	struct trusted_pkwm_options *pkwm = NULL;
-	u8 *input_buf, *output_buf;
+	u8 *input_buf, *output_buf = NULL;
 	u32 output_len, input_len;
 	int rc;
 
@@ -129,7 +129,7 @@ static int trusted_pkwm_seal(struct trusted_key_payload *p, char *datablob)
 
 static int trusted_pkwm_unseal(struct trusted_key_payload *p, char *datablob)
 {
-	u8 *input_buf, *output_buf;
+	u8 *input_buf, *output_buf = NULL;
 	u32 input_len, output_len;
 	int rc;
 
Signature · 1 line
2.52.0
#6 lore raw

Re: [PATCH v6 2/9] keys/trusted_keys: fix PKWM output buffer cleanup

Sashiko has reviewed this patch and found no issues.
Quoted 9 lines

The PKWM seal and unseal paths unconditionally free their output buffers, but the PLPKS helpers can return without initializing those pointers.

Initialize the output buffer pointers to NULL to make cleanup safe on early failure paths.

Fixes c99fcb0d735b ("keys/trusted_keys: establish PKWM as a trusted source")

Sashiko has reviewed this patch and found no issues.

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=2
maintainer Reviewed-by on v6 2/9
#7 lore raw

Re: [PATCH v6 2/9] keys/trusted_keys: fix PKWM output buffer cleanup

In reply to Srish Srinivasan
Quoted 38 lines · Srish Srinivasan

The PKWM seal and unseal paths unconditionally free their output buffers, but the PLPKS helpers can return without initializing those pointers.

Initialize the output buffer pointers to NULL to make cleanup safe on early failure paths.

Fixes c99fcb0d735b ("keys/trusted_keys: establish PKWM as a trusted source")
---
 security/keys/trusted-keys/trusted_pkwm.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
security/keys/trusted-keys/trusted_pkwm.c +2 -2
index 422f752598e1..97ffec0ce1b7 100644
@@ -82,7 +82,7 @@ static int trusted_pkwm_seal(struct trusted_key_payload *p, char *datablob)
 {
 	struct trusted_key_options *options = NULL;
 	struct trusted_pkwm_options *pkwm = NULL;
-	u8 *input_buf, *output_buf;
+	u8 *input_buf, *output_buf = NULL;
 	u32 output_len, input_len;
 	int rc;
 
@@ -129,7 +129,7 @@ static int trusted_pkwm_seal(struct trusted_key_payload *p, char *datablob)
 
 static int trusted_pkwm_unseal(struct trusted_key_payload *p, char *datablob)
 {
-	u8 *input_buf, *output_buf;
+	u8 *input_buf, *output_buf = NULL;
 	u32 input_len, output_len;
 	int rc;
 
Signature · 1 line
2.52.0

Br, Jarkko

author maintainer
#8 lore raw

Re: [PATCH v6 2/9] keys/trusted_keys: fix PKWM output buffer cleanup

Quoted 36 lines · Jarkko Sakkinen
Quoted 35 lines · Srish Srinivasan

The PKWM seal and unseal paths unconditionally free their output buffers, but the PLPKS helpers can return without initializing those pointers.

Initialize the output buffer pointers to NULL to make cleanup safe on early failure paths.

Fixes c99fcb0d735b ("keys/trusted_keys: establish PKWM as a trusted source")
---
  security/keys/trusted-keys/trusted_pkwm.c | 4 ++--
  1 file changed, 2 insertions(+), 2 deletions(-)
security/keys/trusted-keys/trusted_pkwm.c +2 -2
index 422f752598e1..97ffec0ce1b7 100644
@@ -82,7 +82,7 @@ static int trusted_pkwm_seal(struct trusted_key_payload *p, char *datablob)
  {
  	struct trusted_key_options *options = NULL;
  	struct trusted_pkwm_options *pkwm = NULL;
-	u8 *input_buf, *output_buf;
+	u8 *input_buf, *output_buf = NULL;
  	u32 output_len, input_len;
  	int rc;
 
@@ -129,7 +129,7 @@ static int trusted_pkwm_seal(struct trusted_key_payload *p, char *datablob)
 
  static int trusted_pkwm_unseal(struct trusted_key_payload *p, char *datablob)
  {
-	u8 *input_buf, *output_buf;
+	u8 *input_buf, *output_buf = NULL;
  	u32 input_len, output_len;
  	int rc;
 
Quoted 4 lines
Quoted 3 lines
Signature · 1 line
2.52.0

Thank you for the Rb, Jarkko.

Quoted 2 lines

Br, Jarkko

Thanks,
Srish.

author maintainer
#9 lore raw

PATCH v6 3/9 keys/trusted_keys: clear sensitive PKWM buffers

In reply to Srish Srinivasan

The input buffer to PKWM seal and the output buffer from PKWM unseal contain plaintext trusted-key payload data. Freeing these buffers with kfree(), without wiping the contents, can leave sensitive key material in memory.

Use kfree_sensitive() to wipe the sensitive key material when freeing these buffers.

Fixes c99fcb0d735b ("keys/trusted_keys: establish PKWM as a trusted source")
---
 security/keys/trusted-keys/trusted_pkwm.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
security/keys/trusted-keys/trusted_pkwm.c +2 -2
index 97ffec0ce1b7..be69d2b6d46b 100644
@@ -118,7 +118,7 @@ static int trusted_pkwm_seal(struct trusted_key_payload *p, char *datablob)
 		pr_err("Wrapping of payload key failed: %d\n", rc);
 	}
 
-	kfree(input_buf);
+	kfree_sensitive(input_buf);
 	kfree(output_buf);
 
 out:
@@ -153,7 +153,7 @@ static int trusted_pkwm_unseal(struct trusted_key_payload *p, char *datablob)
 	}
 
 	kfree(input_buf);
-	kfree(output_buf);
+	kfree_sensitive(output_buf);
 
 	return rc;
 }
Signature · 1 line
2.52.0
#10 lore raw

Re: [PATCH v6 3/9] keys/trusted_keys: clear sensitive PKWM buffers

Sashiko has reviewed this patch and found no issues.
Quoted 11 lines

The input buffer to PKWM seal and the output buffer from PKWM unseal contain plaintext trusted-key payload data. Freeing these buffers with kfree(), without wiping the contents, can leave sensitive key material in memory.

Use kfree_sensitive() to wipe the sensitive key material when freeing these buffers.

Fixes c99fcb0d735b ("keys/trusted_keys: establish PKWM as a trusted source")

Sashiko has reviewed this patch and found no issues.

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=3
author maintainer
#11 lore raw

PATCH v6 4/9 pseries/plpks: fix double conversion of write and remove flush errors

In reply to Srish Srinivasan

plpks_confirm_object_flushed() returns zero or a negative Linux errno. However, plpks_write_var() and plpks_remove_var() pass this return value through pseries_status_to_err(), which treats it as a raw hypervisor status and converts it again.

Convert only errors returned by the write and remove HCALLs, and propagate errors from plpks_confirm_object_flushed() unchanged.

Fixes 2454a7af0f2a ("powerpc/pseries: define driver for Platform KeyStore")
---
 arch/powerpc/platforms/pseries/plpks.c | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)
arch/powerpc/platforms/pseries/plpks.c +6 -2
index 240cd5632446..110251914b51 100644
@@ -714,6 +714,7 @@ int plpks_signed_update_var(struct plpks_var *var, u64 flags)
  * -ENOMEM	if unable to store the requested object in the space available
  * -EBUSY	if unable to handle the request
  * -EEXIST	if the object label already exists
+ * -ETIMEDOUT	if the object was not flushed to storage in time
  *
  * Returns: On success 0 is returned, a negative errno if not.
  */
@@ -750,8 +751,9 @@ int plpks_write_var(struct plpks_var var)
 
 	if (!rc)
 		rc = plpks_confirm_object_flushed(label, auth);
+	else
+		rc = pseries_status_to_err(rc);
 
-	rc = pseries_status_to_err(rc);
 	kfree(label);
 out:
 	kfree_sensitive(auth);
@@ -780,6 +782,7 @@ int plpks_write_var(struct plpks_var var)
  * -EPERM	if access is denied
  * -ENOENT	if the requested object was not found
  * -EBUSY	if unable to handle the request
+ * -ETIMEDOUT	if the object was not flushed to storage in time
  *
  * Returns: On success 0 is returned, a negative errno if not.
  */
@@ -808,8 +811,9 @@ int plpks_remove_var(char *component, u8 varos, struct plpks_var_name vname)
 
 	if (!rc)
 		rc = plpks_confirm_object_flushed(label, auth);
+	else
+		rc = pseries_status_to_err(rc);
 
-	rc = pseries_status_to_err(rc);
 	kfree(label);
 out:
 	kfree_sensitive(auth);
Signature · 1 line
2.52.0
#12 lore raw

Re: [PATCH v6 4/9] pseries/plpks: fix double conversion of write and remove flush errors

Sashiko has reviewed this patch and found no issues.
Quoted 11 lines

plpks_confirm_object_flushed() returns zero or a negative Linux errno. However, plpks_write_var() and plpks_remove_var() pass this return value through pseries_status_to_err(), which treats it as a raw hypervisor status and converts it again.

Convert only errors returned by the write and remove HCALLs, and propagate errors from plpks_confirm_object_flushed() unchanged.

Fixes 2454a7af0f2a ("powerpc/pseries: define driver for Platform KeyStore")

Sashiko has reviewed this patch and found no issues.

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=4
author maintainer
#13 lore raw

PATCH v6 5/9 pseries/plpks: fix double conversion of wrapping key flush errors

In reply to Srish Srinivasan

plpks_confirm_object_flushed() returns zero or a negative Linux errno. However, plpks_gen_wrapping_key() passes this return value through pseries_status_to_err(), which treats it as a raw hypervisor status and converts it again.

Convert only errors returned by H_PKS_GEN_KEY, and propagate errors from plpks_confirm_object_flushed() unchanged.

Fixes 133aa79e211d ("pseries/plpks: add HCALLs for PowerVM Key Wrapping Module")
Cc [email protected] # 7.0: a2df535d9dd9: pseries/plpks: rename the default wrapping key macro
---
 arch/powerpc/platforms/pseries/plpks.c | 21 +++++++++++----------
 1 file changed, 11 insertions(+), 10 deletions(-)
arch/powerpc/platforms/pseries/plpks.c +11 -10
index 110251914b51..376c50d459fc 100644
@@ -932,6 +932,7 @@ EXPORT_SYMBOL_GPL(plpks_wrapping_is_supported);
  * -ENOMEM	if there is inadequate memory to perform this operation
  * -EBUSY	if unable to handle the request
  * -EEXIST	if the object label already exists
+ * -ETIMEDOUT	if the object was not flushed to storage in time
  *
  * Returns: On success 0 is returned, a negative errno if not.
  */
@@ -940,7 +941,7 @@ int plpks_gen_wrapping_key(void)
 	unsigned long retbuf[PLPAR_HCALL_BUFSIZE] = { 0 };
 	struct plpks_auth *auth;
 	struct label *label;
-	int rc = 0, pseries_status = 0;
+	int rc, pseries_status;
 	struct plpks_var var = {
 		.name = PLPKS_DEFAULT_WRAPKEY_LABEL,
 		.namelen = sizeof(PLPKS_DEFAULT_WRAPKEY_LABEL) - 1,
@@ -964,17 +965,17 @@ int plpks_gen_wrapping_key(void)
 			 label->size, var.policy,
 			 NULL, PLPKS_WRAPPING_KEY_LENGTH);
 
-	if (!rc)
+	if (!rc) {
 		rc = plpks_confirm_object_flushed(label, auth);
-
-	pseries_status = rc;
-	rc = pseries_status_to_err(rc);
-
-	if (rc && rc != -EEXIST) {
-		pr_err("H_PKS_GEN_KEY failed. pseries_status=%d, rc=%d",
-		       pseries_status, rc);
 	} else {
-		rc = 0;
+		pseries_status = rc;
+		rc = pseries_status_to_err(rc);
+
+		if (rc != -EEXIST)
+			pr_err("H_PKS_GEN_KEY failed. pseries_status=%d, rc=%d",
+			       pseries_status, rc);
+		else
+			rc = 0;
 	}
 
 	kfree(label);
Signature · 1 line
2.52.0
#14 lore raw

Re: [PATCH v6 5/9] pseries/plpks: fix double conversion of wrapping key flush errors

Sashiko has reviewed this patch and found no issues.
Quoted 5 lines

plpks_confirm_object_flushed() returns zero or a negative Linux errno. However, plpks_gen_wrapping_key() passes this return value through pseries_status_to_err(), which treats it as a raw hypervisor status and converts it again.

Quoted 7 lines

Convert only errors returned by H_PKS_GEN_KEY, and propagate errors from plpks_confirm_object_flushed() unchanged.

Fixes 133aa79e211d ("pseries/plpks: add HCALLs for PowerVM Key Wrapping Module")
Cc [email protected] # 7.0: a2df535d9dd9: pseries/plpks: rename the default wrapping key macro

Sashiko has reviewed this patch and found no issues.

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=5
author maintainer
#15 lore raw

PATCH v6 6/9 pseries/plpks: fix length types for wrapping operations

In reply to Srish Srinivasan

The object wrapping and unwrapping HCALLs use 64-bit length values, but plpks_wrap_object() and plpks_unwrap_object() currently use u32 for the input and output length parameters.

Use u64 for the length parameters to match the underlying HCALL interfaces, and update the PKWM consumer accordingly. Validate input lengths against the HCALL-defined limits before calculating output buffer sizes.

Fixes 133aa79e211d ("pseries/plpks: add HCALLs for PowerVM Key Wrapping Module")
Fixes c99fcb0d735b ("keys/trusted_keys: establish PKWM as a trusted source")
---
 arch/powerpc/include/asm/plpks.h          |  8 ++++----
 arch/powerpc/platforms/pseries/plpks.c    | 23 +++++++++++++++++------
 security/keys/trusted-keys/trusted_pkwm.c |  4 ++--
 3 files changed, 23 insertions(+), 12 deletions(-)
arch/powerpc/include/asm/plpks.h +4 -4
index e87f90e40d4e..8b2ffb27db5a 100644
@@ -118,11 +118,11 @@ bool plpks_wrapping_is_supported(void);
 
 int plpks_gen_wrapping_key(void);
 
-int plpks_wrap_object(u8 **input_buf, u32 input_len, u16 wrap_flags,
-		      u8 **output_buf, u32 *output_len);
+int plpks_wrap_object(u8 **input_buf, u64 input_len, u16 wrap_flags,
+		      u8 **output_buf, u64 *output_len);
 
-int plpks_unwrap_object(u8 **input_buf, u32 input_len,
-			u8 **output_buf, u32 *output_len);
+int plpks_unwrap_object(u8 **input_buf, u64 input_len,
+			u8 **output_buf, u64 *output_len);
 #else // CONFIG_PSERIES_PLPKS
 static inline bool plpks_is_available(void) { return false; }
 static inline u16 plpks_get_passwordlen(void) { BUILD_BUG(); }
arch/powerpc/platforms/pseries/plpks.c +17 -6
index 376c50d459fc..d5adc563f71f 100644
@@ -22,6 +22,9 @@
  * than the input buffer's length during the WRAP H_CALL
  */
 #define PLPKS_WRAPPING_BUF_DIFF	1024
+#define PLPKS_MAX_WRAPPING_INPUT_SIZE	(32 * 1024)
+#define PLPKS_MAX_WRAPPED_OBJECT_SIZE	\
+	(PLPKS_MAX_WRAPPING_INPUT_SIZE + PLPKS_WRAPPING_BUF_DIFF)
 
 #define PLPKS_WRAP_INTERFACE_BIT	3
 #define PLPKS_WRAPPING_KEY_LENGTH	32
@@ -989,7 +992,7 @@ EXPORT_SYMBOL_GPL(plpks_gen_wrapping_key);
  * plpks_wrap_object() - Wrap an object using the default wrapping key stored in
  * the PLPKS.
  * @input_buf: buffer containing the data to be wrapped
- * @input_len: length of the input buffer
+ * @input_len: length of the input buffer (non-zero, at most 32KB)
  * @wrap_flags: object wrapping flags
  * @output_buf: buffer to store the wrapped data
  * @output_len: length of the output buffer
@@ -1026,8 +1029,8 @@ EXPORT_SYMBOL_GPL(plpks_gen_wrapping_key);
  *
  * Returns: On success 0 is returned, a negative errno if not.
  */
-int plpks_wrap_object(u8 **input_buf, u32 input_len, u16 wrap_flags,
-		      u8 **output_buf, u32 *output_len)
+int plpks_wrap_object(u8 **input_buf, u64 input_len, u16 wrap_flags,
+		      u8 **output_buf, u64 *output_len)
 {
 	unsigned long retbuf[PLPAR_HCALL9_BUFSIZE] = { 0 };
 	struct plpks_auth *auth;
@@ -1044,6 +1047,9 @@ int plpks_wrap_object(u8 **input_buf, u32 input_len, u16 wrap_flags,
 		.component = PLPKS_WRAPKEY_COMPONENT
 	};
 
+	if (!input_len || input_len > PLPKS_MAX_WRAPPING_INPUT_SIZE)
+		return -EINVAL;
+
 	auth = construct_auth(PLPKS_OS_OWNER);
 	if (IS_ERR(auth))
 		return PTR_ERR(auth);
@@ -1114,7 +1120,7 @@ EXPORT_SYMBOL_GPL(plpks_wrap_object);
  * plpks_unwrap_object() - Unwrap an object using the default wrapping key
  * stored in the PLPKS.
  * @input_buf: buffer containing the data to be unwrapped
- * @input_len: length of the input buffer
+ * @input_len: length of the input buffer (more than 1KB, at most 33KB)
  * @output_buf: buffer to store the unwrapped data
  * @output_len: length of the output buffer
  *
@@ -1145,8 +1151,8 @@ EXPORT_SYMBOL_GPL(plpks_wrap_object);
  *
  * Returns: On success 0 is returned, a negative errno if not.
  */
-int plpks_unwrap_object(u8 **input_buf, u32 input_len, u8 **output_buf,
-			u32 *output_len)
+int plpks_unwrap_object(u8 **input_buf, u64 input_len, u8 **output_buf,
+			u64 *output_len)
 {
 	unsigned long retbuf[PLPAR_HCALL9_BUFSIZE] = { 0 };
 	struct plpks_auth *auth;
@@ -1154,6 +1160,11 @@ int plpks_unwrap_object(u8 **input_buf, u32 input_len, u8 **output_buf,
 	u64 objwrapflags = 0;
 	int rc = 0, pseries_status = 0;
 
+	/* outlen = inlen - 1KB must be non-zero */
+	if (input_len <= PLPKS_WRAPPING_BUF_DIFF ||
+	    input_len > PLPKS_MAX_WRAPPED_OBJECT_SIZE)
+		return -EINVAL;
+
 	auth = construct_auth(PLPKS_OS_OWNER);
 	if (IS_ERR(auth))
 		return PTR_ERR(auth);
security/keys/trusted-keys/trusted_pkwm.c +2 -2
index be69d2b6d46b..b5c3d17be475 100644
@@ -83,7 +83,7 @@ static int trusted_pkwm_seal(struct trusted_key_payload *p, char *datablob)
 	struct trusted_key_options *options = NULL;
 	struct trusted_pkwm_options *pkwm = NULL;
 	u8 *input_buf, *output_buf = NULL;
-	u32 output_len, input_len;
+	u64 output_len, input_len;
 	int rc;
 
 	options = trusted_options_alloc();
@@ -130,7 +130,7 @@ static int trusted_pkwm_seal(struct trusted_key_payload *p, char *datablob)
 static int trusted_pkwm_unseal(struct trusted_key_payload *p, char *datablob)
 {
 	u8 *input_buf, *output_buf = NULL;
-	u32 input_len, output_len;
+	u64 input_len, output_len;
 	int rc;
 
 	input_len = p->blob_len;
Signature · 1 line
2.52.0
#16 lore raw

Re: [PATCH v6 6/9] pseries/plpks: fix length types for wrapping operations

Sashiko has reviewed this patch and found no issues.
Quoted 4 lines

The object wrapping and unwrapping HCALLs use 64-bit length values, but plpks_wrap_object() and plpks_unwrap_object() currently use u32 for the input and output length parameters.

Quoted 8 lines

Use u64 for the length parameters to match the underlying HCALL interfaces, and update the PKWM consumer accordingly. Validate input lengths against the HCALL-defined limits before calculating output buffer sizes.

Fixes 133aa79e211d ("pseries/plpks: add HCALLs for PowerVM Key Wrapping Module")
Fixes c99fcb0d735b ("keys/trusted_keys: establish PKWM as a trusted source")

Sashiko has reviewed this patch and found no issues.

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=6
author maintainer
#17 lore raw

PATCH v6 7/9 pseries/plpks: add HCALLs for PKWM wrapping key life cycle management

In reply to Srish Srinivasan

The PKWM trusted source uses a single wrapping key per LPAR, created only once during trust source initialization. All the wrap and unwrap requests are bound to this default wrapping key.

Implement H_PKS_REVOKE_OBJECT, H_PKS_UNREVOKE_OBJECT, and H_PKS_GET_OBJECTLABELS HCALLs to enable support for revoking and unrevoking PKWM wrapping keys, and for retrieving wrapping key labels. The label retrieval operation (H_PKS_GET_OBJECTLABELS) applies to all wrapping keys including the default one, while the life cycle operations (H_PKS_REVOKE_OBJECT/H_PKS_UNREVOKE_OBJECT) apply only to user-created wrapping keys.

---
 Documentation/arch/powerpc/papr_hcalls.rst |  33 ++
 arch/powerpc/include/asm/hvcall.h          |   5 +-
 arch/powerpc/include/asm/plpks.h           |  15 +
 arch/powerpc/platforms/pseries/plpks.c     | 489 ++++++++++++++++++++-
 4 files changed, 538 insertions(+), 4 deletions(-)
Documentation/arch/powerpc/papr_hcalls.rst +33 -0
index 44c9c8b32ae3..776ceb8cc54e 100644
@@ -343,6 +343,39 @@ is returned to the caller.
 H_PKS_UNWRAP_OBJECT is used to unwrap an object that was previously wrapped with
 H_PKS_WRAP_OBJECT.
 
+**H_PKS_REVOKE_OBJECT**
+
+| Input: authorization, objectlabel, objectlabellen, flags
+| Out: *object policy*
+| Return Value: *H_Success, H_Function, H_State, H_Parameter, H_P2, H_P3, H_P4,
+                H_Authority, H_Not_Found, H_Busy, H_Aborted*
+
+H_PKS_REVOKE_OBJECT is used to revoke an object in Platform Keystore.
+
+
+**H_PKS_UNREVOKE_OBJECT**
+
+| Input: authorization, objectlabel, objectlabellen, flags
+| Out: *object policy*
+| Return Value: *H_Success, H_Function, H_State, H_Parameter, H_P2, H_P3, H_P4,
+                H_Authority, H_Not_Found, H_Busy, H_Aborted*
+
+H_PKS_UNREVOKE_OBJECT is used to unrevoke an object that was previously revoked
+with H_PKS_REVOKE_OBJECT in Platform Keystore.
+
+
+**H_PKS_GET_OBJECTLABELS**
+
+| Input: authorization, continueToken, out, outlen
+| Out: *number of object labels in the returned list, continue-token, object
+        label list*
+| Return Value: *H_Success, H_Function, H_State, H_Parameter, H_P2, H_P3, H_P4,
+                H_Authority, H_Busy, H_Aborted, H_Continue*
+
+H_PKS_GET_OBJECTLABELS is used to retrieve a list of object labels owned by the
+specified consumer.
+
+
 References
 ==========
 .. [1] "Power Architecture Platform Reference"
arch/powerpc/include/asm/hvcall.h +4 -1
index dff90a7d7f70..4d4c2ce1dd87 100644
@@ -340,6 +340,7 @@
 #define H_PKS_GET_CONFIG	0x41C
 #define H_PKS_SET_PASSWORD	0x420
 #define H_PKS_GEN_PASSWORD	0x424
+#define H_PKS_GET_OBJECTLABELS	0x428
 #define H_PKS_WRITE_OBJECT	0x42C
 #define H_PKS_GEN_KEY		0x430
 #define H_PKS_READ_OBJECT	0x434
@@ -362,7 +363,9 @@
 #define H_GUEST_DELETE		0x488
 #define H_PKS_WRAP_OBJECT	0x490
 #define H_PKS_UNWRAP_OBJECT	0x494
-#define MAX_HCALL_OPCODE	H_PKS_UNWRAP_OBJECT
+#define H_PKS_REVOKE_OBJECT	0x4AC
+#define H_PKS_UNREVOKE_OBJECT	0x4B0
+#define MAX_HCALL_OPCODE	H_PKS_UNREVOKE_OBJECT
 
 /* Scope args for H_SCM_UNBIND_ALL */
 #define H_UNBIND_SCOPE_ALL (0x1)
arch/powerpc/include/asm/plpks.h +15 -0
index 8b2ffb27db5a..707c9ce14365 100644
@@ -25,6 +25,7 @@
 #define PLPKS_SIGNEDUPDATE	PPC_BIT32(7) // Object can only be modified by signed updates
 #define PLPKS_WRAPPINGKEY	PPC_BIT32(8) // Object contains a wrapping key
 #define PLPKS_HVPROVISIONED	PPC_BIT32(28) // Hypervisor has provisioned this object
+#define PLPKS_REVOKED		PPC_BIT32(30) // Object is revoked
 
 // Signature algorithm flags from signed_update_algorithms
 #define PLPKS_ALG_RSA2048	PPC_BIT(0)
@@ -123,6 +124,20 @@ int plpks_wrap_object(u8 **input_buf, u64 input_len, u16 wrap_flags,
 
 int plpks_unwrap_object(u8 **input_buf, u64 input_len,
 			u8 **output_buf, u64 *output_len);
+
+int plpks_revoke_wrapping_key(struct plpks_var *var);
+
+int plpks_unrevoke_wrapping_key(struct plpks_var *var);
+
+int plpks_del_wrapping_key(struct plpks_var *var);
+
+int plpks_is_wrapping_key_revoked(const struct plpks_var *var);
+
+int plpks_get_object_labels(u8 **output_buf, u64 *output_len,
+			    char *comp_prefix);
+
+bool plpks_revoke_is_supported(void);
+
 #else // CONFIG_PSERIES_PLPKS
 static inline bool plpks_is_available(void) { return false; }
 static inline u16 plpks_get_passwordlen(void) { BUILD_BUG(); }
arch/powerpc/platforms/pseries/plpks.c +486 -3

539 lines not on this page · raw message

Signature · 1 line
2.52.0
#18 lore raw

Re: [PATCH v6 7/9] pseries/plpks: add HCALLs for PKWM wrapping key life cycle management

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [Medium] pseries/plpks: output_buf leak and -EAGAIN return in plpks_get_object_labels() --

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] pseries/plpks: output_buf leak and -EAGAIN return in plpks_get_object_labels() --

commit 41ef08b282e00765eba284dad461f1985057d371
Author: Srish Srinivasan <[email protected]>

pseries/plpks: add HCALLs for PKWM wrapping key life cycle management

This commit adds support for the H_PKS_REVOKE_OBJECT, H_PKS_UNREVOKE_OBJECT, and H_PKS_GET_OBJECTLABELS hypercalls to manage the life cycle of PKWM wrapping keys and retrieve wrapping key labels in the Platform Keystore.

Quoted 4 lines
arch/powerpc/platforms/pseries/plpks.c +0 -0
index d5adc563f71f0..6e10bd6a9a481 100644

[ ... ]

Quoted 1 line
arch/powerpc/platforms/pseries/plpks.c +0 -0
@@ -1205,6 +1237,457 @@ int plpks_unwrap_object(u8 **input_buf, u64 input_len, u8 **output_buf,

[ ... ]

Quoted 133 lines
arch/powerpc/platforms/pseries/plpks.c +133 -0
+int plpks_get_object_labels(u8 **output_buf, u64 *output_len,
+			    char *comp_prefix)
+{
+	unsigned long retbuf[PLPAR_HCALL_BUFSIZE] = { 0 };
+	u8 *labels_buf = NULL;
+	u8 *tmp_buf = NULL;
+	struct plpks_auth *auth;
+	struct label_attr *metadata = NULL;
+	u8 prefix[PLPKS_OBJLABEL_PREFIX_LEN];
+	u16 label_len;
+	u64 labels_count;
+	u64 continuetoken = 0, output_buf_len = 0;
+	int rc, pseries_status;
+	size_t labels_buf_offset = 0, output_buf_offset = 0;
+	size_t obj_label_entry_size, i;
+
+	if (!output_buf || !output_len || !comp_prefix)
+		return -EINVAL;
+
+	*output_buf = NULL;
+	*output_len = 0;
+
+	memset(prefix, 0, sizeof(prefix));
+	memcpy(prefix, comp_prefix, strnlen(comp_prefix, sizeof(prefix)));
+
+	auth = construct_auth(PLPKS_OS_OWNER);
+	if (IS_ERR(auth))
+		return PTR_ERR(auth);
+
+	do {
+		labels_buf =
+			kzalloc(PLPKS_OBJLABEL_BUF_SIZE, GFP_KERNEL);
+
+		if (!labels_buf) {
+			rc = -ENOMEM;
+			goto out_free_output_buf;
+		}
+
+		rc = plpar_hcall(H_PKS_GET_OBJECTLABELS, retbuf,
+				 virt_to_phys(auth), continuetoken,
+				 virt_to_phys(labels_buf),
+				 PLPKS_OBJLABEL_BUF_SIZE);
+
+		pseries_status = rc;
+		rc = pseries_status_to_err(rc);
+
+		if (rc && rc != -EAGAIN) {
+			pr_err("H_PKS_GET_OBJECTLABELS failed. pseries_status=%d rc=%d\n",
+			       pseries_status, rc);
+			goto out_free_labels_buf;
+		} else {
+			/*
+			 * Setting an incorrect countinuetoken upon
+			 * receiving H_CONTINUE would result in H_P2. Since
+			 * the continuetoken is being set to the expected
+			 * value from the previous call, H_P2 must not be
+			 * returned.
+			 */
+			continuetoken = retbuf[1];
+
+			labels_count = retbuf[0];
+			if (!labels_count) {
+				kfree(labels_buf);
+				labels_buf = NULL;
+				goto out;
+			}
+
+			/*
+			 * Filter out object labels that don't have the provided
+			 * component prefix.
+			 */
+
+			output_buf_len += PLPKS_OBJLABEL_BUF_SIZE;
+
+			tmp_buf = krealloc(*output_buf, output_buf_len,
+					   GFP_KERNEL);
+
+			if (!tmp_buf) {
+				rc = -ENOMEM;
+				goto out_free_labels_buf;
+			}
+
+			*output_buf = tmp_buf;
+
+			for (i = 0; i < labels_count; ++i) {
+				if (labels_buf_offset +
+				    PLPKS_OBJLABEL_LEN_FIELD_SIZE >
+				    PLPKS_OBJLABEL_BUF_SIZE) {
+					rc = -EIO;
+					goto out_free_labels_buf;
+				}
+
+				label_len =
+					get_unaligned_be16(labels_buf +
+							   labels_buf_offset);
+
+				obj_label_entry_size =
+					PLPKS_OBJLABEL_LEN_FIELD_SIZE +
+					label_len;
+
+				if (label_len < sizeof(struct label_attr) ||
+				    labels_buf_offset + obj_label_entry_size >
+				    PLPKS_OBJLABEL_BUF_SIZE) {
+					rc = -EIO;
+					goto out_free_labels_buf;
+				}
+
+				metadata =
+					(struct label_attr *)(labels_buf +
+					labels_buf_offset +
+					PLPKS_OBJLABEL_LEN_FIELD_SIZE);
+
+				if (!memcmp(metadata->prefix, prefix,
+					    PLPKS_OBJLABEL_PREFIX_LEN)) {
+					memcpy(*output_buf + output_buf_offset,
+					       labels_buf + labels_buf_offset,
+					       obj_label_entry_size);
+
+					output_buf_offset +=
+						obj_label_entry_size;
+					(*output_len) += 1;
+				}
+				labels_buf_offset += obj_label_entry_size;
+			}
+			kfree(labels_buf);
+			labels_buf = NULL;
+			labels_buf_offset = 0;
+		}
+	} while (rc == -EAGAIN);
+
+	goto out;
+
+out_free_labels_buf:

arch/powerpc/platforms/pseries/plpks.c int plpks_unwrap_object(u8 **input_buf, u64 input_len, u8 **output_buf, · line 1688

Quoted 10 lines
arch/powerpc/platforms/pseries/plpks.c +10 -0
+	kfree(labels_buf);
+	labels_buf = NULL;
+out_free_output_buf:
+	kfree(*output_buf);
+	*output_buf = NULL;
+	*output_len = 0;
+out:
+	kfree_sensitive(auth);
+	return rc;
+}

[Severity: Medium]
Can plpks_get_object_labels() leak *output_buf or return -EAGAIN to the caller?

First, inside the do-while loop, when labels_count > 0, *output_buf is allocated or grown via krealloc() before checking whether any of the returned labels match comp_prefix. If PLPKS contains objects for other components so labels_count > 0, but none of those labels match comp_prefix, the loop completes with rc == 0 and *output_len == 0 and jumps to out without freeing *output_buf. Unlike the case where PLPKS has 0 total objects on the first call (which returns 0 with *output_len == 0 and *output_buf == NULL), this returns 0 with *output_len == 0 while *output_buf points to an allocated buffer of uninitialized heap memory. Callers checking *output_len == 0 will leak *output_buf, while callers checking *output_buf != NULL will read uninitialized memory.

Second, if !labels_count (retbuf[0] == 0) is encountered when H_PKS_GET_OBJECTLABELS returns H_CONTINUE (converted by pseries_status_to_err() to rc = -EAGAIN) or on a subsequent continuation iteration after *output_buf was already allocated, jumping directly to out bypasses the loop condition and out_free_output_buf. Could that return the internal continuation code -EAGAIN to the caller and leak any *output_buf allocated in prior iterations?

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=7
author maintainer
#19 lore raw

Re: [PATCH v6 7/9] pseries/plpks: add HCALLs for PKWM wrapping key life cycle management

Hi

Quoted 3 lines · On 11/5/26 8:26 PM, [email protected] wrote:

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] pseries/plpks: output_buf leak and -EAGAIN return in plpks_get_object_labels() --

Yes, these are valid findings and I will fix these in my next version.

Thanks,
Srish.

Quoted 184 lines

commit 41ef08b282e00765eba284dad461f1985057d371
Author: Srish Srinivasan <[email protected]>

pseries/plpks: add HCALLs for PKWM wrapping key life cycle management

This commit adds support for the H_PKS_REVOKE_OBJECT, H_PKS_UNREVOKE_OBJECT, and H_PKS_GET_OBJECTLABELS hypercalls to manage the life cycle of PKWM wrapping keys and retrieve wrapping key labels in the Platform Keystore.

Quoted 4 lines
arch/powerpc/platforms/pseries/plpks.c +0 -0
index d5adc563f71f0..6e10bd6a9a481 100644

[ ... ]

Quoted 1 line
arch/powerpc/platforms/pseries/plpks.c +0 -0
@@ -1205,6 +1237,457 @@ int plpks_unwrap_object(u8 **input_buf, u64 input_len, u8 **output_buf,

[ ... ]

Quoted 133 lines
arch/powerpc/platforms/pseries/plpks.c +133 -0
+int plpks_get_object_labels(u8 **output_buf, u64 *output_len,
+			    char *comp_prefix)
+{
+	unsigned long retbuf[PLPAR_HCALL_BUFSIZE] = { 0 };
+	u8 *labels_buf = NULL;
+	u8 *tmp_buf = NULL;
+	struct plpks_auth *auth;
+	struct label_attr *metadata = NULL;
+	u8 prefix[PLPKS_OBJLABEL_PREFIX_LEN];
+	u16 label_len;
+	u64 labels_count;
+	u64 continuetoken = 0, output_buf_len = 0;
+	int rc, pseries_status;
+	size_t labels_buf_offset = 0, output_buf_offset = 0;
+	size_t obj_label_entry_size, i;
+
+	if (!output_buf || !output_len || !comp_prefix)
+		return -EINVAL;
+
+	*output_buf = NULL;
+	*output_len = 0;
+
+	memset(prefix, 0, sizeof(prefix));
+	memcpy(prefix, comp_prefix, strnlen(comp_prefix, sizeof(prefix)));
+
+	auth = construct_auth(PLPKS_OS_OWNER);
+	if (IS_ERR(auth))
+		return PTR_ERR(auth);
+
+	do {
+		labels_buf =
+			kzalloc(PLPKS_OBJLABEL_BUF_SIZE, GFP_KERNEL);
+
+		if (!labels_buf) {
+			rc = -ENOMEM;
+			goto out_free_output_buf;
+		}
+
+		rc = plpar_hcall(H_PKS_GET_OBJECTLABELS, retbuf,
+				 virt_to_phys(auth), continuetoken,
+				 virt_to_phys(labels_buf),
+				 PLPKS_OBJLABEL_BUF_SIZE);
+
+		pseries_status = rc;
+		rc = pseries_status_to_err(rc);
+
+		if (rc && rc != -EAGAIN) {
+			pr_err("H_PKS_GET_OBJECTLABELS failed. pseries_status=%d rc=%d\n",
+			       pseries_status, rc);
+			goto out_free_labels_buf;
+		} else {
+			/*
+			 * Setting an incorrect countinuetoken upon
+			 * receiving H_CONTINUE would result in H_P2. Since
+			 * the continuetoken is being set to the expected
+			 * value from the previous call, H_P2 must not be
+			 * returned.
+			 */
+			continuetoken = retbuf[1];
+
+			labels_count = retbuf[0];
+			if (!labels_count) {
+				kfree(labels_buf);
+				labels_buf = NULL;
+				goto out;
+			}
+
+			/*
+			 * Filter out object labels that don't have the provided
+			 * component prefix.
+			 */
+
+			output_buf_len += PLPKS_OBJLABEL_BUF_SIZE;
+
+			tmp_buf = krealloc(*output_buf, output_buf_len,
+					   GFP_KERNEL);
+
+			if (!tmp_buf) {
+				rc = -ENOMEM;
+				goto out_free_labels_buf;
+			}
+
+			*output_buf = tmp_buf;
+
+			for (i = 0; i < labels_count; ++i) {
+				if (labels_buf_offset +
+				    PLPKS_OBJLABEL_LEN_FIELD_SIZE >
+				    PLPKS_OBJLABEL_BUF_SIZE) {
+					rc = -EIO;
+					goto out_free_labels_buf;
+				}
+
+				label_len =
+					get_unaligned_be16(labels_buf +
+							   labels_buf_offset);
+
+				obj_label_entry_size =
+					PLPKS_OBJLABEL_LEN_FIELD_SIZE +
+					label_len;
+
+				if (label_len < sizeof(struct label_attr) ||
+				    labels_buf_offset + obj_label_entry_size >
+				    PLPKS_OBJLABEL_BUF_SIZE) {
+					rc = -EIO;
+					goto out_free_labels_buf;
+				}
+
+				metadata =
+					(struct label_attr *)(labels_buf +
+					labels_buf_offset +
+					PLPKS_OBJLABEL_LEN_FIELD_SIZE);
+
+				if (!memcmp(metadata->prefix, prefix,
+					    PLPKS_OBJLABEL_PREFIX_LEN)) {
+					memcpy(*output_buf + output_buf_offset,
+					       labels_buf + labels_buf_offset,
+					       obj_label_entry_size);
+
+					output_buf_offset +=
+						obj_label_entry_size;
+					(*output_len) += 1;
+				}
+				labels_buf_offset += obj_label_entry_size;
+			}
+			kfree(labels_buf);
+			labels_buf = NULL;
+			labels_buf_offset = 0;
+		}
+	} while (rc == -EAGAIN);
+
+	goto out;
+
+out_free_labels_buf:

arch/powerpc/platforms/pseries/plpks.c int plpks_unwrap_object(u8 **input_buf, u64 input_len, u8 **output_buf, · line 1688

Quoted 10 lines
arch/powerpc/platforms/pseries/plpks.c +10 -0
+	kfree(labels_buf);
+	labels_buf = NULL;
+out_free_output_buf:
+	kfree(*output_buf);
+	*output_buf = NULL;
+	*output_len = 0;
+out:
+	kfree_sensitive(auth);
+	return rc;
+}

[Severity: Medium]
Can plpks_get_object_labels() leak *output_buf or return -EAGAIN to the caller?

First, inside the do-while loop, when labels_count > 0, *output_buf is allocated or grown via krealloc() before checking whether any of the returned labels match comp_prefix. If PLPKS contains objects for other components so labels_count > 0, but none of those labels match comp_prefix, the loop completes with rc == 0 and *output_len == 0 and jumps to out without freeing *output_buf. Unlike the case where PLPKS has 0 total objects on the first call (which returns 0 with *output_len == 0 and *output_buf == NULL), this returns 0 with *output_len == 0 while *output_buf points to an allocated buffer of uninitialized heap memory. Callers checking *output_len == 0 will leak *output_buf, while callers checking *output_buf != NULL will read uninitialized memory.

Second, if !labels_count (retbuf[0] == 0) is encountered when H_PKS_GET_OBJECTLABELS returns H_CONTINUE (converted by pseries_status_to_err() to rc = -EAGAIN) or on a subsequent continuation iteration after *output_buf was already allocated, jumping directly to out bypasses the loop condition and out_free_output_buf. Could that return the internal continuation code -EAGAIN to the caller and leak any *output_buf allocated in prior iterations?

author maintainer
#20 lore raw

PATCH v6 8/9 keys/trusted_keys: enable PKWM wrapping key selection by label

In reply to Srish Srinivasan

The PKWM trusted source currently uses the default wrapping key for all seal and unseal operations. However, PKWM supports multiple wrapping keys, enabling better isolation between trusted keys.

Allow users to provide an active wrapping key of their choice created through sysfs when sealing trusted keys through a new wrapping_key=<label> option. If no label is specified, the default wrapping key is used for sealing.

---
 .../security/keys/trusted-encrypted.rst       |  4 +-
 arch/powerpc/include/asm/plpks.h              | 10 ++++-
 arch/powerpc/platforms/pseries/plpks.c        | 42 ++++++++-----------
 include/keys/trusted_pkwm.h                   |  3 ++
 security/keys/trusted-keys/trusted_pkwm.c     | 38 +++++++++++++++--
 5 files changed, 65 insertions(+), 32 deletions(-)
Documentation/security/keys/trusted-encrypted.rst +3 -1
index ddff7c7c2582..76fda28fddb1 100644
@@ -83,7 +83,7 @@ safe.
 
      (5) PKWM (PowerVM Key Wrapping Module: IBM PowerVM + Platform KeyStore)
 
-         Rooted to a unique, per-LPAR key, which is derived from a system-wide,
+         Rooted to unique, per-LPAR keys, which are derived from a system-wide,
          randomly generated LPAR root key. Both the per-LPAR keys and the LPAR
          root key are stored in hypervisor-owned secure memory at runtime,
          and the LPAR root key is additionally persisted in secure locations
@@ -366,6 +366,8 @@ Usage::
                        0x01: require secure boot to be in either audit or
                              enforced mode
                        0x02: require secure boot to be in enforced mode
+       wrapping_key= optional wrapping key label as a string. The default
+                     wrapping key is used when no label is provided.
 
 "keyctl print" returns an ASCII hex copy of the sealed key, which is in format
 specific to PKWM key-blob implementation.  The key length for new keys is
arch/powerpc/include/asm/plpks.h +8 -2
index 707c9ce14365..abf8a71cf3f2 100644
@@ -50,6 +50,12 @@
 #define PLPKS_MAX_TIMEOUT		(5 * USEC_PER_SEC)
 #define PLPKS_FLUSH_SLEEP		10000 // usec
 
+// Label for the PKWM default wrapping key
+#define PLPKS_DEFAULT_WRAPKEY_LABEL	"default-wrapping-key"
+
+// Component for a PKWM wrapping key
+#define PLPKS_WRAPKEY_COMPONENT	"PLPKSWR"
+
 struct plpks_var {
 	char *component;
 	u8 *name;
@@ -117,10 +123,10 @@ int plpks_config_create_softlink(struct kobject *from);
 
 bool plpks_wrapping_is_supported(void);
 
-int plpks_gen_wrapping_key(void);
+int plpks_gen_wrapping_key(struct plpks_var *var);
 
 int plpks_wrap_object(u8 **input_buf, u64 input_len, u16 wrap_flags,
-		      u8 **output_buf, u64 *output_len);
+		      u8 **output_buf, u64 *output_len, struct plpks_var *var);
 
 int plpks_unwrap_object(u8 **input_buf, u64 input_len,
 			u8 **output_buf, u64 *output_len);
arch/powerpc/platforms/pseries/plpks.c +17 -25

118 lines not on this page · raw message

include/keys/trusted_pkwm.h +3 -0
index 4035b9776394..53a5f780edd2 100644
@@ -6,10 +6,13 @@
 #include <linux/bitops.h>
 #include <linux/printk.h>
 
+#define WRAPPING_KEY_LABEL_LEN_MAX	239
+
 extern struct trusted_key_ops pkwm_trusted_key_ops;
 
 struct trusted_pkwm_options {
 	u16 wrap_flags;
+	unsigned char wrapping_key_label[WRAPPING_KEY_LABEL_LEN_MAX + 1];
 };
 
 static inline void dump_options(struct trusted_key_options *o)
security/keys/trusted-keys/trusted_pkwm.c +34 -4
index b5c3d17be475..c44b50d68d4c 100644
@@ -13,10 +13,12 @@
 enum {
 	Opt_err,
 	Opt_wrap_flags,
+	Opt_wrapping_key_label,
 };
 
 static const match_table_t key_tokens = {
 	{Opt_wrap_flags, "wrap_flags=%s"},
+	{Opt_wrapping_key_label, "wrapping_key=%s"},
 	{Opt_err, NULL}
 };
 
@@ -50,6 +52,12 @@ static int getoptions(char *datablob, struct trusted_key_options *opt)
 				return -EINVAL;
 			pkwm->wrap_flags = wrap_flags;
 			break;
+		case Opt_wrapping_key_label:
+			if (strlen(args[0].from) > WRAPPING_KEY_LABEL_LEN_MAX)
+				return -EINVAL;
+			strscpy(pkwm->wrapping_key_label, args[0].from,
+				sizeof(pkwm->wrapping_key_label));
+			break;
 		default:
 			return -EINVAL;
 		}
@@ -83,6 +91,7 @@ static int trusted_pkwm_seal(struct trusted_key_payload *p, char *datablob)
 	struct trusted_key_options *options = NULL;
 	struct trusted_pkwm_options *pkwm = NULL;
 	u8 *input_buf, *output_buf = NULL;
+	struct plpks_var var = { 0 };
 	u64 output_len, input_len;
 	int rc;
 
@@ -108,8 +117,18 @@ static int trusted_pkwm_seal(struct trusted_key_payload *p, char *datablob)
 
 	pkwm = options->private;
 
+	if (pkwm->wrapping_key_label[0]) {
+		var.name = (u8 *)pkwm->wrapping_key_label;
+		var.namelen = strlen(pkwm->wrapping_key_label);
+	} else {
+		var.name = (u8 *)PLPKS_DEFAULT_WRAPKEY_LABEL;
+		var.namelen = strlen(PLPKS_DEFAULT_WRAPKEY_LABEL);
+	}
+	var.os = PLPKS_VAR_LINUX;
+	var.component = PLPKS_WRAPKEY_COMPONENT;
+
 	rc = plpks_wrap_object(&input_buf, input_len, pkwm->wrap_flags,
-			       &output_buf, &output_len);
+			       &output_buf, &output_len, &var);
 	if (!rc) {
 		memcpy(p->blob, output_buf, output_len);
 		p->blob_len = output_len;
@@ -161,15 +180,26 @@ static int trusted_pkwm_unseal(struct trusted_key_payload *p, char *datablob)
 static int trusted_pkwm_init(void)
 {
 	int ret;
+	struct plpks_var var;
 
 	if (!plpks_wrapping_is_supported()) {
 		pr_err("H_PKS_WRAP_OBJECT interface not supported\n");
 		return -ENODEV;
 	}
 
-	ret = plpks_gen_wrapping_key();
-	if (ret) {
-		pr_err("Failed to generate default wrapping key\n");
+	var = (struct plpks_var) {
+		.name = (u8 *)PLPKS_DEFAULT_WRAPKEY_LABEL,
+		.namelen = sizeof(PLPKS_DEFAULT_WRAPKEY_LABEL) - 1,
+		.policy = PLPKS_WRAPPINGKEY,
+		.os = PLPKS_VAR_LINUX,
+		.component = PLPKS_WRAPKEY_COMPONENT
+	};
+
+	ret = plpks_gen_wrapping_key(&var);
+
+	/* An existing default wrapping key is considered success. */
+	if (ret && ret != -EEXIST) {
+		pr_err("Failed to generate default wrapping key: %d\n", ret);
 		return ret;
 	}
 
Signature · 1 line
2.52.0
#21 lore raw

Re: [PATCH v6 8/9] keys/trusted_keys: enable PKWM wrapping key selection by label

Sashiko has reviewed this patch and found no issues.
Quoted 10 lines

The PKWM trusted source currently uses the default wrapping key for all seal and unseal operations. However, PKWM supports multiple wrapping keys, enabling better isolation between trusted keys.

Allow users to provide an active wrapping key of their choice created through sysfs when sealing trusted keys through a new wrapping_key=<label> option. If no label is specified, the default wrapping key is used for sealing.

Sashiko has reviewed this patch and found no issues.

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=8
author maintainer
#22 lore raw

PATCH v6 9/9 pseries/plpks/wrapkey: expose PKWM wrapping key management to userspace via sysfs

In reply to Srish Srinivasan

The PKWM trusted source currently uses a single default wrapping key created during initialization. However, PKWM also supports user-created wrapping keys for better isolation between trusted keys.

Expose wrapping key lifecycle operations through sysfs to allow users to create and manage their own wrapping keys.

Create a sysfs interface at /sys/firmware/plpks/wrapkey with the following attributes:

  create        - Create a new wrapping key with a user-provided label
  delete        - Delete a revoked wrapping key by label
  revoke        - Revoke a wrapping key by label
  unrevoke      - Unrevoke a previously revoked wrapping key by label
  list_active   - List labels of all active (non-revoked) wrapping keys
  list_revoked  - List labels of all revoked wrapping keys

User-provided labels are validated to contain only alphanumeric characters.

Wrapping key lifecycle constraints:
- A wrapping key must be revoked before it can be deleted
- Revoked wrapping keys cannot be used for wrap operations until unrevoked
- The label "default-wrapping-key" is reserved for the default wrapping key and cannot be managed through this interface

The wrapping keys themselves are confidential objects stored in PLPKS. Only their labels are exposed to userspace for identification and management purposes.

These user-created wrapping keys can be used in seal operations via the wrapping_key=<label> option.

---
 .../ABI/testing/sysfs-firmware-plpks          | 113 +++++
 arch/powerpc/include/asm/plpks.h              |   9 +
 arch/powerpc/platforms/pseries/Kconfig        |  15 +
 arch/powerpc/platforms/pseries/Makefile       |   1 +
 arch/powerpc/platforms/pseries/plpks-sysfs.c  |  14 +
 .../platforms/pseries/plpks-wrapkey-sysfs.c   | 408 ++++++++++++++++++
 arch/powerpc/platforms/pseries/plpks.c        |   1 -
 7 files changed, 560 insertions(+), 1 deletion(-)
 create mode 100644 arch/powerpc/platforms/pseries/plpks-wrapkey-sysfs.c
Documentation/ABI/testing/sysfs-firmware-plpks +113 -0
index cba061e4eee2..7fa7df526017 100644
@@ -56,3 +56,116 @@ Description:	Bitmask of the wrapping features indicating the wrapping
 		algorithms that are supported for the H_PKS_WRAP_OBJECT requests
 		, represented as a 8 byte hexadecimal ASCII string. Consult the
 		hypervisor documentation for what these flags mean.
+
+What:		/sys/firmware/plpks/wrapkey
+Date:		August 2026
+Contact:	Srish Srinivasan <[email protected]>
+Description:	This directory provides the interface for creating and managing
+		user-created PKWM wrapping keys.
+
+		The directory is present only when CONFIG_PLPKS_WRAPKEY_SYSFS is
+		enabled and the system supports the H_PKS_{UN}REVOKE_OBJECT
+		interfaces.
+
+		Only alphanumeric characters are accepted in wrapping key
+		labels. The wrapping keys themselves are confidential PLPKS
+		objects and are not exposed to userspace. Only their labels are
+		exposed for identification and management.
+
+		Each write is processed as an independent operation and accepts
+		exactly one wrapping key label. The resulting state can be
+		verified through list_active or list_revoked, as appropriate.
+
+		The label "default-wrapping-key" is reserved for the default
+		wrapping key and cannot be managed through this interface.
+
+What:		/sys/firmware/plpks/wrapkey/create
+Date:		August 2026
+Contact:	Srish Srinivasan <[email protected]>
+Description:	(WO) Create a wrapping key with the specified label, except
+		"default-wrapping-key", which is reserved for the default
+		wrapping key.
+
+		On success, the label appears in list_active.
+
+		For example, to create a wrapping key labelled "wk1":
+
+			echo -n wk1 > /sys/firmware/plpks/wrapkey/create
+
+		or:
+
+			printf '%s' 'wk1' > /sys/firmware/plpks/wrapkey/create
+
+What:		/sys/firmware/plpks/wrapkey/delete
+Date:		August 2026
+Contact:	Srish Srinivasan <[email protected]>
+Description:	(WO) Delete a revoked wrapping key identified by the specified
+		label. A wrapping key must be revoked before it can be deleted.
+		The default wrapping key, named "default-wrapping-key", cannot
+		be deleted.
+
+		On success, the label no longer appears in list_revoked.
+
+		For example, to delete a revoked wrapping key labelled "wk1":
+
+			echo -n wk1 > /sys/firmware/plpks/wrapkey/delete
+
+		or:
+
+			printf '%s' 'wk1' > /sys/firmware/plpks/wrapkey/delete
+
+What:		/sys/firmware/plpks/wrapkey/revoke
+Date:		August 2026
+Contact:	Srish Srinivasan <[email protected]>
+Description:	(WO) Revoke a wrapping key identified by the specified label. A
+		revoked wrapping key cannot be used for seal or unseal
+		operations until it is unrevoked. The default wrapping key,
+		named "default-wrapping-key", cannot be revoked.
+
+		On success, the label appears in list_revoked and no longer
+		appears in list_active.
+
+		For example, to revoke a wrapping key labelled "wk1":
+
+			echo -n wk1 > /sys/firmware/plpks/wrapkey/revoke
+
+		or:
+
+			printf '%s' 'wk1' > /sys/firmware/plpks/wrapkey/revoke
+
+What:		/sys/firmware/plpks/wrapkey/unrevoke
+Date:		August 2026
+Contact:	Srish Srinivasan <[email protected]>
+Description:	(WO) Unrevoke a revoked wrapping key identified by the specified
+		label. The default wrapping key, named "default-wrapping-key",
+		is not subject to revocation and therefore cannot be unrevoked
+		through this interface.
+
+		On success, the label appears in list_active and no longer
+		appears in list_revoked.
+
+		For example, to unrevoke a revoked wrapping key labelled "wk1":
+
+			echo -n wk1 > /sys/firmware/plpks/wrapkey/unrevoke
+
+		or:
+
+			printf '%s' 'wk1' > /sys/firmware/plpks/wrapkey/unrevoke
+
+What:		/sys/firmware/plpks/wrapkey/list_active
+Date:		August 2026
+Contact:	Srish Srinivasan <[email protected]>
+Description:	(RO) List the labels of active, non-revoked wrapping keys.
+
+		For example:
+
+			cat /sys/firmware/plpks/wrapkey/list_active
+
+What:		/sys/firmware/plpks/wrapkey/list_revoked
+Date:		August 2026
+Contact:	Srish Srinivasan <[email protected]>
+Description:	(RO) List the labels of revoked wrapping keys.
+
+		For example:
+
+			cat /sys/firmware/plpks/wrapkey/list_revoked
arch/powerpc/include/asm/plpks.h +9 -0
index abf8a71cf3f2..fa0380560f12 100644
@@ -56,6 +56,8 @@
 // Component for a PKWM wrapping key
 #define PLPKS_WRAPKEY_COMPONENT	"PLPKSWR"
 
+#define PLPKS_OBJLABEL_LEN_FIELD_SIZE	2
+
 struct plpks_var {
 	char *component;
 	u8 *name;
@@ -144,6 +146,9 @@ int plpks_get_object_labels(u8 **output_buf, u64 *output_len,
 
 bool plpks_revoke_is_supported(void);
 
+int plpks_init_child_kobj(struct kobject *kobj, const struct kobj_type *ktype,
+			  const char *name);
+
 #else // CONFIG_PSERIES_PLPKS
 static inline bool plpks_is_available(void) { return false; }
 static inline u16 plpks_get_passwordlen(void) { BUILD_BUG(); }
@@ -151,6 +156,10 @@ static inline void plpks_early_init_devtree(void) { }
 static inline int plpks_populate_fdt(void *fdt) { BUILD_BUG(); }
 static inline int plpks_config_create_softlink(struct kobject *from)
 						{ return 0; }
+static inline int plpks_init_child_kobj(struct kobject *kobj,
+					const struct kobj_type *ktype,
+					const char *name)
+						{ return 0; }
 #endif // CONFIG_PSERIES_PLPKS
 
 #endif // _ASM_POWERPC_PLPKS_H
arch/powerpc/platforms/pseries/Kconfig +15 -0
index 7d125e288f6e..14d2eed4d881 100644
@@ -215,3 +215,18 @@ config PPC_SVM
 	 those guests.
 
 	 If unsure, say "N".
+
+config PLPKS_WRAPKEY_SYSFS
+	bool "Enable sysfs interface for managing PKWM wrapping keys"
+	default n
+	depends on PSERIES_PLPKS
+	depends on SYSFS
+	depends on TRUSTED_KEYS_PKWM
+	help
+	  The PKWM trusted source supports user-created wrapping keys that are
+	  managed by the OS. These wrapping keys can be created, revoked,
+	  unrevoked, and deleted through sysfs. Their labels can also be viewed
+	  through sysfs.
+
+	  Say Y if PLPKS and PKWM are enabled and you want to isolate trusted
+	  keys using user-created wrapping keys.
arch/powerpc/platforms/pseries/Makefile +1 -0
index 3ced289a675b..c604be40f546 100644
@@ -33,6 +33,7 @@ obj-$(CONFIG_FA_DUMP)		+= rtas-fadump.o
 obj-$(CONFIG_PSERIES_PLPKS)	+= plpks.o plpks-sysfs.o
 obj-$(CONFIG_PPC_SECURE_BOOT)	+= plpks-secvar.o
 obj-$(CONFIG_PSERIES_PLPKS_SED)	+= plpks_sed_ops.o
+obj-$(CONFIG_PLPKS_WRAPKEY_SYSFS) += plpks-wrapkey-sysfs.o
 obj-$(CONFIG_SUSPEND)		+= suspend.o
 obj-$(CONFIG_PPC_VAS)		+= vas.o vas-sysfs.o
 
arch/powerpc/platforms/pseries/plpks-sysfs.c +14 -0
index f2436229f323..f462dc50a341 100644
@@ -55,6 +55,17 @@ static umode_t plpks_config_attr_is_visible(struct kobject *kobj,
 	return attr->mode;
 }
 
+int plpks_init_child_kobj(struct kobject *kobj, const struct kobj_type *ktype,
+			  const char *name)
+{
+	kobject_init(kobj, ktype);
+
+	if (!plpks_kobj)
+		return -ENODEV;
+
+	return kobject_add(kobj, plpks_kobj, "%s", name);
+}
+
 int plpks_config_create_softlink(struct kobject *from)
 {
 	if (!plpks_config_kobj)
@@ -89,6 +100,7 @@ static __init int plpks_sysfs_init(void)
 	if (!plpks_config_kobj) {
 		pr_err("Failed to create plpks config kobj\n");
 		kobject_put(plpks_kobj);
+		plpks_kobj = NULL;
 		return -ENOMEM;
 	}
 
@@ -96,7 +108,9 @@ static __init int plpks_sysfs_init(void)
 	if (rc) {
 		pr_err("Failed to create attribute group for plpks config\n");
 		kobject_put(plpks_config_kobj);
+		plpks_config_kobj = NULL;
 		kobject_put(plpks_kobj);
+		plpks_kobj = NULL;
 		return rc;
 	}
 
arch/powerpc/platforms/pseries/plpks-wrapkey-sysfs.c new file +408 -0

408 lines not on this page · raw message

arch/powerpc/platforms/pseries/plpks.c +0 -1
index df144accd223..0f3343ec254d 100644
@@ -31,7 +31,6 @@
 #define PLPKS_OBJLABEL_BUF_SIZE	\
 	roundup_pow_of_two(PLPKS_OBJLABEL_BUF_BASE_SIZE)
 
-#define PLPKS_OBJLABEL_LEN_FIELD_SIZE	2
 #define PLPKS_OBJLABEL_PREFIX_LEN	8
 
 #define PLPKS_WRAP_INTERFACE_BIT	3
Signature · 1 line
2.52.0
#23 lore raw

Re: [PATCH v6 9/9] pseries/plpks/wrapkey: expose PKWM wrapping key management to userspace via sysfs

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider: - [Medium] pseries/plpks: label truncation and undocumented format in list_key_labels() - [Medium]…

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [Medium] pseries/plpks: label truncation and undocumented format in list_key_labels()
- [Medium] pseries/plpks: TOCTOU race between delete_store() and unrevoke_store()
- [Low] pseries/plpks: duplicated sysfs store/show logic in plpks-wrapkey-sysfs.c --

commit 58203a02dbc210e21378df2545831a7ea49bbca4
Author: Srish Srinivasan <[email protected]>

pseries/plpks/wrapkey: expose PKWM wrapping key management to userspace via sysfs

This commit adds a sysfs interface under /sys/firmware/plpks/wrapkey to allow userspace to create, revoke, unrevoke, delete, and list PKWM wrapping keys.

Quoted 5 lines
arch/powerpc/platforms/pseries/plpks-wrapkey-sysfs.c new file +0 -0
new file mode 100644
index 0000000000000..c293196cc9adf

[ ... ]

Quoted 23 lines
arch/powerpc/platforms/pseries/plpks-wrapkey-sysfs.c new file +23 -0
+static ssize_t list_key_labels(char *buf, u8 *obj_labels_buf,
+			       u64 obj_labels_count, int revoked)
+{
+	struct plpks_var var = { 0 };
+	u8 *obj_labels_buf_ptr, *comp_prefix;
+	u8 *key_label_ptr;
+	u16 obj_label_len;
+	int len = 0;
+	int is_revoked;
+	u64 i;
+
+	var.os = PLPKS_VAR_LINUX;
+	var.component = PLPKS_WRAPKEY_COMPONENT;
+	obj_labels_buf_ptr = obj_labels_buf;
+
+	for (i = 0; i < obj_labels_count; ++i) {
+		obj_label_len = get_unaligned_be16(obj_labels_buf_ptr);
+		comp_prefix = obj_labels_buf_ptr +
+			      PLPKS_OBJLABEL_LEN_FIELD_SIZE;
+
+		var.namelen = obj_label_len - PLPKS_MAX_LABEL_ATTR_SIZE;
+		var.name = kzalloc(var.namelen + 1, GFP_KERNEL);
+

arch/powerpc/platforms/pseries/plpks-wrapkey-sysfs.c line 71

Quoted 10 lines
arch/powerpc/platforms/pseries/plpks-wrapkey-sysfs.c new file +10 -0
+		if (!var.name)
+			return -ENOMEM;
+
+		key_label_ptr = comp_prefix + PLPKS_MAX_LABEL_ATTR_SIZE;
+		memcpy(var.name, key_label_ptr, var.namelen);
+
+		is_revoked = plpks_is_wrapping_key_revoked(&var);
+		if (is_revoked == revoked)
+			len += sysfs_emit_at(buf, len, "<%s>\n",
+					     (char *)var.name);

[Severity: Medium]
Can list_key_labels() silently truncate wrapping key labels when reading /sys/firmware/plpks/wrapkey/list_active or list_revoked?

plpks_get_object_labels() can retrieve labels across multiple 4KB buffers via H_CONTINUE, and each label can be up to PLPKS_MAX_NAME_SIZE (239) bytes. However, a standard kobj_attribute show buffer is limited to PAGE_SIZE - 1 bytes. Once len reaches PAGE_SIZE - 1, sysfs_emit_at() returns 0 and any remaining wrapping key labels are silently omitted without any error or indication to userspace.

Also, list_key_labels() formats each label wrapped in angle brackets ("<%s>\n"), whereas Documentation/ABI/testing/sysfs-firmware-plpks does not mention the angle-bracket format for list_active and list_revoked. Should the output format and buffer limit be addressed or documented?

Quoted 58 lines
arch/powerpc/platforms/pseries/plpks-wrapkey-sysfs.c new file +58 -0
+		else if (is_revoked < 0)
+			pr_warn("Failed to get revocation status for <%s>\n",
+				(char *)var.name);
+
+		kfree(var.name);
+		obj_labels_buf_ptr = key_label_ptr + var.namelen;
+	}
+
+	return len;
+}
+
+static ssize_t list_active_show(struct kobject *kobj,
+				struct kobj_attribute *attr, char *buf)
+{
+	u8 *obj_labels_buf = NULL;
+	u64 obj_labels_count = 0;
+	int rc;
+
+	rc = plpks_get_object_labels(&obj_labels_buf, &obj_labels_count,
+				     PLPKS_WRAPKEY_COMPONENT);
+	if (rc) {
+		pr_err("Retrieving object labels failed. rc=%d\n", rc);
+		goto out;
+	}
+
+	rc = list_key_labels(buf, obj_labels_buf, obj_labels_count,
+			     PLPKS_WRAPKEY_UNREVOKED);
+
+out:
+	kfree(obj_labels_buf);
+	return rc;
+}
+
+static ssize_t list_revoked_show(struct kobject *kobj,
+				 struct kobj_attribute *attr, char *buf)
+{
+	u8 *obj_labels_buf = NULL;
+	u64 obj_labels_count = 0;
+	int rc;
+
+	rc = plpks_get_object_labels(&obj_labels_buf, &obj_labels_count,
+				     PLPKS_WRAPKEY_COMPONENT);
+	if (rc) {
+		pr_err("Retrieving object labels failed. rc=%d\n", rc);
+		goto out;
+	}
+
+	rc = list_key_labels(buf, obj_labels_buf, obj_labels_count,
+			     PLPKS_WRAPKEY_REVOKED);
+
+out:
+	kfree(obj_labels_buf);
+	return rc;
+}
+
+static ssize_t create_store(struct kobject *kobj, struct kobj_attribute *attr,
+			    const char *buf, size_t count)
+{

[ ... ]

Quoted 44 lines
arch/powerpc/platforms/pseries/plpks-wrapkey-sysfs.c new file +44 -0
+static ssize_t unrevoke_store(struct kobject *kobj, struct kobj_attribute *attr,
+			      const char *buf, size_t count)
+{
+	struct plpks_var var = { 0 };
+	int rc;
+
+	if (!capable(CAP_SYS_ADMIN)) {
+		rc = -EPERM;
+		goto out;
+	}
+
+	if (!count) {
+		rc = -EINVAL;
+		goto out;
+	}
+
+	if (count > PLPKS_MAX_NAME_SIZE) {
+		rc = -ENAMETOOLONG;
+		goto out;
+	}
+
+	if (!strcmp(buf, PLPKS_DEFAULT_WRAPKEY_LABEL)) {
+		rc = -EINVAL;
+		goto out;
+	}
+
+	if (!is_wrapkey_label_alnum(buf, count)) {
+		rc = -EINVAL;
+		goto out;
+	}
+
+	var.name = kstrndup(buf, count, GFP_KERNEL);
+	if (!var.name) {
+		rc = -ENOMEM;
+		goto out;
+	}
+
+	var.namelen = count;
+	var.os = PLPKS_VAR_LINUX;
+	var.component = PLPKS_WRAPKEY_COMPONENT;
+
+	rc = plpks_unrevoke_wrapping_key(&var);
+
+	if (rc) {

arch/powerpc/platforms/pseries/plpks-wrapkey-sysfs.c line 290

Quoted 10 lines
arch/powerpc/platforms/pseries/plpks-wrapkey-sysfs.c new file +10 -0
+		pr_err("un-revocation of wrapping key <%s> failed. rc = %d\n",
+		       (char *)var.name, rc);
+		goto out;
+	}
+
+	rc = count;
+out:
+	kfree(var.name);
+	return rc;
+}

[Severity: Low]
This isn't a bug, but create_store(), revoke_store(), unrevoke_store(), and delete_store() all duplicate the same CAP_SYS_ADMIN check, count validation, PLPKS_DEFAULT_WRAPKEY_LABEL check, is_wrapkey_label_alnum() validation, kstrndup() allocation, and struct plpks_var initialization (with unrevoke_store() returning -EINVAL instead of -EPERM for PLPKS_DEFAULT_WRAPKEY_LABEL).

Similarly, list_active_show() and list_revoked_show() duplicate the plpks_get_object_labels() call and cleanup, differing only in passing PLPKS_WRAPKEY_UNREVOKED vs PLPKS_WRAPKEY_REVOKED to list_key_labels().

Could the shared store validation/setup and show label retrieval logic be factored into common helpers?

Quoted 4 lines
arch/powerpc/platforms/pseries/plpks-wrapkey-sysfs.c new file +4 -0
+
+static ssize_t delete_store(struct kobject *kobj, struct kobj_attribute *attr,
+			    const char *buf, size_t count)
+{

[ ... ]

arch/powerpc/platforms/pseries/plpks-wrapkey-sysfs.c line 333

Quoted 1 line
arch/powerpc/platforms/pseries/plpks-wrapkey-sysfs.c new file +1 -0
+	rc = plpks_del_wrapping_key(&var);

[Severity: Medium]
What prevents a TOCTOU race if delete_store() and unrevoke_store() are called concurrently on the same revoked wrapping key?

In arch/powerpc/platforms/pseries/plpks.c, plpks_del_wrapping_key() enforces that a key is revoked before deletion across two separate hypercalls without any lock:

plpks_del_wrapping_key()

    rc = plpks_is_wrapping_key_revoked(var);
    if (rc == 1) {
        ...
        rc = plpks_remove_var(PLPKS_WRAPKEY_COMPONENT, var->os, vname);

Because delete and unrevoke are separate sysfs attributes, kernfs only holds its per-open-file mutex separately for each attribute. If CPU 0 enters delete_store() -> plpks_del_wrapping_key() and sees plpks_is_wrapping_key_revoked() == 1, and CPU 1 concurrently runs unrevoke_store() -> plpks_unrevoke_wrapping_key() (H_PKS_UNREVOKE_OBJECT) before CPU 0 calls plpks_remove_var() (H_PKS_REMOVE_OBJECT), won't CPU 0 delete the now-active wrapping key?

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=9
author maintainer
#24 lore raw

Re: [PATCH v6 9/9] pseries/plpks/wrapkey: expose PKWM wrapping key management to userspace via sysfs

Hi

Quoted 5 lines · On 11/5/26 8:32 PM, [email protected] wrote:

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [Medium] pseries/plpks: label truncation and undocumented format in list_key_labels()
- [Medium] pseries/plpks: TOCTOU race between delete_store() and unrevoke_store()
- [Low] pseries/plpks: duplicated sysfs store/show logic in plpks-wrapkey-sysfs.c --

Yes, these are valid findings and I will fix these in my next version.

Thanks,
Srish

Quoted 220 lines

commit 58203a02dbc210e21378df2545831a7ea49bbca4
Author: Srish Srinivasan <[email protected]>

pseries/plpks/wrapkey: expose PKWM wrapping key management to userspace via sysfs

This commit adds a sysfs interface under /sys/firmware/plpks/wrapkey to allow userspace to create, revoke, unrevoke, delete, and list PKWM wrapping keys.

Quoted 5 lines
arch/powerpc/platforms/pseries/plpks-wrapkey-sysfs.c new file +0 -0
new file mode 100644
index 0000000000000..c293196cc9adf

[ ... ]

Quoted 23 lines
arch/powerpc/platforms/pseries/plpks-wrapkey-sysfs.c new file +23 -0
+static ssize_t list_key_labels(char *buf, u8 *obj_labels_buf,
+			       u64 obj_labels_count, int revoked)
+{
+	struct plpks_var var = { 0 };
+	u8 *obj_labels_buf_ptr, *comp_prefix;
+	u8 *key_label_ptr;
+	u16 obj_label_len;
+	int len = 0;
+	int is_revoked;
+	u64 i;
+
+	var.os = PLPKS_VAR_LINUX;
+	var.component = PLPKS_WRAPKEY_COMPONENT;
+	obj_labels_buf_ptr = obj_labels_buf;
+
+	for (i = 0; i < obj_labels_count; ++i) {
+		obj_label_len = get_unaligned_be16(obj_labels_buf_ptr);
+		comp_prefix = obj_labels_buf_ptr +
+			      PLPKS_OBJLABEL_LEN_FIELD_SIZE;
+
+		var.namelen = obj_label_len - PLPKS_MAX_LABEL_ATTR_SIZE;
+		var.name = kzalloc(var.namelen + 1, GFP_KERNEL);
+

arch/powerpc/platforms/pseries/plpks-wrapkey-sysfs.c line 71

Quoted 10 lines
arch/powerpc/platforms/pseries/plpks-wrapkey-sysfs.c new file +10 -0
+		if (!var.name)
+			return -ENOMEM;
+
+		key_label_ptr = comp_prefix + PLPKS_MAX_LABEL_ATTR_SIZE;
+		memcpy(var.name, key_label_ptr, var.namelen);
+
+		is_revoked = plpks_is_wrapping_key_revoked(&var);
+		if (is_revoked == revoked)
+			len += sysfs_emit_at(buf, len, "<%s>\n",
+					     (char *)var.name);

[Severity: Medium]
Can list_key_labels() silently truncate wrapping key labels when reading /sys/firmware/plpks/wrapkey/list_active or list_revoked?

plpks_get_object_labels() can retrieve labels across multiple 4KB buffers via H_CONTINUE, and each label can be up to PLPKS_MAX_NAME_SIZE (239) bytes. However, a standard kobj_attribute show buffer is limited to PAGE_SIZE - 1 bytes. Once len reaches PAGE_SIZE - 1, sysfs_emit_at() returns 0 and any remaining wrapping key labels are silently omitted without any error or indication to userspace.

Also, list_key_labels() formats each label wrapped in angle brackets ("<%s>\n"), whereas Documentation/ABI/testing/sysfs-firmware-plpks does not mention the angle-bracket format for list_active and list_revoked. Should the output format and buffer limit be addressed or documented?

Quoted 58 lines
arch/powerpc/platforms/pseries/plpks-wrapkey-sysfs.c new file +58 -0
+		else if (is_revoked < 0)
+			pr_warn("Failed to get revocation status for <%s>\n",
+				(char *)var.name);
+
+		kfree(var.name);
+		obj_labels_buf_ptr = key_label_ptr + var.namelen;
+	}
+
+	return len;
+}
+
+static ssize_t list_active_show(struct kobject *kobj,
+				struct kobj_attribute *attr, char *buf)
+{
+	u8 *obj_labels_buf = NULL;
+	u64 obj_labels_count = 0;
+	int rc;
+
+	rc = plpks_get_object_labels(&obj_labels_buf, &obj_labels_count,
+				     PLPKS_WRAPKEY_COMPONENT);
+	if (rc) {
+		pr_err("Retrieving object labels failed. rc=%d\n", rc);
+		goto out;
+	}
+
+	rc = list_key_labels(buf, obj_labels_buf, obj_labels_count,
+			     PLPKS_WRAPKEY_UNREVOKED);
+
+out:
+	kfree(obj_labels_buf);
+	return rc;
+}
+
+static ssize_t list_revoked_show(struct kobject *kobj,
+				 struct kobj_attribute *attr, char *buf)
+{
+	u8 *obj_labels_buf = NULL;
+	u64 obj_labels_count = 0;
+	int rc;
+
+	rc = plpks_get_object_labels(&obj_labels_buf, &obj_labels_count,
+				     PLPKS_WRAPKEY_COMPONENT);
+	if (rc) {
+		pr_err("Retrieving object labels failed. rc=%d\n", rc);
+		goto out;
+	}
+
+	rc = list_key_labels(buf, obj_labels_buf, obj_labels_count,
+			     PLPKS_WRAPKEY_REVOKED);
+
+out:
+	kfree(obj_labels_buf);
+	return rc;
+}
+
+static ssize_t create_store(struct kobject *kobj, struct kobj_attribute *attr,
+			    const char *buf, size_t count)
+{

[ ... ]

Quoted 44 lines
arch/powerpc/platforms/pseries/plpks-wrapkey-sysfs.c new file +44 -0
+static ssize_t unrevoke_store(struct kobject *kobj, struct kobj_attribute *attr,
+			      const char *buf, size_t count)
+{
+	struct plpks_var var = { 0 };
+	int rc;
+
+	if (!capable(CAP_SYS_ADMIN)) {
+		rc = -EPERM;
+		goto out;
+	}
+
+	if (!count) {
+		rc = -EINVAL;
+		goto out;
+	}
+
+	if (count > PLPKS_MAX_NAME_SIZE) {
+		rc = -ENAMETOOLONG;
+		goto out;
+	}
+
+	if (!strcmp(buf, PLPKS_DEFAULT_WRAPKEY_LABEL)) {
+		rc = -EINVAL;
+		goto out;
+	}
+
+	if (!is_wrapkey_label_alnum(buf, count)) {
+		rc = -EINVAL;
+		goto out;
+	}
+
+	var.name = kstrndup(buf, count, GFP_KERNEL);
+	if (!var.name) {
+		rc = -ENOMEM;
+		goto out;
+	}
+
+	var.namelen = count;
+	var.os = PLPKS_VAR_LINUX;
+	var.component = PLPKS_WRAPKEY_COMPONENT;
+
+	rc = plpks_unrevoke_wrapping_key(&var);
+
+	if (rc) {

arch/powerpc/platforms/pseries/plpks-wrapkey-sysfs.c line 290

Quoted 10 lines
arch/powerpc/platforms/pseries/plpks-wrapkey-sysfs.c new file +10 -0
+		pr_err("un-revocation of wrapping key <%s> failed. rc = %d\n",
+		       (char *)var.name, rc);
+		goto out;
+	}
+
+	rc = count;
+out:
+	kfree(var.name);
+	return rc;
+}

[Severity: Low]
This isn't a bug, but create_store(), revoke_store(), unrevoke_store(), and delete_store() all duplicate the same CAP_SYS_ADMIN check, count validation, PLPKS_DEFAULT_WRAPKEY_LABEL check, is_wrapkey_label_alnum() validation, kstrndup() allocation, and struct plpks_var initialization (with unrevoke_store() returning -EINVAL instead of -EPERM for PLPKS_DEFAULT_WRAPKEY_LABEL).

Similarly, list_active_show() and list_revoked_show() duplicate the plpks_get_object_labels() call and cleanup, differing only in passing PLPKS_WRAPKEY_UNREVOKED vs PLPKS_WRAPKEY_REVOKED to list_key_labels().

Could the shared store validation/setup and show label retrieval logic be factored into common helpers?

Quoted 4 lines
arch/powerpc/platforms/pseries/plpks-wrapkey-sysfs.c new file +4 -0
+
+static ssize_t delete_store(struct kobject *kobj, struct kobj_attribute *attr,
+			    const char *buf, size_t count)
+{

[ ... ]

arch/powerpc/platforms/pseries/plpks-wrapkey-sysfs.c line 333

Quoted 1 line
arch/powerpc/platforms/pseries/plpks-wrapkey-sysfs.c new file +1 -0
+	rc = plpks_del_wrapping_key(&var);

[Severity: Medium]
What prevents a TOCTOU race if delete_store() and unrevoke_store() are called concurrently on the same revoked wrapping key?

In arch/powerpc/platforms/pseries/plpks.c, plpks_del_wrapping_key() enforces that a key is revoked before deletion across two separate hypercalls without any lock:

plpks_del_wrapping_key()

     rc = plpks_is_wrapping_key_revoked(var);
     if (rc == 1) {
         ...
         rc = plpks_remove_var(PLPKS_WRAPKEY_COMPONENT, var->os, vname);

Because delete and unrevoke are separate sysfs attributes, kernfs only holds its per-open-file mutex separately for each attribute. If CPU 0 enters delete_store() -> plpks_del_wrapping_key() and sees plpks_is_wrapping_key_revoked() == 1, and CPU 1 concurrently runs unrevoke_store() -> plpks_unrevoke_wrapping_key() (H_PKS_UNREVOKE_OBJECT) before CPU 0 calls plpks_remove_var() (H_PKS_REMOVE_OBJECT), won't CPU 0 delete the now-active wrapping key?