summaryrefslogtreecommitdiffstats
path: root/security/keys/keyring.c
diff options
context:
space:
mode:
authorDavid Howells <dhowells@redhat.com>2011-01-25 16:34:28 +0000
committerLinus Torvalds <torvalds@linux-foundation.org>2011-01-26 08:58:20 +1000
commitceb73c12047b8d543570b23353e7848eb7c540a1 (patch)
treea637dc88d418be1b705a66bea375af955bd14e22 /security/keys/keyring.c
parentf5c66d70ac2a9016a7ad481bd37e39afd7dd7369 (diff)
KEYS: Fix __key_link_end() quota fixup on error
Fix __key_link_end()'s attempt to fix up the quota if an error occurs. There are two erroneous cases: Firstly, we always decrease the quota if the preallocated replacement keyring needs cleaning up, irrespective of whether or not we should (we may have replaced a pointer rather than adding another pointer). Secondly, we never clean up the quota if we added a pointer without the keyring storage being extended (we allocate multiple pointers at a time, even if we're not going to use them all immediately). We handle this by setting the bottom bit of the preallocation pointer in __key_link_begin() to indicate that the quota needs fixing up, which is then passed to __key_link() (which clears the whole thing) and __key_link_end(). Signed-off-by: David Howells <dhowells@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'security/keys/keyring.c')
-rw-r--r--security/keys/keyring.c31
1 files changed, 19 insertions, 12 deletions
diff --git a/security/keys/keyring.c b/security/keys/keyring.c
index 92024ed12e0..5620f084ded 100644
--- a/security/keys/keyring.c
+++ b/security/keys/keyring.c
@@ -25,6 +25,8 @@
(keyring)->payload.subscriptions, \
rwsem_is_locked((struct rw_semaphore *)&(keyring)->sem)))
+#define KEY_LINK_FIXQUOTA 1UL
+
/*
* When plumbing the depths of the key tree, this sets a hard limit
* set on how deep we're willing to go.
@@ -699,11 +701,11 @@ static void keyring_unlink_rcu_disposal(struct rcu_head *rcu)
* Preallocate memory so that a key can be linked into to a keyring.
*/
int __key_link_begin(struct key *keyring, const struct key_type *type,
- const char *description,
- struct keyring_list **_prealloc)
+ const char *description, unsigned long *_prealloc)
__acquires(&keyring->sem)
{
struct keyring_list *klist, *nklist;
+ unsigned long prealloc;
unsigned max;
size_t size;
int loop, ret;
@@ -746,6 +748,7 @@ int __key_link_begin(struct key *keyring, const struct key_type *type,
/* note replacement slot */
klist->delkey = nklist->delkey = loop;
+ prealloc = (unsigned long)nklist;
goto done;
}
}
@@ -760,6 +763,7 @@ int __key_link_begin(struct key *keyring, const struct key_type *type,
if (klist && klist->nkeys < klist->maxkeys) {
/* there's sufficient slack space to append directly */
nklist = NULL;
+ prealloc = KEY_LINK_FIXQUOTA;
} else {
/* grow the key list */
max = 4;
@@ -794,8 +798,9 @@ int __key_link_begin(struct key *keyring, const struct key_type *type,
nklist->keys[nklist->delkey] = NULL;
}
+ prealloc = (unsigned long)nklist | KEY_LINK_FIXQUOTA;
done:
- *_prealloc = nklist;
+ *_prealloc = prealloc;
kleave(" = 0");
return 0;
@@ -836,12 +841,12 @@ int __key_link_check_live_key(struct key *keyring, struct key *key)
* combination.
*/
void __key_link(struct key *keyring, struct key *key,
- struct keyring_list **_prealloc)
+ unsigned long *_prealloc)
{
struct keyring_list *klist, *nklist;
- nklist = *_prealloc;
- *_prealloc = NULL;
+ nklist = (struct keyring_list *)(*_prealloc & ~KEY_LINK_FIXQUOTA);
+ *_prealloc = 0;
kenter("%d,%d,%p", keyring->serial, key->serial, nklist);
@@ -881,20 +886,22 @@ void __key_link(struct key *keyring, struct key *key,
* Must be called with __key_link_begin() having being called.
*/
void __key_link_end(struct key *keyring, struct key_type *type,
- struct keyring_list *prealloc)
+ unsigned long prealloc)
__releases(&keyring->sem)
{
BUG_ON(type == NULL);
BUG_ON(type->name == NULL);
- kenter("%d,%s,%p", keyring->serial, type->name, prealloc);
+ kenter("%d,%s,%lx", keyring->serial, type->name, prealloc);
if (type == &key_type_keyring)
up_write(&keyring_serialise_link_sem);
if (prealloc) {
- kfree(prealloc);
- key_payload_reserve(keyring,
- keyring->datalen - KEYQUOTA_LINK_BYTES);
+ if (prealloc & KEY_LINK_FIXQUOTA)
+ key_payload_reserve(keyring,
+ keyring->datalen -
+ KEYQUOTA_LINK_BYTES);
+ kfree((struct keyring_list *)(prealloc & ~KEY_LINK_FIXQUOTA));
}
up_write(&keyring->sem);
}
@@ -921,7 +928,7 @@ void __key_link_end(struct key *keyring, struct key_type *type,
*/
int key_link(struct key *keyring, struct key *key)
{
- struct keyring_list *prealloc;
+ unsigned long prealloc;
int ret;
key_check(keyring);