Skip to content

Commit 31d5a79

Browse files
committed
KEYS: Do LRU discard in full keyrings
Do an LRU discard in keyrings that are full rather than returning ENFILE. To perform this, a time_t is added to the key struct and updated by the creation of a link to a key and by a key being found as the result of a search. At the completion of a successful search, the keyrings in the path between the root of the search and the first found link to it also have their last-used times updated. Note that discarding a link to a key from a keyring does not necessarily destroy the key as there may be references held by other places. An alternate discard method that might suffice is to perform FIFO discard from the keyring, using the spare 2-byte hole in the keylist header as the index of the next link to be discarded. This is useful when using a keyring as a cache for DNS results or foreign filesystem IDs. This can be tested by the following. As root do: echo 1000 >/proc/sys/kernel/keys/root_maxkeys kr=`keyctl newring foo @s` for ((i=0; i<2000; i++)); do keyctl add user a$i a $kr; done Without this patch ENFILE should be reported when the keyring fills up. With this patch, the keyring discards keys in an LRU fashion. Note that the stored LRU time has a granularity of 1s. After doing this, /proc/key-users can be observed and should show that most of the 2000 keys have been discarded: [root@andromeda ~]# cat /proc/key-users 0: 517 516/516 513/1000 5249/20000 The "513/1000" here is the number of quota-accounted keys present for this user out of the maximum permitted. In /proc/keys, the keyring shows the number of keys it has and the number of slots it has allocated: [root@andromeda ~]# grep foo /proc/keys 200c64c4 I--Q-- 1 perm 3b3f0000 0 0 keyring foo: 509/509 The maximum is (PAGE_SIZE - header) / key pointer size. That's typically 509 on a 64-bit system and 1020 on a 32-bit system. Signed-off-by: David Howells <dhowells@redhat.com>
1 parent 233e473 commit 31d5a79

File tree

3 files changed

+43
-7
lines changed

3 files changed

+43
-7
lines changed

include/linux/key.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ struct key {
136136
time_t expiry; /* time at which key expires (or 0) */
137137
time_t revoked_at; /* time at which key was revoked */
138138
};
139+
time_t last_used_at; /* last time used for LRU keyring discard */
139140
uid_t uid;
140141
gid_t gid;
141142
key_perm_t perm; /* access permissions */

security/keys/keyring.c

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@
3030
(klist)->keys[index], \
3131
rwsem_is_locked((struct rw_semaphore *)&(keyring)->sem)))
3232

33+
#define MAX_KEYRING_LINKS \
34+
min_t(size_t, USHRT_MAX - 1, \
35+
((PAGE_SIZE - sizeof(struct keyring_list)) / sizeof(struct key *)))
36+
3337
#define KEY_LINK_FIXQUOTA 1UL
3438

3539
/*
@@ -319,6 +323,8 @@ key_ref_t keyring_search_aux(key_ref_t keyring_ref,
319323
bool no_state_check)
320324
{
321325
struct {
326+
/* Need a separate keylist pointer for RCU purposes */
327+
struct key *keyring;
322328
struct keyring_list *keylist;
323329
int kix;
324330
} stack[KEYRING_SEARCH_MAX_DEPTH];
@@ -451,6 +457,7 @@ key_ref_t keyring_search_aux(key_ref_t keyring_ref,
451457
continue;
452458

453459
/* stack the current position */
460+
stack[sp].keyring = keyring;
454461
stack[sp].keylist = keylist;
455462
stack[sp].kix = kix;
456463
sp++;
@@ -466,6 +473,7 @@ key_ref_t keyring_search_aux(key_ref_t keyring_ref,
466473
if (sp > 0) {
467474
/* resume the processing of a keyring higher up in the tree */
468475
sp--;
476+
keyring = stack[sp].keyring;
469477
keylist = stack[sp].keylist;
470478
kix = stack[sp].kix + 1;
471479
goto ascend;
@@ -477,6 +485,10 @@ key_ref_t keyring_search_aux(key_ref_t keyring_ref,
477485
/* we found a viable match */
478486
found:
479487
atomic_inc(&key->usage);
488+
key->last_used_at = now.tv_sec;
489+
keyring->last_used_at = now.tv_sec;
490+
while (sp > 0)
491+
stack[--sp].keyring->last_used_at = now.tv_sec;
480492
key_check(key);
481493
key_ref = make_key_ref(key, possessed);
482494
error_2:
@@ -558,6 +570,8 @@ key_ref_t __keyring_search_one(key_ref_t keyring_ref,
558570

559571
found:
560572
atomic_inc(&key->usage);
573+
keyring->last_used_at = key->last_used_at =
574+
current_kernel_time().tv_sec;
561575
rcu_read_unlock();
562576
return make_key_ref(key, possessed);
563577
}
@@ -611,6 +625,7 @@ struct key *find_keyring_by_name(const char *name, bool skip_perm_check)
611625
* (ie. it has a zero usage count) */
612626
if (!atomic_inc_not_zero(&keyring->usage))
613627
continue;
628+
keyring->last_used_at = current_kernel_time().tv_sec;
614629
goto out;
615630
}
616631
}
@@ -734,8 +749,9 @@ int __key_link_begin(struct key *keyring, const struct key_type *type,
734749
struct keyring_list *klist, *nklist;
735750
unsigned long prealloc;
736751
unsigned max;
752+
time_t lowest_lru;
737753
size_t size;
738-
int loop, ret;
754+
int loop, lru, ret;
739755

740756
kenter("%d,%s,%s,", key_serial(keyring), type->name, description);
741757

@@ -756,7 +772,9 @@ int __key_link_begin(struct key *keyring, const struct key_type *type,
756772
klist = rcu_dereference_locked_keyring(keyring);
757773

758774
/* see if there's a matching key we can displace */
775+
lru = -1;
759776
if (klist && klist->nkeys > 0) {
777+
lowest_lru = TIME_T_MAX;
760778
for (loop = klist->nkeys - 1; loop >= 0; loop--) {
761779
struct key *key = rcu_deref_link_locked(klist, loop,
762780
keyring);
@@ -770,9 +788,23 @@ int __key_link_begin(struct key *keyring, const struct key_type *type,
770788
prealloc = 0;
771789
goto done;
772790
}
791+
if (key->last_used_at < lowest_lru) {
792+
lowest_lru = key->last_used_at;
793+
lru = loop;
794+
}
773795
}
774796
}
775797

798+
/* If the keyring is full then do an LRU discard */
799+
if (klist &&
800+
klist->nkeys == klist->maxkeys &&
801+
klist->maxkeys >= MAX_KEYRING_LINKS) {
802+
kdebug("LRU discard %d\n", lru);
803+
klist->delkey = lru;
804+
prealloc = 0;
805+
goto done;
806+
}
807+
776808
/* check that we aren't going to overrun the user's quota */
777809
ret = key_payload_reserve(keyring,
778810
keyring->datalen + KEYQUOTA_LINK_BYTES);
@@ -786,15 +818,14 @@ int __key_link_begin(struct key *keyring, const struct key_type *type,
786818
} else {
787819
/* grow the key list */
788820
max = 4;
789-
if (klist)
821+
if (klist) {
790822
max += klist->maxkeys;
823+
if (max > MAX_KEYRING_LINKS)
824+
max = MAX_KEYRING_LINKS;
825+
BUG_ON(max <= klist->maxkeys);
826+
}
791827

792-
ret = -ENFILE;
793-
if (max > USHRT_MAX - 1)
794-
goto error_quota;
795828
size = sizeof(*klist) + sizeof(struct key *) * max;
796-
if (size > PAGE_SIZE)
797-
goto error_quota;
798829

799830
ret = -ENOMEM;
800831
nklist = kmalloc(size, GFP_KERNEL);
@@ -873,6 +904,8 @@ void __key_link(struct key *keyring, struct key *key,
873904
klist = rcu_dereference_locked_keyring(keyring);
874905

875906
atomic_inc(&key->usage);
907+
keyring->last_used_at = key->last_used_at =
908+
current_kernel_time().tv_sec;
876909

877910
/* there's a matching key we can displace or an empty slot in a newly
878911
* allocated list we can fill */

security/keys/process_keys.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -732,6 +732,8 @@ key_ref_t lookup_user_key(key_serial_t id, unsigned long lflags,
732732
if (ret < 0)
733733
goto invalid_key;
734734

735+
key->last_used_at = current_kernel_time().tv_sec;
736+
735737
error:
736738
put_cred(cred);
737739
return key_ref;

0 commit comments

Comments
 (0)