Skip to contentSkip to conversation

KEYS: encrypted: fix integer overflow of datablob_len

PATCH In review Cen Zhang 3 participants 4 messages Sep 9 – Sep 18

Subsystems (5)
lore
Share & tools
lore

Cen Zhang

author
#0 lore raw

PATCH v3 1/1 KEYS: encrypted: fix integer overflow of datablob_len

encrypted_key_alloc() stores datablob_len in a u16. It is computed from multiple string and payload lengths. If the result exceeds U16_MAX, the assignment truncates the allocation size. KASAN reports a 32760-byte slab-out-of-bounds write when __ekey_init() copies the master key description into the undersized buffer.

The total payload length stored in key->datalen is also a u16. Use check_add_overflow() to reject values that do not fit either destination, and use kzalloc_flex() for the flexible-array allocation.

Fixes 7e70cb497850 ("keys: add new key-type encrypted")
Assisted-by GitHub-Copilot:claude-opus-4.6

---
Changes in v3:
- Describe the bounds using u16 and U16_MAX and simplify the commit message.
- Include the KASAN-reported write size.
- Remove organization names from the author and sign-off identities.
- Remove the unnecessary off-list discussion note.
- Use [email protected].
- No code changes.

 security/keys/encrypted-keys/encrypted.c | 20 ++++++++++++++------
 1 file changed, 14 insertions(+), 6 deletions(-)
security/keys/encrypted-keys/encrypted.c +14 -6
index 59cb77b237b3..e07092ea301a 100644
@@ -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);
 

base-commit: 893e11787f78e43b534e252249ac3fff4d1333f8

Signature · 1 line
2.55.0
maintainer Reviewed-by on v3 1/1
#1 lore raw
Quoted 79 lines · Cen Zhang

encrypted_key_alloc() stores datablob_len in a u16. It is computed from multiple string and payload lengths. If the result exceeds U16_MAX, the assignment truncates the allocation size. KASAN reports a 32760-byte slab-out-of-bounds write when __ekey_init() copies the master key description into the undersized buffer.

The total payload length stored in key->datalen is also a u16. Use check_add_overflow() to reject values that do not fit either destination, and use kzalloc_flex() for the flexible-array allocation.

Fixes 7e70cb497850 ("keys: add new key-type encrypted")
Assisted-by GitHub-Copilot:claude-opus-4.6

---
Changes in v3:
- Describe the bounds using u16 and U16_MAX and simplify the commit message.
- Include the KASAN-reported write size.
- Remove organization names from the author and sign-off identities.
- Remove the unnecessary off-list discussion note.
- Use [email protected].
- No code changes.

 security/keys/encrypted-keys/encrypted.c | 20 ++++++++++++++------
 1 file changed, 14 insertions(+), 6 deletions(-)
security/keys/encrypted-keys/encrypted.c +14 -6
index 59cb77b237b3..e07092ea301a 100644
@@ -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);
 
Quoted 3 lines

base-commit: 893e11787f78e43b534e252249ac3fff4d1333f8

Signature · 1 line
2.55.0

Thanks!

BR, Jarkko

Tested-by on v3 1/1
#2 lore raw
In reply to Cen Zhang
Quoted 10 lines · Cen Zhang

encrypted_key_alloc() stores datablob_len in a u16. It is computed from multiple string and payload lengths. If the result exceeds U16_MAX, the assignment truncates the allocation size. KASAN reports a 32760-byte slab-out-of-bounds write when __ekey_init() copies the master key description into the undersized buffer.

The total payload length stored in key->datalen is also a u16. Use check_add_overflow() to reject values that do not fit either destination, and use kzalloc_flex() for the flexible-array allocation.

Quoted 6 lines
Fixes 7e70cb497850 ("keys: add new key-type encrypted")
Assisted-by GitHub-Copilot:claude-opus-4.6

System: ppc64le LPAR (IBM POWER), Linux 7.3-rc2

Quoted 64 lines

---
Changes in v3:
- Describe the bounds using u16 and U16_MAX and simplify the commit message.
- Include the KASAN-reported write size.
- Remove organization names from the author and sign-off identities.
- Remove the unnecessary off-list discussion note.
- Use [email protected].
- No code changes.

  security/keys/encrypted-keys/encrypted.c | 20 ++++++++++++++------
  1 file changed, 14 insertions(+), 6 deletions(-)
security/keys/encrypted-keys/encrypted.c +14 -6
index 59cb77b237b3..e07092ea301a 100644
@@ -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);
 

base-commit: 893e11787f78e43b534e252249ac3fff4d1333f8

Signature · 2 lines
Thanks and Regards
R.Nageswara Sastry
maintainer Reviewed-by
#3 lore raw
Quoted 79 lines · R Nageswara Sastry
Quoted 10 lines · Cen Zhang

encrypted_key_alloc() stores datablob_len in a u16. It is computed from multiple string and payload lengths. If the result exceeds U16_MAX, the assignment truncates the allocation size. KASAN reports a 32760-byte slab-out-of-bounds write when __ekey_init() copies the master key description into the undersized buffer.

The total payload length stored in key->datalen is also a u16. Use check_add_overflow() to reject values that do not fit either destination, and use kzalloc_flex() for the flexible-array allocation.

Quoted 6 lines
Fixes 7e70cb497850 ("keys: add new key-type encrypted")
Assisted-by GitHub-Copilot:claude-opus-4.6

System: ppc64le LPAR (IBM POWER), Linux 7.3-rc2

Quoted 58 lines

---
Changes in v3:
- Describe the bounds using u16 and U16_MAX and simplify the commit message.
- Include the KASAN-reported write size.
- Remove organization names from the author and sign-off identities.
- Remove the unnecessary off-list discussion note.
- Use [email protected].
- No code changes.

  security/keys/encrypted-keys/encrypted.c | 20 ++++++++++++++------
  1 file changed, 14 insertions(+), 6 deletions(-)
security/keys/encrypted-keys/encrypted.c +14 -6
index 59cb77b237b3..e07092ea301a 100644
@@ -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);
 
Quoted 6 lines
Quoted 1 line

base-commit: 893e11787f78e43b534e252249ac3fff4d1333f8

Signature · 2 lines
Thanks and Regards
R.Nageswara Sastry

BR, Jarkko