Skip to content

Commit 3239b6f

Browse files
ebiggersJames Morris
authored andcommitted
KEYS: return full count in keyring_read() if buffer is too small
Commit e645016 ("KEYS: fix writing past end of user-supplied buffer in keyring_read()") made keyring_read() stop corrupting userspace memory when the user-supplied buffer is too small. However it also made the return value in that case be the short buffer size rather than the size required, yet keyctl_read() is actually documented to return the size required. Therefore, switch it over to the documented behavior. Note that for now we continue to have it fill the short buffer, since it did that before (pre-v3.13) and dump_key_tree_aux() in keyutils arguably relies on it. Fixes: e645016 ("KEYS: fix writing past end of user-supplied buffer in keyring_read()") Reported-by: Ben Hutchings <ben@decadent.org.uk> Cc: <stable@vger.kernel.org> # v3.13+ Signed-off-by: Eric Biggers <ebiggers@google.com> Signed-off-by: David Howells <dhowells@redhat.com> Reviewed-by: James Morris <james.l.morris@oracle.com> Signed-off-by: James Morris <james.l.morris@oracle.com>
1 parent 3a99df9 commit 3239b6f

1 file changed

Lines changed: 19 additions & 20 deletions

File tree

security/keys/keyring.c

Lines changed: 19 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -459,34 +459,33 @@ static long keyring_read(const struct key *keyring,
459459
char __user *buffer, size_t buflen)
460460
{
461461
struct keyring_read_iterator_context ctx;
462-
unsigned long nr_keys;
463-
int ret;
462+
long ret;
464463

465464
kenter("{%d},,%zu", key_serial(keyring), buflen);
466465

467466
if (buflen & (sizeof(key_serial_t) - 1))
468467
return -EINVAL;
469468

470-
nr_keys = keyring->keys.nr_leaves_on_tree;
471-
if (nr_keys == 0)
472-
return 0;
473-
474-
/* Calculate how much data we could return */
475-
if (!buffer || !buflen)
476-
return nr_keys * sizeof(key_serial_t);
477-
478-
/* Copy the IDs of the subscribed keys into the buffer */
479-
ctx.buffer = (key_serial_t __user *)buffer;
480-
ctx.buflen = buflen;
481-
ctx.count = 0;
482-
ret = assoc_array_iterate(&keyring->keys, keyring_read_iterator, &ctx);
483-
if (ret < 0) {
484-
kleave(" = %d [iterate]", ret);
485-
return ret;
469+
/* Copy as many key IDs as fit into the buffer */
470+
if (buffer && buflen) {
471+
ctx.buffer = (key_serial_t __user *)buffer;
472+
ctx.buflen = buflen;
473+
ctx.count = 0;
474+
ret = assoc_array_iterate(&keyring->keys,
475+
keyring_read_iterator, &ctx);
476+
if (ret < 0) {
477+
kleave(" = %ld [iterate]", ret);
478+
return ret;
479+
}
486480
}
487481

488-
kleave(" = %zu [ok]", ctx.count);
489-
return ctx.count;
482+
/* Return the size of the buffer needed */
483+
ret = keyring->keys.nr_leaves_on_tree * sizeof(key_serial_t);
484+
if (ret <= buflen)
485+
kleave("= %ld [ok]", ret);
486+
else
487+
kleave("= %ld [buffer too small]", ret);
488+
return ret;
490489
}
491490

492491
/*

0 commit comments

Comments
 (0)