@@ -827,26 +827,28 @@ long keyctl_read_key(key_serial_t keyid, char __user *buffer, size_t buflen)
struct key *key;
key_ref_t key_ref;
long ret;
+ char *key_data = NULL;
+ size_t key_data_len;
/* find the key first */
key_ref = lookup_user_key(keyid, 0, 0);
if (IS_ERR(key_ref)) {
ret = -ENOKEY;
- goto error;
+ goto out;
}
key = key_ref_to_ptr(key_ref);
ret = key_read_state(key);
if (ret < 0)
- goto error2; /* Negatively instantiated */
+ goto key_put_out; /* Negatively instantiated */
/* see if we can read it directly */
ret = key_permission(key_ref, KEY_NEED_READ);
if (ret == 0)
goto can_read_key;
if (ret != -EACCES)
- goto error2;
+ goto key_put_out;
/* we can't; see if it's searchable from this process's keyrings
* - we automatically take account of the fact that it may be
@@ -854,75 +856,77 @@ long keyctl_read_key(key_serial_t keyid, char __user *buffer, size_t buflen)
*/
if (!is_key_possessed(key_ref)) {
ret = -EACCES;
- goto error2;
+ goto key_put_out;
}
/* the key is probably readable - now try to read it */
can_read_key:
if (!key->type->read) {
ret = -EOPNOTSUPP;
- goto error2;
+ goto key_put_out;
}
if (!buffer || !buflen) {
/* Get the key length from the read method */
ret = __keyctl_read_key(key, NULL, 0);
- } else {
-
- /*
- * Read the data with the semaphore held (since we might sleep)
- * to protect against the key being updated or revoked.
- *
- * Allocating a temporary buffer to hold the keys before
- * transferring them to user buffer to avoid potential
- * deadlock involving page fault and mmap_sem.
- */
- char *key_data = NULL;
- size_t key_data_len = buflen;
+ goto key_put_out;
+ }
- /*
- * When the user-supplied key length is larger than
- * PAGE_SIZE, we get the actual key length first before
- * allocating a right-sized key data buffer.
- */
- if (buflen <= PAGE_SIZE) {
-allocbuf:
+ /*
+ * Read the data with the semaphore held (since we might sleep)
+ * to protect against the key being updated or revoked.
+ *
+ * Allocating a temporary buffer to hold the keys before
+ * transferring them to user buffer to avoid potential
+ * deadlock involving page fault and mmap_sem.
+ *
+ * key_data_len = (buflen <= PAGE_SIZE)
+ * ? buflen : actual length of key data
+ *
+ * This prevents allocating arbitrary large buffer which can
+ * be much larger than the actual key length. In the latter case,
+ * at least 2 passes of this loop is required.
+ */
+ key_data_len = (buflen <= PAGE_SIZE) ? buflen : 0;
+ do {
+ if (key_data_len) {
key_data = kvmalloc(key_data_len, GFP_KERNEL);
if (!key_data) {
ret = -ENOMEM;
- goto error2;
+ goto key_put_out;
}
}
+
ret = __keyctl_read_key(key, key_data, key_data_len);
/*
- * Read methods will just return the required length
- * without any copying if the provided length isn't big
- * enough.
+ * Read methods will just return the required length without
+ * any copying if the provided length isn't large enough.
*/
- if (ret > 0 && ret <= buflen) {
- /*
- * The key may change (unlikely) in between 2
- * consecutive __keyctl_read_key() calls. We will
- * need to allocate a larger buffer and redo the key
- * read when key_data_len < ret <= buflen.
- */
- if (!key_data || unlikely(ret > key_data_len)) {
- if (unlikely(key_data))
- __kvzfree(key_data, key_data_len);
- key_data_len = ret;
- goto allocbuf;
- }
+ if (ret <= 0 || ret > buflen)
+ break;
- if (copy_to_user(buffer, key_data, ret))
- ret = -EFAULT;
+ /*
+ * The key may change (unlikely) in between 2 consecutive
+ * __keyctl_read_key() calls. In this case, we reallocate
+ * a larger buffer and redo the key read when
+ * key_data_len < ret <= buflen.
+ */
+ if (ret > key_data_len) {
+ if (unlikely(key_data))
+ __kvzfree(key_data, key_data_len);
+ key_data_len = ret;
+ continue; /* Allocate buffer */
}
- __kvzfree(key_data, key_data_len);
- }
-error2:
+ if (copy_to_user(buffer, key_data, ret))
+ ret = -EFAULT;
+ } while (0);
+ __kvzfree(key_data, key_data_len);
+
+key_put_out:
key_put(key);
-error:
+out:
return ret;
}