Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ceb73c12 authored by David Howells's avatar David Howells Committed by Linus Torvalds
Browse files

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: default avatarDavid Howells <dhowells@redhat.com>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent f5c66d70
Loading
Loading
Loading
Loading
+3 −3
Original line number Original line Diff line number Diff line
@@ -87,13 +87,13 @@ extern void key_type_put(struct key_type *ktype);
extern int __key_link_begin(struct key *keyring,
extern int __key_link_begin(struct key *keyring,
			    const struct key_type *type,
			    const struct key_type *type,
			    const char *description,
			    const char *description,
			    struct keyring_list **_prealloc);
			    unsigned long *_prealloc);
extern int __key_link_check_live_key(struct key *keyring, struct key *key);
extern int __key_link_check_live_key(struct key *keyring, struct key *key);
extern void __key_link(struct key *keyring, struct key *key,
extern void __key_link(struct key *keyring, struct key *key,
		       struct keyring_list **_prealloc);
		       unsigned long *_prealloc);
extern void __key_link_end(struct key *keyring,
extern void __key_link_end(struct key *keyring,
			   struct key_type *type,
			   struct key_type *type,
			   struct keyring_list *prealloc);
			   unsigned long prealloc);


extern key_ref_t __keyring_search_one(key_ref_t keyring_ref,
extern key_ref_t __keyring_search_one(key_ref_t keyring_ref,
				      const struct key_type *type,
				      const struct key_type *type,
+4 −4
Original line number Original line Diff line number Diff line
@@ -415,7 +415,7 @@ static int __key_instantiate_and_link(struct key *key,
				      size_t datalen,
				      size_t datalen,
				      struct key *keyring,
				      struct key *keyring,
				      struct key *authkey,
				      struct key *authkey,
				      struct keyring_list **_prealloc)
				      unsigned long *_prealloc)
{
{
	int ret, awaken;
	int ret, awaken;


@@ -481,7 +481,7 @@ int key_instantiate_and_link(struct key *key,
			     struct key *keyring,
			     struct key *keyring,
			     struct key *authkey)
			     struct key *authkey)
{
{
	struct keyring_list *prealloc;
	unsigned long prealloc;
	int ret;
	int ret;


	if (keyring) {
	if (keyring) {
@@ -526,7 +526,7 @@ int key_negate_and_link(struct key *key,
			struct key *keyring,
			struct key *keyring,
			struct key *authkey)
			struct key *authkey)
{
{
	struct keyring_list *prealloc;
	unsigned long prealloc;
	struct timespec now;
	struct timespec now;
	int ret, awaken, link_ret = 0;
	int ret, awaken, link_ret = 0;


@@ -814,7 +814,7 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
			       key_perm_t perm,
			       key_perm_t perm,
			       unsigned long flags)
			       unsigned long flags)
{
{
	struct keyring_list *prealloc;
	unsigned long prealloc;
	const struct cred *cred = current_cred();
	const struct cred *cred = current_cred();
	struct key_type *ktype;
	struct key_type *ktype;
	struct key *keyring, *key = NULL;
	struct key *keyring, *key = NULL;
+19 −12
Original line number Original line Diff line number Diff line
@@ -25,6 +25,8 @@
		(keyring)->payload.subscriptions,			\
		(keyring)->payload.subscriptions,			\
		rwsem_is_locked((struct rw_semaphore *)&(keyring)->sem)))
		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
 * When plumbing the depths of the key tree, this sets a hard limit
 * set on how deep we're willing to go.
 * 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.
 * 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,
int __key_link_begin(struct key *keyring, const struct key_type *type,
		     const char *description,
		     const char *description, unsigned long *_prealloc)
		     struct keyring_list **_prealloc)
	__acquires(&keyring->sem)
	__acquires(&keyring->sem)
{
{
	struct keyring_list *klist, *nklist;
	struct keyring_list *klist, *nklist;
	unsigned long prealloc;
	unsigned max;
	unsigned max;
	size_t size;
	size_t size;
	int loop, ret;
	int loop, ret;
@@ -746,6 +748,7 @@ int __key_link_begin(struct key *keyring, const struct key_type *type,


				/* note replacement slot */
				/* note replacement slot */
				klist->delkey = nklist->delkey = loop;
				klist->delkey = nklist->delkey = loop;
				prealloc = (unsigned long)nklist;
				goto done;
				goto done;
			}
			}
		}
		}
@@ -760,6 +763,7 @@ int __key_link_begin(struct key *keyring, const struct key_type *type,
	if (klist && klist->nkeys < klist->maxkeys) {
	if (klist && klist->nkeys < klist->maxkeys) {
		/* there's sufficient slack space to append directly */
		/* there's sufficient slack space to append directly */
		nklist = NULL;
		nklist = NULL;
		prealloc = KEY_LINK_FIXQUOTA;
	} else {
	} else {
		/* grow the key list */
		/* grow the key list */
		max = 4;
		max = 4;
@@ -794,8 +798,9 @@ int __key_link_begin(struct key *keyring, const struct key_type *type,
		nklist->keys[nklist->delkey] = NULL;
		nklist->keys[nklist->delkey] = NULL;
	}
	}


	prealloc = (unsigned long)nklist | KEY_LINK_FIXQUOTA;
done:
done:
	*_prealloc = nklist;
	*_prealloc = prealloc;
	kleave(" = 0");
	kleave(" = 0");
	return 0;
	return 0;


@@ -836,12 +841,12 @@ int __key_link_check_live_key(struct key *keyring, struct key *key)
 * combination.
 * combination.
 */
 */
void __key_link(struct key *keyring, struct key *key,
void __key_link(struct key *keyring, struct key *key,
		struct keyring_list **_prealloc)
		unsigned long *_prealloc)
{
{
	struct keyring_list *klist, *nklist;
	struct keyring_list *klist, *nklist;


	nklist = *_prealloc;
	nklist = (struct keyring_list *)(*_prealloc & ~KEY_LINK_FIXQUOTA);
	*_prealloc = NULL;
	*_prealloc = 0;


	kenter("%d,%d,%p", keyring->serial, key->serial, nklist);
	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.
 * Must be called with __key_link_begin() having being called.
 */
 */
void __key_link_end(struct key *keyring, struct key_type *type,
void __key_link_end(struct key *keyring, struct key_type *type,
		    struct keyring_list *prealloc)
		    unsigned long prealloc)
	__releases(&keyring->sem)
	__releases(&keyring->sem)
{
{
	BUG_ON(type == NULL);
	BUG_ON(type == NULL);
	BUG_ON(type->name == 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)
	if (type == &key_type_keyring)
		up_write(&keyring_serialise_link_sem);
		up_write(&keyring_serialise_link_sem);


	if (prealloc) {
	if (prealloc) {
		kfree(prealloc);
		if (prealloc & KEY_LINK_FIXQUOTA)
			key_payload_reserve(keyring,
			key_payload_reserve(keyring,
				    keyring->datalen - KEYQUOTA_LINK_BYTES);
					    keyring->datalen -
					    KEYQUOTA_LINK_BYTES);
		kfree((struct keyring_list *)(prealloc & ~KEY_LINK_FIXQUOTA));
	}
	}
	up_write(&keyring->sem);
	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)
int key_link(struct key *keyring, struct key *key)
{
{
	struct keyring_list *prealloc;
	unsigned long prealloc;
	int ret;
	int ret;


	key_check(keyring);
	key_check(keyring);
+1 −1
Original line number Original line Diff line number Diff line
@@ -352,8 +352,8 @@ static int construct_alloc_key(struct key_type *type,
			       struct key_user *user,
			       struct key_user *user,
			       struct key **_key)
			       struct key **_key)
{
{
	struct keyring_list *prealloc;
	const struct cred *cred = current_cred();
	const struct cred *cred = current_cred();
	unsigned long prealloc;
	struct key *key;
	struct key *key;
	key_ref_t key_ref;
	key_ref_t key_ref;
	int ret;
	int ret;