* [RFC PATCH 1/6] keys: prevent quota overflow and widen quotalen and datalen to 32 bits
[not found] <20261016215326.4110064-1-jarkko@kernel.org>
@ 2026-10-16 21:52 ` Jarkko Sakkinen
2026-10-17 5:28 ` Eric Biggers
0 siblings, 1 reply; 3+ messages in thread
From: Jarkko Sakkinen @ 2026-10-16 21:52 UTC (permalink / raw)
To: David Howells
Cc: keyrings, Linus Torvalds, Jarkko Sakkinen, Paulo Alcantara,
Namjae Jeon, Ronnie Sahlberg, Shyam Prasad N, Tom Talpey,
Bharath SM, Paul Moore, James Morris, Serge E. Hallyn,
open list:COMMON INTERNET FILE SYSTEM CLIENT (CIFS and SMB3),
moderated list:COMMON INTERNET FILE SYSTEM CLIENT (CIFS and SMB3),
open list, open list:SECURITY SUBSYSTEM
In struct key, quotalen and datalen are 16-bit unsigned shorts, limiting
payload and quota tracking to 64 KiB. With large descriptions or payload
updates, addition and difference calculations can truncate or overflow,
corrupting user quota accounting.
Additionally, existing quota checks rely on undefined signed overflow
checks (e.g. user->qnbytes + quotalen < user->qnbytes) and do not guard
against user->qnkeys or user->qnbytes reaching INT_MAX.
Widen quotalen and datalen to unsigned 32-bit integers in struct key.
Validate description lengths against INT_MAX in key_alloc(), and check
quota headroom using safe subtraction across key_alloc(),
key_payload_reserve(), and keyctl_chown_key().
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Assisted-by: LLM
Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
---
fs/smb/client/cifsacl.c | 6 +++---
include/linux/key.h | 4 ++--
security/keys/key.c | 25 ++++++++++++++++++++-----
security/keys/keyctl.c | 9 +++++----
4 files changed, 30 insertions(+), 14 deletions(-)
diff --git a/fs/smb/client/cifsacl.c b/fs/smb/client/cifsacl.c
index c5e47a835f99..de2acc7234be 100644
--- a/fs/smb/client/cifsacl.c
+++ b/fs/smb/client/cifsacl.c
@@ -385,7 +385,7 @@ id_to_sid(unsigned int cid, uint sidtype, struct smb_sid *ssid)
goto out_revert_creds;
} else if (sidkey->datalen < CIFS_SID_BASE_SIZE) {
rc = smb_EIO1(smb_eio_trace_malformed_sid_key, sidkey->datalen);
- cifs_dbg(FYI, "%s: Downcall contained malformed key (datalen=%hu)\n",
+ cifs_dbg(FYI, "%s: Downcall contained malformed key (datalen=%u)\n",
__func__, sidkey->datalen);
goto invalidate_key;
}
@@ -403,7 +403,7 @@ id_to_sid(unsigned int cid, uint sidtype, struct smb_sid *ssid)
if (ksid_size > sidkey->datalen) {
rc = smb_EIO2(smb_eio_trace_malformed_ksid_key,
ksid_size, sidkey->datalen);
- cifs_dbg(FYI, "%s: Downcall contained malformed key (datalen=%hu, ksid_size=%u)\n",
+ cifs_dbg(FYI, "%s: Downcall contained malformed key (datalen=%u, ksid_size=%u)\n",
__func__, sidkey->datalen, ksid_size);
goto invalidate_key;
}
@@ -498,7 +498,7 @@ sid_to_id(struct cifs_sb_info *cifs_sb, struct smb_sid *psid,
*/
BUILD_BUG_ON(sizeof(uid_t) != sizeof(gid_t));
if (sidkey->datalen != sizeof(uid_t)) {
- cifs_dbg(FYI, "%s: Downcall contained malformed key (datalen=%hu)\n",
+ cifs_dbg(FYI, "%s: Downcall contained malformed key (datalen=%u)\n",
__func__, sidkey->datalen);
key_invalidate(sidkey);
goto out_key_put;
diff --git a/include/linux/key.h b/include/linux/key.h
index e40b5267658f..26139c3adf62 100644
--- a/include/linux/key.h
+++ b/include/linux/key.h
@@ -216,8 +216,8 @@ struct key {
kuid_t uid;
kgid_t gid;
key_perm_t perm; /* access permissions */
- unsigned short quotalen; /* length added to quota */
- unsigned short datalen; /* payload data length
+ unsigned int quotalen; /* length added to quota */
+ unsigned int datalen; /* payload data length
* - may not match RCU dereferenced payload
* - payload should contain own length
*/
diff --git a/security/keys/key.c b/security/keys/key.c
index 6ebb8691551f..144745684334 100644
--- a/security/keys/key.c
+++ b/security/keys/key.c
@@ -245,6 +245,10 @@ struct key *key_alloc(struct key_type *type, const char *desc,
}
desclen = strlen(desc);
+ if (desclen >= INT_MAX || type->def_datalen > INT_MAX - desclen - 1) {
+ key = ERR_PTR(-EINVAL);
+ goto error;
+ }
quotalen = desclen + 1 + type->def_datalen;
/* get hold of the key tracking for this user */
@@ -261,10 +265,14 @@ struct key *key_alloc(struct key_type *type, const char *desc,
key_quota_root_maxbytes : key_quota_maxbytes;
spin_lock_irqsave(&user->lock, irqflags);
+ if (user->qnkeys == INT_MAX ||
+ quotalen > INT_MAX - user->qnbytes)
+ goto no_quota;
+
if (!(flags & KEY_ALLOC_QUOTA_OVERRUN)) {
if (user->qnkeys + 1 > maxkeys ||
- user->qnbytes + quotalen > maxbytes ||
- user->qnbytes + quotalen < user->qnbytes)
+ quotalen > maxbytes ||
+ user->qnbytes > maxbytes - quotalen)
goto no_quota;
}
@@ -373,11 +381,16 @@ EXPORT_SYMBOL(key_alloc);
*/
int key_payload_reserve(struct key *key, size_t datalen)
{
- int delta = (int)datalen - key->datalen;
+ int delta;
int ret = 0;
key_check(key);
+ if (datalen > INT_MAX)
+ return -EINVAL;
+
+ delta = (int)datalen - (int)key->datalen;
+
/* contemplate the quota adjustment */
if (delta != 0 && test_bit(KEY_FLAG_IN_QUOTA, &key->flags)) {
unsigned maxbytes = uid_eq(key->user->uid, GLOBAL_ROOT_UID) ?
@@ -387,8 +400,10 @@ int key_payload_reserve(struct key *key, size_t datalen)
spin_lock_irqsave(&key->user->lock, flags);
if (delta > 0 &&
- (key->user->qnbytes + delta > maxbytes ||
- key->user->qnbytes + delta < key->user->qnbytes)) {
+ (delta > INT_MAX - key->user->qnbytes ||
+ delta > INT_MAX - key->quotalen ||
+ delta > maxbytes ||
+ key->user->qnbytes > maxbytes - delta)) {
ret = -EDQUOT;
}
else {
diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c
index 5a5efa26ed0e..6b528faab1a7 100644
--- a/security/keys/keyctl.c
+++ b/security/keys/keyctl.c
@@ -1014,10 +1014,11 @@ long keyctl_chown_key(key_serial_t id, uid_t user, gid_t group)
key_quota_root_maxbytes : key_quota_maxbytes;
spin_lock_irqsave(&newowner->lock, flags);
- if (newowner->qnkeys + 1 > maxkeys ||
- newowner->qnbytes + key->quotalen > maxbytes ||
- newowner->qnbytes + key->quotalen <
- newowner->qnbytes)
+ if (newowner->qnkeys == INT_MAX ||
+ newowner->qnkeys + 1 > maxkeys ||
+ key->quotalen > INT_MAX - newowner->qnbytes ||
+ key->quotalen > maxbytes ||
+ newowner->qnbytes > maxbytes - key->quotalen)
goto quota_overrun;
newowner->qnkeys++;
--
2.47.3
^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [RFC PATCH 1/6] keys: prevent quota overflow and widen quotalen and datalen to 32 bits
2026-10-16 21:52 ` [RFC PATCH 1/6] keys: prevent quota overflow and widen quotalen and datalen to 32 bits Jarkko Sakkinen
@ 2026-10-17 5:28 ` Eric Biggers
2026-10-17 16:48 ` Jarkko Sakkinen
0 siblings, 1 reply; 3+ messages in thread
From: Eric Biggers @ 2026-10-17 5:28 UTC (permalink / raw)
To: Jarkko Sakkinen
Cc: David Howells, keyrings, Linus Torvalds, Paulo Alcantara,
Namjae Jeon, Ronnie Sahlberg, Shyam Prasad N, Tom Talpey,
Bharath SM, Paul Moore, James Morris, Serge E. Hallyn,
open list:COMMON INTERNET FILE SYSTEM CLIENT (CIFS and SMB3),
moderated list:COMMON INTERNET FILE SYSTEM CLIENT (CIFS and SMB3),
open list, open list:SECURITY SUBSYSTEM
On Sat, Oct 17, 2026 at 12:52:59AM +0300, Jarkko Sakkinen wrote:
> @@ -245,6 +245,10 @@ struct key *key_alloc(struct key_type *type, const char *desc,
> }
>
> desclen = strlen(desc);
> + if (desclen >= INT_MAX || type->def_datalen > INT_MAX - desclen - 1) {
> + key = ERR_PTR(-EINVAL);
> + goto error;
> + }
> quotalen = desclen + 1 + type->def_datalen;
This should be tightened further to 'desclen > U16_MAX', since desclen
is assigned to a u16 later in the function.
- Eric
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [RFC PATCH 1/6] keys: prevent quota overflow and widen quotalen and datalen to 32 bits
2026-10-17 5:28 ` Eric Biggers
@ 2026-10-17 16:48 ` Jarkko Sakkinen
0 siblings, 0 replies; 3+ messages in thread
From: Jarkko Sakkinen @ 2026-10-17 16:48 UTC (permalink / raw)
To: Eric Biggers
Cc: David Howells, keyrings, Linus Torvalds, Paulo Alcantara,
Namjae Jeon, Ronnie Sahlberg, Shyam Prasad N, Tom Talpey,
Bharath SM, Paul Moore, James Morris, Serge E. Hallyn,
open list:COMMON INTERNET FILE SYSTEM CLIENT (CIFS and SMB3),
moderated list:COMMON INTERNET FILE SYSTEM CLIENT (CIFS and SMB3),
open list, open list:SECURITY SUBSYSTEM
On Fri, Oct 16, 2026 at 10:28:44PM -0700, Eric Biggers wrote:
> On Sat, Oct 17, 2026 at 12:52:59AM +0300, Jarkko Sakkinen wrote:
> > @@ -245,6 +245,10 @@ struct key *key_alloc(struct key_type *type, const char *desc,
> > }
> >
> > desclen = strlen(desc);
> > + if (desclen >= INT_MAX || type->def_datalen > INT_MAX - desclen - 1) {
> > + key = ERR_PTR(-EINVAL);
> > + goto error;
> > + }
> > quotalen = desclen + 1 + type->def_datalen;
>
> This should be tightened further to 'desclen > U16_MAX', since desclen
> is assigned to a u16 later in the function.
Thanks for the remark. I think you are right and fits to what I've been
working on today.
Cutting hairs perhaps but I have really hard time to comprehend this as
a scoped bug fix.
Today morning I tried different things and what made sense to me is
instead think the bug scope as three separate integer overflows and and
one truncation bug.
The net result is here already:
https://git.kernel.org/pub/scm/linux/kernel/git/jarkko/linux-tpmdd.git/log/?h=keys-rhashtable
I decided that I retain all fixes as part of the series but eagerly
apply and drop them series of course. Split series would be a mess.
And they are any way part of the staircase from "now" to "then".
>
> - Eric
Br, Jarkko
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-10-17 16:48 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
[not found] <20261016215326.4110064-1-jarkko@kernel.org>
2026-10-16 21:52 ` [RFC PATCH 1/6] keys: prevent quota overflow and widen quotalen and datalen to 32 bits Jarkko Sakkinen
2026-10-17 5:28 ` Eric Biggers
2026-10-17 16:48 ` Jarkko Sakkinen
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox