* [PATCH v2] KEYS: encrypted: fix integer overflow of datablob_len
@ 2026-08-26 15:44 Cen Zhang (Microsoft Security FORGE Labs)
2026-08-28 1:53 ` Jarkko Sakkinen
` (2 more replies)
0 siblings, 3 replies; 5+ messages in thread
From: Cen Zhang (Microsoft Security FORGE Labs) @ 2026-08-26 15:44 UTC (permalink / raw)
To: Mimi Zohar
Cc: David Howells, Jarkko Sakkinen, Paul Moore, James Morris,
Serge E. Hallyn, Roberto Sassu, David Safford, Greg Kroah-Hartman,
Kees Cook, Francis Perron, linux-integrity, keyrings,
linux-security-module, linux-kernel, Akrites SIRT,
AutonomousCodeSecurity, Cen Zhang
From: "Cen Zhang (Microsoft Security FORGE Labs)" <cenzhang@microsoft.com>
The datablob_len field in struct encrypted_key_payload and the local
variable in encrypted_key_alloc() are declared as unsigned short, which
has a maximum value of 65535. The datablob_len is computed as:
format_len + 1 + strlen(master_desc) + 1 + strlen(datalen) + 1
+ ivsize + 1 + encrypted_datalen
An attacker can create an encrypted key with a very long datalen string
(e.g., 32756 characters of leading zeros followed by "4096", which
kstrtol() happily parses as 4096), and then update it with a very long
master_desc string (~32760 characters). The combined lengths exceed
65535, causing the unsigned short to silently wrap around. This results
in a grossly undersized kzalloc() allocation, and the subsequent
memcpy() in __ekey_init() writes ~32KB past the end of the buffer,
corrupting adjacent slab objects.
Fix this by:
1. Using check_add_overflow() to calculate datablob_len directly into
its existing unsigned short destination.
2. Checking the total payload length the same way before passing it to
key_payload_reserve(), since key->datalen is also unsigned short.
3. Using kzalloc_flex() to allocate encrypted_key_payload together with
its trailing payload_data[] array.
Fixes: 7e70cb497850 ("keys: add new key-type encrypted")
Cc: stable@vger.kernel.org
Assisted-by: GitHub-Copilot:claude-opus-4.6
Signed-off-by: Cen Zhang (Microsoft Security FORGE Labs) <cenzhang@microsoft.com>
Signed-off-by: Francis Perron (Akrites SIRT) <francis@akrites.dev>
---
Changes in v2:
- Keep datablob_len unchanged and check both unsigned short bounds with
check_add_overflow().
- Use kzalloc_flex() for the trailing payload_data[] array.
- Correct the attribution and sign-off trailers.
The initial version was discussed off-list and is not publicly archived.
security/keys/encrypted-keys/encrypted.c | 20 ++++++++++++++------
1 file changed, 14 insertions(+), 6 deletions(-)
diff --git a/security/keys/encrypted-keys/encrypted.c b/security/keys/encrypted-keys/encrypted.c
index 59cb77b237b3..e07092ea301a 100644
--- a/security/keys/encrypted-keys/encrypted.c
+++ b/security/keys/encrypted-keys/encrypted.c
@@ -19,6 +19,7 @@
#include <linux/parser.h>
#include <linux/string.h>
#include <linux/err.h>
+#include <linux/overflow.h>
#include <keys/user-type.h>
#include <keys/trusted-type.h>
#include <keys/encrypted-type.h>
@@ -579,6 +580,7 @@ static struct encrypted_key_payload *encrypted_key_alloc(struct key *key,
{
struct encrypted_key_payload *epayload = NULL;
unsigned short datablob_len;
+ unsigned short payload_totallen;
unsigned short decrypted_datalen;
unsigned short payload_datalen;
unsigned int encrypted_datalen;
@@ -632,16 +634,22 @@ static struct encrypted_key_payload *encrypted_key_alloc(struct key *key,
encrypted_datalen = roundup(decrypted_datalen, blksize);
- datablob_len = format_len + 1 + strlen(master_desc) + 1
- + strlen(datalen) + 1 + ivsize + 1 + encrypted_datalen;
+ if (check_add_overflow(format_len + 1 + strlen(master_desc) + 1
+ + strlen(datalen) + 1 + ivsize + 1,
+ encrypted_datalen, &datablob_len))
+ return ERR_PTR(-EINVAL);
+
+ if (check_add_overflow(datablob_len,
+ payload_datalen + HASH_SIZE + 1,
+ &payload_totallen))
+ return ERR_PTR(-EINVAL);
- ret = key_payload_reserve(key, payload_datalen + datablob_len
- + HASH_SIZE + 1);
+ ret = key_payload_reserve(key, payload_totallen);
if (ret < 0)
return ERR_PTR(ret);
- epayload = kzalloc(sizeof(*epayload) + payload_datalen +
- datablob_len + HASH_SIZE + 1, GFP_KERNEL);
+ epayload = kzalloc_flex(*epayload, payload_data, payload_totallen,
+ GFP_KERNEL);
if (!epayload)
return ERR_PTR(-ENOMEM);
--
2.55.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH v2] KEYS: encrypted: fix integer overflow of datablob_len
2026-08-26 15:44 [PATCH v2] KEYS: encrypted: fix integer overflow of datablob_len Cen Zhang (Microsoft Security FORGE Labs)
@ 2026-08-28 1:53 ` Jarkko Sakkinen
2026-08-28 1:55 ` Jarkko Sakkinen
2026-08-28 1:59 ` Jarkko Sakkinen
2 siblings, 0 replies; 5+ messages in thread
From: Jarkko Sakkinen @ 2026-08-28 1:53 UTC (permalink / raw)
To: Cen Zhang (Microsoft Security FORGE Labs)
Cc: Mimi Zohar, David Howells, Paul Moore, James Morris,
Serge E. Hallyn, Roberto Sassu, David Safford, Greg Kroah-Hartman,
Kees Cook, Francis Perron, linux-integrity, keyrings,
linux-security-module, linux-kernel, Akrites SIRT,
AutonomousCodeSecurity, Cen Zhang
On Wed, Aug 26, 2026 at 11:44:56AM -0400, Cen Zhang (Microsoft Security FORGE Labs) wrote:
> From: "Cen Zhang (Microsoft Security FORGE Labs)" <cenzhang@microsoft.com>
>
> The datablob_len field in struct encrypted_key_payload and the local
> variable in encrypted_key_alloc() are declared as unsigned short, which
> has a maximum value of 65535. The datablob_len is computed as:
>
> format_len + 1 + strlen(master_desc) + 1 + strlen(datalen) + 1
> + ivsize + 1 + encrypted_datalen
>
> An attacker can create an encrypted key with a very long datalen string
> (e.g., 32756 characters of leading zeros followed by "4096", which
> kstrtol() happily parses as 4096), and then update it with a very long
> master_desc string (~32760 characters). The combined lengths exceed
> 65535, causing the unsigned short to silently wrap around. This results
> in a grossly undersized kzalloc() allocation, and the subsequent
> memcpy() in __ekey_init() writes ~32KB past the end of the buffer,
> corrupting adjacent slab objects.
>
> Fix this by:
> 1. Using check_add_overflow() to calculate datablob_len directly into
> its existing unsigned short destination.
> 2. Checking the total payload length the same way before passing it to
> key_payload_reserve(), since key->datalen is also unsigned short.
> 3. Using kzalloc_flex() to allocate encrypted_key_payload together with
> its trailing payload_data[] array.
>
> Fixes: 7e70cb497850 ("keys: add new key-type encrypted")
> Cc: stable@vger.kernel.org
> Assisted-by: GitHub-Copilot:claude-opus-4.6
> Signed-off-by: Cen Zhang (Microsoft Security FORGE Labs) <cenzhang@microsoft.com>
> Signed-off-by: Francis Perron (Akrites SIRT) <francis@akrites.dev>
Please remove "(Microsoft Security FORGE Labs)" and "(Akrites SIRT)"
> ---
> Changes in v2:
> - Keep datablob_len unchanged and check both unsigned short bounds with
> check_add_overflow().
> - Use kzalloc_flex() for the trailing payload_data[] array.
> - Correct the attribution and sign-off trailers.
>
> The initial version was discussed off-list and is not publicly archived.
>
> security/keys/encrypted-keys/encrypted.c | 20 ++++++++++++++------
> 1 file changed, 14 insertions(+), 6 deletions(-)
>
> diff --git a/security/keys/encrypted-keys/encrypted.c b/security/keys/encrypted-keys/encrypted.c
> index 59cb77b237b3..e07092ea301a 100644
> --- a/security/keys/encrypted-keys/encrypted.c
> +++ b/security/keys/encrypted-keys/encrypted.c
> @@ -19,6 +19,7 @@
> #include <linux/parser.h>
> #include <linux/string.h>
> #include <linux/err.h>
> +#include <linux/overflow.h>
> #include <keys/user-type.h>
> #include <keys/trusted-type.h>
> #include <keys/encrypted-type.h>
> @@ -579,6 +580,7 @@ static struct encrypted_key_payload *encrypted_key_alloc(struct key *key,
> {
> struct encrypted_key_payload *epayload = NULL;
> unsigned short datablob_len;
> + unsigned short payload_totallen;
> unsigned short decrypted_datalen;
> unsigned short payload_datalen;
> unsigned int encrypted_datalen;
> @@ -632,16 +634,22 @@ static struct encrypted_key_payload *encrypted_key_alloc(struct key *key,
>
> encrypted_datalen = roundup(decrypted_datalen, blksize);
>
> - datablob_len = format_len + 1 + strlen(master_desc) + 1
> - + strlen(datalen) + 1 + ivsize + 1 + encrypted_datalen;
> + if (check_add_overflow(format_len + 1 + strlen(master_desc) + 1
> + + strlen(datalen) + 1 + ivsize + 1,
> + encrypted_datalen, &datablob_len))
> + return ERR_PTR(-EINVAL);
> +
> + if (check_add_overflow(datablob_len,
> + payload_datalen + HASH_SIZE + 1,
> + &payload_totallen))
> + return ERR_PTR(-EINVAL);
>
> - ret = key_payload_reserve(key, payload_datalen + datablob_len
> - + HASH_SIZE + 1);
> + ret = key_payload_reserve(key, payload_totallen);
> if (ret < 0)
> return ERR_PTR(ret);
>
> - epayload = kzalloc(sizeof(*epayload) + payload_datalen +
> - datablob_len + HASH_SIZE + 1, GFP_KERNEL);
> + epayload = kzalloc_flex(*epayload, payload_data, payload_totallen,
> + GFP_KERNEL);
> if (!epayload)
> return ERR_PTR(-ENOMEM);
>
> --
> 2.55.0
BR, Jarkko
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH v2] KEYS: encrypted: fix integer overflow of datablob_len
2026-08-26 15:44 [PATCH v2] KEYS: encrypted: fix integer overflow of datablob_len Cen Zhang (Microsoft Security FORGE Labs)
2026-08-28 1:53 ` Jarkko Sakkinen
@ 2026-08-28 1:55 ` Jarkko Sakkinen
2026-08-28 1:59 ` Jarkko Sakkinen
2 siblings, 0 replies; 5+ messages in thread
From: Jarkko Sakkinen @ 2026-08-28 1:55 UTC (permalink / raw)
To: Cen Zhang (Microsoft Security FORGE Labs)
Cc: Mimi Zohar, David Howells, Paul Moore, James Morris,
Serge E. Hallyn, Roberto Sassu, David Safford, Greg Kroah-Hartman,
Kees Cook, Francis Perron, linux-integrity, keyrings,
linux-security-module, linux-kernel, Akrites SIRT,
AutonomousCodeSecurity, Cen Zhang
On Wed, Aug 26, 2026 at 11:44:56AM -0400, Cen Zhang (Microsoft Security FORGE Labs) wrote:
> From: "Cen Zhang (Microsoft Security FORGE Labs)" <cenzhang@microsoft.com>
>
> The datablob_len field in struct encrypted_key_payload and the local
> variable in encrypted_key_alloc() are declared as unsigned short, which
> has a maximum value of 65535. The datablob_len is computed as:
>
> format_len + 1 + strlen(master_desc) + 1 + strlen(datalen) + 1
> + ivsize + 1 + encrypted_datalen
>
> An attacker can create an encrypted key with a very long datalen string
> (e.g., 32756 characters of leading zeros followed by "4096", which
> kstrtol() happily parses as 4096), and then update it with a very long
> master_desc string (~32760 characters). The combined lengths exceed
> 65535, causing the unsigned short to silently wrap around. This results
> in a grossly undersized kzalloc() allocation, and the subsequent
> memcpy() in __ekey_init() writes ~32KB past the end of the buffer,
> corrupting adjacent slab objects.
>
> Fix this by:
> 1. Using check_add_overflow() to calculate datablob_len directly into
> its existing unsigned short destination.
> 2. Checking the total payload length the same way before passing it to
> key_payload_reserve(), since key->datalen is also unsigned short.
> 3. Using kzalloc_flex() to allocate encrypted_key_payload together with
> its trailing payload_data[] array.
>
> Fixes: 7e70cb497850 ("keys: add new key-type encrypted")
> Cc: stable@vger.kernel.org
> Assisted-by: GitHub-Copilot:claude-opus-4.6
> Signed-off-by: Cen Zhang (Microsoft Security FORGE Labs) <cenzhang@microsoft.com>
> Signed-off-by: Francis Perron (Akrites SIRT) <francis@akrites.dev>
> ---
> Changes in v2:
> - Keep datablob_len unchanged and check both unsigned short bounds with
> check_add_overflow().
> - Use kzalloc_flex() for the trailing payload_data[] array.
> - Correct the attribution and sign-off trailers.
>
> The initial version was discussed off-list and is not publicly archived.
I have no recollection of being part of this discussion.
>
> security/keys/encrypted-keys/encrypted.c | 20 ++++++++++++++------
> 1 file changed, 14 insertions(+), 6 deletions(-)
>
> diff --git a/security/keys/encrypted-keys/encrypted.c b/security/keys/encrypted-keys/encrypted.c
> index 59cb77b237b3..e07092ea301a 100644
> --- a/security/keys/encrypted-keys/encrypted.c
> +++ b/security/keys/encrypted-keys/encrypted.c
> @@ -19,6 +19,7 @@
> #include <linux/parser.h>
> #include <linux/string.h>
> #include <linux/err.h>
> +#include <linux/overflow.h>
> #include <keys/user-type.h>
> #include <keys/trusted-type.h>
> #include <keys/encrypted-type.h>
> @@ -579,6 +580,7 @@ static struct encrypted_key_payload *encrypted_key_alloc(struct key *key,
> {
> struct encrypted_key_payload *epayload = NULL;
> unsigned short datablob_len;
> + unsigned short payload_totallen;
> unsigned short decrypted_datalen;
> unsigned short payload_datalen;
> unsigned int encrypted_datalen;
> @@ -632,16 +634,22 @@ static struct encrypted_key_payload *encrypted_key_alloc(struct key *key,
>
> encrypted_datalen = roundup(decrypted_datalen, blksize);
>
> - datablob_len = format_len + 1 + strlen(master_desc) + 1
> - + strlen(datalen) + 1 + ivsize + 1 + encrypted_datalen;
> + if (check_add_overflow(format_len + 1 + strlen(master_desc) + 1
> + + strlen(datalen) + 1 + ivsize + 1,
> + encrypted_datalen, &datablob_len))
> + return ERR_PTR(-EINVAL);
> +
> + if (check_add_overflow(datablob_len,
> + payload_datalen + HASH_SIZE + 1,
> + &payload_totallen))
> + return ERR_PTR(-EINVAL);
>
> - ret = key_payload_reserve(key, payload_datalen + datablob_len
> - + HASH_SIZE + 1);
> + ret = key_payload_reserve(key, payload_totallen);
> if (ret < 0)
> return ERR_PTR(ret);
>
> - epayload = kzalloc(sizeof(*epayload) + payload_datalen +
> - datablob_len + HASH_SIZE + 1, GFP_KERNEL);
> + epayload = kzalloc_flex(*epayload, payload_data, payload_totallen,
> + GFP_KERNEL);
> if (!epayload)
> return ERR_PTR(-ENOMEM);
>
> --
> 2.55.0
BR, Jarkko
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH v2] KEYS: encrypted: fix integer overflow of datablob_len
2026-08-26 15:44 [PATCH v2] KEYS: encrypted: fix integer overflow of datablob_len Cen Zhang (Microsoft Security FORGE Labs)
2026-08-28 1:53 ` Jarkko Sakkinen
2026-08-28 1:55 ` Jarkko Sakkinen
@ 2026-08-28 1:59 ` Jarkko Sakkinen
2026-09-09 15:30 ` Cen Zhang (Microsoft Security FORGE Labs)
2 siblings, 1 reply; 5+ messages in thread
From: Jarkko Sakkinen @ 2026-08-28 1:59 UTC (permalink / raw)
To: Cen Zhang (Microsoft Security FORGE Labs)
Cc: Mimi Zohar, David Howells, Paul Moore, James Morris,
Serge E. Hallyn, Roberto Sassu, David Safford, Greg Kroah-Hartman,
Kees Cook, Francis Perron, linux-integrity, keyrings,
linux-security-module, linux-kernel, Akrites SIRT,
AutonomousCodeSecurity, Cen Zhang
On Wed, Aug 26, 2026 at 11:44:56AM -0400, Cen Zhang (Microsoft Security FORGE Labs) wrote:
> From: "Cen Zhang (Microsoft Security FORGE Labs)" <cenzhang@microsoft.com>
>
> The datablob_len field in struct encrypted_key_payload and the local
> variable in encrypted_key_alloc() are declared as unsigned short, which
> has a maximum value of 65535. The datablob_len is computed as:
>
> format_len + 1 + strlen(master_desc) + 1 + strlen(datalen) + 1
> + ivsize + 1 + encrypted_datalen
>
> An attacker can create an encrypted key with a very long datalen string
> (e.g., 32756 characters of leading zeros followed by "4096", which
> kstrtol() happily parses as 4096), and then update it with a very long
> master_desc string (~32760 characters). The combined lengths exceed
> 65535, causing the unsigned short to silently wrap around. This results
Please use concrete sizes as possible. E.g., 16-bit word or u16 would be
much less involved than "unsigned short".
> in a grossly undersized kzalloc() allocation, and the subsequent
> memcpy() in __ekey_init() writes ~32KB past the end of the buffer,
> corrupting adjacent slab objects.
One more: please describe the issue as ergnomic as possible (but still
complete). I care neither Alice nor Bob.
>
> Fix this by:
> 1. Using check_add_overflow() to calculate datablob_len directly into
> its existing unsigned short destination.
> 2. Checking the total payload length the same way before passing it to
> key_payload_reserve(), since key->datalen is also unsigned short.
> 3. Using kzalloc_flex() to allocate encrypted_key_payload together with
> its trailing payload_data[] array.
>
> Fixes: 7e70cb497850 ("keys: add new key-type encrypted")
> Cc: stable@vger.kernel.org
> Assisted-by: GitHub-Copilot:claude-opus-4.6
> Signed-off-by: Cen Zhang (Microsoft Security FORGE Labs) <cenzhang@microsoft.com>
> Signed-off-by: Francis Perron (Akrites SIRT) <francis@akrites.dev>
> ---
> Changes in v2:
> - Keep datablob_len unchanged and check both unsigned short bounds with
> check_add_overflow().
> - Use kzalloc_flex() for the trailing payload_data[] array.
> - Correct the attribution and sign-off trailers.
>
> The initial version was discussed off-list and is not publicly archived.
>
> security/keys/encrypted-keys/encrypted.c | 20 ++++++++++++++------
> 1 file changed, 14 insertions(+), 6 deletions(-)
>
> diff --git a/security/keys/encrypted-keys/encrypted.c b/security/keys/encrypted-keys/encrypted.c
> index 59cb77b237b3..e07092ea301a 100644
> --- a/security/keys/encrypted-keys/encrypted.c
> +++ b/security/keys/encrypted-keys/encrypted.c
> @@ -19,6 +19,7 @@
> #include <linux/parser.h>
> #include <linux/string.h>
> #include <linux/err.h>
> +#include <linux/overflow.h>
> #include <keys/user-type.h>
> #include <keys/trusted-type.h>
> #include <keys/encrypted-type.h>
> @@ -579,6 +580,7 @@ static struct encrypted_key_payload *encrypted_key_alloc(struct key *key,
> {
> struct encrypted_key_payload *epayload = NULL;
> unsigned short datablob_len;
> + unsigned short payload_totallen;
> unsigned short decrypted_datalen;
> unsigned short payload_datalen;
> unsigned int encrypted_datalen;
> @@ -632,16 +634,22 @@ static struct encrypted_key_payload *encrypted_key_alloc(struct key *key,
>
> encrypted_datalen = roundup(decrypted_datalen, blksize);
>
> - datablob_len = format_len + 1 + strlen(master_desc) + 1
> - + strlen(datalen) + 1 + ivsize + 1 + encrypted_datalen;
> + if (check_add_overflow(format_len + 1 + strlen(master_desc) + 1
> + + strlen(datalen) + 1 + ivsize + 1,
> + encrypted_datalen, &datablob_len))
> + return ERR_PTR(-EINVAL);
> +
> + if (check_add_overflow(datablob_len,
> + payload_datalen + HASH_SIZE + 1,
> + &payload_totallen))
> + return ERR_PTR(-EINVAL);
>
> - ret = key_payload_reserve(key, payload_datalen + datablob_len
> - + HASH_SIZE + 1);
> + ret = key_payload_reserve(key, payload_totallen);
> if (ret < 0)
> return ERR_PTR(ret);
>
> - epayload = kzalloc(sizeof(*epayload) + payload_datalen +
> - datablob_len + HASH_SIZE + 1, GFP_KERNEL);
> + epayload = kzalloc_flex(*epayload, payload_data, payload_totallen,
> + GFP_KERNEL);
> if (!epayload)
> return ERR_PTR(-ENOMEM);
>
> --
> 2.55.0
BR, Jarkko
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH v2] KEYS: encrypted: fix integer overflow of datablob_len
2026-08-28 1:59 ` Jarkko Sakkinen
@ 2026-09-09 15:30 ` Cen Zhang (Microsoft Security FORGE Labs)
0 siblings, 0 replies; 5+ messages in thread
From: Cen Zhang (Microsoft Security FORGE Labs) @ 2026-09-09 15:30 UTC (permalink / raw)
To: jarkko
Cc: AutonomousCodeSecurity, blbllhy, cenzhang, dhowells, francis,
gregkh, jmorris, kees, keyrings, linux-integrity, linux-kernel,
linux-security-module, paul, roberto.sassu, safford, serge, sirt,
zohar
Hi Jarkko,
Thanks for the review. I'll simplify the description using u16 and
U16_MAX, remove the organization names and the off-list note, and send
v3 shortly.
Thanks,
Cen
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-09-09 15:30 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-26 15:44 [PATCH v2] KEYS: encrypted: fix integer overflow of datablob_len Cen Zhang (Microsoft Security FORGE Labs)
2026-08-28 1:53 ` Jarkko Sakkinen
2026-08-28 1:55 ` Jarkko Sakkinen
2026-08-28 1:59 ` Jarkko Sakkinen
2026-09-09 15:30 ` Cen Zhang (Microsoft Security FORGE Labs)
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.