aboutsummaryrefslogtreecommitdiffstats
path: root/security/keys
diff options
context:
space:
mode:
authorDavid Howells <dhowells@redhat.com>2009-09-14 12:26:13 -0400
committerJames Morris <jmorris@namei.org>2009-09-14 19:11:02 -0400
commitc08ef808ef24df32e25fbd949fe5310172f3c408 (patch)
tree12bae6fd48e1cdcc1b792c221376c727d9472cc6 /security/keys
parent5c84342a3e147a23752276650340801c237d0e56 (diff)
KEYS: Fix garbage collector
Fix a number of problems with the new key garbage collector: (1) A rogue semicolon in keyring_gc() was causing the initial count of dead keys to be miscalculated. (2) A missing return in keyring_gc() meant that under certain circumstances, the keyring semaphore would be unlocked twice. (3) The key serial tree iterator (key_garbage_collector()) part of the garbage collector has been modified to: (a) Complete each scan of the keyrings before setting the new timer. (b) Only set the new timer for keys that have yet to expire. This means that the new timer is now calculated correctly, and the gc doesn't get into a loop continually scanning for keys that have expired, and preventing other things from happening, like RCU cleaning up the old keyring contents. (c) Perform an extra scan if any keys were garbage collected in this one as a key might become garbage during a scan, and (b) could mean we don't set the timer again. (4) Made key_schedule_gc() take the time at which to do a collection run, rather than the time at which the key expires. This means the collection of dead keys (key type unregistered) can happen immediately. Signed-off-by: David Howells <dhowells@redhat.com> Signed-off-by: James Morris <jmorris@namei.org>
Diffstat (limited to 'security/keys')
-rw-r--r--security/keys/gc.c78
-rw-r--r--security/keys/key.c4
-rw-r--r--security/keys/keyctl.c2
-rw-r--r--security/keys/keyring.c24
4 files changed, 73 insertions, 35 deletions
diff --git a/security/keys/gc.c b/security/keys/gc.c
index 1e616aef55fd..485fc6233c38 100644
--- a/security/keys/gc.c
+++ b/security/keys/gc.c
@@ -26,8 +26,10 @@ static void key_garbage_collector(struct work_struct *);
26static DEFINE_TIMER(key_gc_timer, key_gc_timer_func, 0, 0); 26static DEFINE_TIMER(key_gc_timer, key_gc_timer_func, 0, 0);
27static DECLARE_WORK(key_gc_work, key_garbage_collector); 27static DECLARE_WORK(key_gc_work, key_garbage_collector);
28static key_serial_t key_gc_cursor; /* the last key the gc considered */ 28static key_serial_t key_gc_cursor; /* the last key the gc considered */
29static bool key_gc_again;
29static unsigned long key_gc_executing; 30static unsigned long key_gc_executing;
30static time_t key_gc_next_run = LONG_MAX; 31static time_t key_gc_next_run = LONG_MAX;
32static time_t key_gc_new_timer;
31 33
32/* 34/*
33 * Schedule a garbage collection run 35 * Schedule a garbage collection run
@@ -40,9 +42,7 @@ void key_schedule_gc(time_t gc_at)
40 42
41 kenter("%ld", gc_at - now); 43 kenter("%ld", gc_at - now);
42 44
43 gc_at += key_gc_delay; 45 if (gc_at <= now) {
44
45 if (now >= gc_at) {
46 schedule_work(&key_gc_work); 46 schedule_work(&key_gc_work);
47 } else if (gc_at < key_gc_next_run) { 47 } else if (gc_at < key_gc_next_run) {
48 expires = jiffies + (gc_at - now) * HZ; 48 expires = jiffies + (gc_at - now) * HZ;
@@ -112,16 +112,18 @@ static void key_garbage_collector(struct work_struct *work)
112 struct rb_node *rb; 112 struct rb_node *rb;
113 key_serial_t cursor; 113 key_serial_t cursor;
114 struct key *key, *xkey; 114 struct key *key, *xkey;
115 time_t new_timer = LONG_MAX, limit; 115 time_t new_timer = LONG_MAX, limit, now;
116 116
117 kenter(""); 117 now = current_kernel_time().tv_sec;
118 kenter("[%x,%ld]", key_gc_cursor, key_gc_new_timer - now);
118 119
119 if (test_and_set_bit(0, &key_gc_executing)) { 120 if (test_and_set_bit(0, &key_gc_executing)) {
120 key_schedule_gc(current_kernel_time().tv_sec); 121 key_schedule_gc(current_kernel_time().tv_sec + 1);
122 kleave(" [busy; deferring]");
121 return; 123 return;
122 } 124 }
123 125
124 limit = current_kernel_time().tv_sec; 126 limit = now;
125 if (limit > key_gc_delay) 127 if (limit > key_gc_delay)
126 limit -= key_gc_delay; 128 limit -= key_gc_delay;
127 else 129 else
@@ -129,12 +131,19 @@ static void key_garbage_collector(struct work_struct *work)
129 131
130 spin_lock(&key_serial_lock); 132 spin_lock(&key_serial_lock);
131 133
132 if (RB_EMPTY_ROOT(&key_serial_tree)) 134 if (unlikely(RB_EMPTY_ROOT(&key_serial_tree))) {
133 goto reached_the_end; 135 spin_unlock(&key_serial_lock);
136 clear_bit(0, &key_gc_executing);
137 return;
138 }
134 139
135 cursor = key_gc_cursor; 140 cursor = key_gc_cursor;
136 if (cursor < 0) 141 if (cursor < 0)
137 cursor = 0; 142 cursor = 0;
143 if (cursor > 0)
144 new_timer = key_gc_new_timer;
145 else
146 key_gc_again = false;
138 147
139 /* find the first key above the cursor */ 148 /* find the first key above the cursor */
140 key = NULL; 149 key = NULL;
@@ -160,35 +169,50 @@ static void key_garbage_collector(struct work_struct *work)
160 169
161 /* trawl through the keys looking for keyrings */ 170 /* trawl through the keys looking for keyrings */
162 for (;;) { 171 for (;;) {
163 if (key->expiry > 0 && key->expiry < new_timer) 172 if (key->expiry > now && key->expiry < new_timer) {
173 kdebug("will expire %x in %ld",
174 key_serial(key), key->expiry - now);
164 new_timer = key->expiry; 175 new_timer = key->expiry;
176 }
165 177
166 if (key->type == &key_type_keyring && 178 if (key->type == &key_type_keyring &&
167 key_gc_keyring(key, limit)) { 179 key_gc_keyring(key, limit))
168 /* the gc ate our lock */ 180 /* the gc had to release our lock so that the keyring
169 schedule_work(&key_gc_work); 181 * could be modified, so we have to get it again */
170 goto no_unlock; 182 goto gc_released_our_lock;
171 }
172 183
173 rb = rb_next(&key->serial_node); 184 rb = rb_next(&key->serial_node);
174 if (!rb) { 185 if (!rb)
175 key_gc_cursor = 0; 186 goto reached_the_end;
176 break;
177 }
178 key = rb_entry(rb, struct key, serial_node); 187 key = rb_entry(rb, struct key, serial_node);
179 } 188 }
180 189
181out: 190gc_released_our_lock:
182 spin_unlock(&key_serial_lock); 191 kdebug("gc_released_our_lock");
183no_unlock: 192 key_gc_new_timer = new_timer;
193 key_gc_again = true;
184 clear_bit(0, &key_gc_executing); 194 clear_bit(0, &key_gc_executing);
185 if (new_timer < LONG_MAX) 195 schedule_work(&key_gc_work);
186 key_schedule_gc(new_timer); 196 kleave(" [continue]");
187
188 kleave("");
189 return; 197 return;
190 198
199 /* when we reach the end of the run, we set the timer for the next one */
191reached_the_end: 200reached_the_end:
201 kdebug("reached_the_end");
202 spin_unlock(&key_serial_lock);
203 key_gc_new_timer = new_timer;
192 key_gc_cursor = 0; 204 key_gc_cursor = 0;
193 goto out; 205 clear_bit(0, &key_gc_executing);
206
207 if (key_gc_again) {
208 /* there may have been a key that expired whilst we were
209 * scanning, so if we discarded any links we should do another
210 * scan */
211 new_timer = now + 1;
212 key_schedule_gc(new_timer);
213 } else if (new_timer < LONG_MAX) {
214 new_timer += key_gc_delay;
215 key_schedule_gc(new_timer);
216 }
217 kleave(" [end]");
194} 218}
diff --git a/security/keys/key.c b/security/keys/key.c
index 08531ad0f252..e50d264c9ad1 100644
--- a/security/keys/key.c
+++ b/security/keys/key.c
@@ -500,7 +500,7 @@ int key_negate_and_link(struct key *key,
500 set_bit(KEY_FLAG_INSTANTIATED, &key->flags); 500 set_bit(KEY_FLAG_INSTANTIATED, &key->flags);
501 now = current_kernel_time(); 501 now = current_kernel_time();
502 key->expiry = now.tv_sec + timeout; 502 key->expiry = now.tv_sec + timeout;
503 key_schedule_gc(key->expiry); 503 key_schedule_gc(key->expiry + key_gc_delay);
504 504
505 if (test_and_clear_bit(KEY_FLAG_USER_CONSTRUCT, &key->flags)) 505 if (test_and_clear_bit(KEY_FLAG_USER_CONSTRUCT, &key->flags))
506 awaken = 1; 506 awaken = 1;
@@ -909,7 +909,7 @@ void key_revoke(struct key *key)
909 time = now.tv_sec; 909 time = now.tv_sec;
910 if (key->revoked_at == 0 || key->revoked_at > time) { 910 if (key->revoked_at == 0 || key->revoked_at > time) {
911 key->revoked_at = time; 911 key->revoked_at = time;
912 key_schedule_gc(key->revoked_at); 912 key_schedule_gc(key->revoked_at + key_gc_delay);
913 } 913 }
914 914
915 up_write(&key->sem); 915 up_write(&key->sem);
diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c
index 60983f38852e..2fb28efc5326 100644
--- a/security/keys/keyctl.c
+++ b/security/keys/keyctl.c
@@ -1115,7 +1115,7 @@ long keyctl_set_timeout(key_serial_t id, unsigned timeout)
1115 } 1115 }
1116 1116
1117 key->expiry = expiry; 1117 key->expiry = expiry;
1118 key_schedule_gc(key->expiry); 1118 key_schedule_gc(key->expiry + key_gc_delay);
1119 1119
1120 up_write(&key->sem); 1120 up_write(&key->sem);
1121 key_put(key); 1121 key_put(key);
diff --git a/security/keys/keyring.c b/security/keys/keyring.c
index ac977f661a79..8ec02746ca99 100644
--- a/security/keys/keyring.c
+++ b/security/keys/keyring.c
@@ -1019,18 +1019,18 @@ void keyring_gc(struct key *keyring, time_t limit)
1019 struct key *key; 1019 struct key *key;
1020 int loop, keep, max; 1020 int loop, keep, max;
1021 1021
1022 kenter("%x", key_serial(keyring)); 1022 kenter("{%x,%s}", key_serial(keyring), keyring->description);
1023 1023
1024 down_write(&keyring->sem); 1024 down_write(&keyring->sem);
1025 1025
1026 klist = keyring->payload.subscriptions; 1026 klist = keyring->payload.subscriptions;
1027 if (!klist) 1027 if (!klist)
1028 goto just_return; 1028 goto no_klist;
1029 1029
1030 /* work out how many subscriptions we're keeping */ 1030 /* work out how many subscriptions we're keeping */
1031 keep = 0; 1031 keep = 0;
1032 for (loop = klist->nkeys - 1; loop >= 0; loop--) 1032 for (loop = klist->nkeys - 1; loop >= 0; loop--)
1033 if (!key_is_dead(klist->keys[loop], limit)); 1033 if (!key_is_dead(klist->keys[loop], limit))
1034 keep++; 1034 keep++;
1035 1035
1036 if (keep == klist->nkeys) 1036 if (keep == klist->nkeys)
@@ -1041,7 +1041,7 @@ void keyring_gc(struct key *keyring, time_t limit)
1041 new = kmalloc(sizeof(struct keyring_list) + max * sizeof(struct key *), 1041 new = kmalloc(sizeof(struct keyring_list) + max * sizeof(struct key *),
1042 GFP_KERNEL); 1042 GFP_KERNEL);
1043 if (!new) 1043 if (!new)
1044 goto just_return; 1044 goto nomem;
1045 new->maxkeys = max; 1045 new->maxkeys = max;
1046 new->nkeys = 0; 1046 new->nkeys = 0;
1047 new->delkey = 0; 1047 new->delkey = 0;
@@ -1081,7 +1081,21 @@ void keyring_gc(struct key *keyring, time_t limit)
1081discard_new: 1081discard_new:
1082 new->nkeys = keep; 1082 new->nkeys = keep;
1083 keyring_clear_rcu_disposal(&new->rcu); 1083 keyring_clear_rcu_disposal(&new->rcu);
1084 up_write(&keyring->sem);
1085 kleave(" [discard]");
1086 return;
1087
1084just_return: 1088just_return:
1085 up_write(&keyring->sem); 1089 up_write(&keyring->sem);
1086 kleave(" [no]"); 1090 kleave(" [no dead]");
1091 return;
1092
1093no_klist:
1094 up_write(&keyring->sem);
1095 kleave(" [no_klist]");
1096 return;
1097
1098nomem:
1099 up_write(&keyring->sem);
1100 kleave(" [oom]");
1087} 1101}