hc
2024-02-20 102a0743326a03cd1a1202ceda21e175b7d3575c
kernel/security/selinux/ss/sidtab.c
....@@ -9,6 +9,8 @@
99 */
1010 #include <linux/errno.h>
1111 #include <linux/kernel.h>
12
+#include <linux/list.h>
13
+#include <linux/rcupdate.h>
1214 #include <linux/slab.h>
1315 #include <linux/sched.h>
1416 #include <linux/spinlock.h>
....@@ -16,6 +18,14 @@
1618 #include "flask.h"
1719 #include "security.h"
1820 #include "sidtab.h"
21
+
22
+struct sidtab_str_cache {
23
+ struct rcu_head rcu_member;
24
+ struct list_head lru_member;
25
+ struct sidtab_entry *parent;
26
+ u32 len;
27
+ char str[];
28
+};
1929
2030 #define index_to_sid(index) (index + SECINITSID_NUM + 1)
2131 #define sid_to_index(sid) (sid - (SECINITSID_NUM + 1))
....@@ -29,22 +39,31 @@
2939 for (i = 0; i < SECINITSID_NUM; i++)
3040 s->isids[i].set = 0;
3141
42
+ s->frozen = false;
3243 s->count = 0;
3344 s->convert = NULL;
3445 hash_init(s->context_to_sid);
3546
3647 spin_lock_init(&s->lock);
48
+
49
+#if CONFIG_SECURITY_SELINUX_SID2STR_CACHE_SIZE > 0
50
+ s->cache_free_slots = CONFIG_SECURITY_SELINUX_SID2STR_CACHE_SIZE;
51
+ INIT_LIST_HEAD(&s->cache_lru_list);
52
+ spin_lock_init(&s->cache_lock);
53
+#endif
54
+
3755 return 0;
3856 }
3957
40
-static u32 context_to_sid(struct sidtab *s, struct context *context)
58
+static u32 context_to_sid(struct sidtab *s, struct context *context, u32 hash)
4159 {
42
- struct sidtab_entry_leaf *entry;
60
+ struct sidtab_entry *entry;
4361 u32 sid = 0;
4462
4563 rcu_read_lock();
46
- hash_for_each_possible_rcu(s->context_to_sid, entry, list,
47
- context->hash) {
64
+ hash_for_each_possible_rcu(s->context_to_sid, entry, list, hash) {
65
+ if (entry->hash != hash)
66
+ continue;
4867 if (context_cmp(&entry->context, context)) {
4968 sid = entry->sid;
5069 break;
....@@ -56,19 +75,25 @@
5675
5776 int sidtab_set_initial(struct sidtab *s, u32 sid, struct context *context)
5877 {
59
- struct sidtab_isid_entry *entry;
78
+ struct sidtab_isid_entry *isid;
79
+ u32 hash;
6080 int rc;
6181
6282 if (sid == 0 || sid > SECINITSID_NUM)
6383 return -EINVAL;
6484
65
- entry = &s->isids[sid - 1];
85
+ isid = &s->isids[sid - 1];
6686
67
- rc = context_cpy(&entry->leaf.context, context);
87
+ rc = context_cpy(&isid->entry.context, context);
6888 if (rc)
6989 return rc;
7090
71
- entry->set = 1;
91
+#if CONFIG_SECURITY_SELINUX_SID2STR_CACHE_SIZE > 0
92
+ isid->entry.cache = NULL;
93
+#endif
94
+ isid->set = 1;
95
+
96
+ hash = context_compute_hash(context);
7297
7398 /*
7499 * Multiple initial sids may map to the same context. Check that this
....@@ -76,9 +101,10 @@
76101 * to avoid duplicate entries and long linked lists upon hash
77102 * collision.
78103 */
79
- if (!context_to_sid(s, context)) {
80
- entry->leaf.sid = sid;
81
- hash_add(s->context_to_sid, &entry->leaf.list, context->hash);
104
+ if (!context_to_sid(s, context, hash)) {
105
+ isid->entry.sid = sid;
106
+ isid->entry.hash = hash;
107
+ hash_add(s->context_to_sid, &isid->entry.list, hash);
82108 }
83109
84110 return 0;
....@@ -92,7 +118,7 @@
92118 int entries = 0;
93119 int max_chain_len = 0;
94120 int cur_bucket = 0;
95
- struct sidtab_entry_leaf *entry;
121
+ struct sidtab_entry *entry;
96122
97123 rcu_read_lock();
98124 hash_for_each_rcu(sidtab->context_to_sid, i, entry, list) {
....@@ -151,8 +177,8 @@
151177 return 0;
152178 }
153179
154
-static struct sidtab_entry_leaf *sidtab_do_lookup(struct sidtab *s, u32 index,
155
- int alloc)
180
+static struct sidtab_entry *sidtab_do_lookup(struct sidtab *s, u32 index,
181
+ int alloc)
156182 {
157183 union sidtab_entry_inner *entry;
158184 u32 level, capacity_shift, leaf_index = index / SIDTAB_LEAF_ENTRIES;
....@@ -192,7 +218,7 @@
192218 return &entry->ptr_leaf->entries[index % SIDTAB_LEAF_ENTRIES];
193219 }
194220
195
-static struct context *sidtab_lookup(struct sidtab *s, u32 index)
221
+static struct sidtab_entry *sidtab_lookup(struct sidtab *s, u32 index)
196222 {
197223 /* read entries only after reading count */
198224 u32 count = smp_load_acquire(&s->count);
....@@ -200,36 +226,37 @@
200226 if (index >= count)
201227 return NULL;
202228
203
- return &sidtab_do_lookup(s, index, 0)->context;
229
+ return sidtab_do_lookup(s, index, 0);
204230 }
205231
206
-static struct context *sidtab_lookup_initial(struct sidtab *s, u32 sid)
232
+static struct sidtab_entry *sidtab_lookup_initial(struct sidtab *s, u32 sid)
207233 {
208
- return s->isids[sid - 1].set ? &s->isids[sid - 1].leaf.context : NULL;
234
+ return s->isids[sid - 1].set ? &s->isids[sid - 1].entry : NULL;
209235 }
210236
211
-static struct context *sidtab_search_core(struct sidtab *s, u32 sid, int force)
237
+static struct sidtab_entry *sidtab_search_core(struct sidtab *s, u32 sid,
238
+ int force)
212239 {
213
- struct context *context;
214
-
215240 if (sid != 0) {
241
+ struct sidtab_entry *entry;
242
+
216243 if (sid > SECINITSID_NUM)
217
- context = sidtab_lookup(s, sid_to_index(sid));
244
+ entry = sidtab_lookup(s, sid_to_index(sid));
218245 else
219
- context = sidtab_lookup_initial(s, sid);
220
- if (context && (!context->len || force))
221
- return context;
246
+ entry = sidtab_lookup_initial(s, sid);
247
+ if (entry && (!entry->context.len || force))
248
+ return entry;
222249 }
223250
224251 return sidtab_lookup_initial(s, SECINITSID_UNLABELED);
225252 }
226253
227
-struct context *sidtab_search(struct sidtab *s, u32 sid)
254
+struct sidtab_entry *sidtab_search_entry(struct sidtab *s, u32 sid)
228255 {
229256 return sidtab_search_core(s, sid, 0);
230257 }
231258
232
-struct context *sidtab_search_force(struct sidtab *s, u32 sid)
259
+struct sidtab_entry *sidtab_search_entry_force(struct sidtab *s, u32 sid)
233260 {
234261 return sidtab_search_core(s, sid, 1);
235262 }
....@@ -238,12 +265,12 @@
238265 u32 *sid)
239266 {
240267 unsigned long flags;
241
- u32 count;
268
+ u32 count, hash = context_compute_hash(context);
242269 struct sidtab_convert_params *convert;
243
- struct sidtab_entry_leaf *dst, *dst_convert;
270
+ struct sidtab_entry *dst, *dst_convert;
244271 int rc;
245272
246
- *sid = context_to_sid(s, context);
273
+ *sid = context_to_sid(s, context, hash);
247274 if (*sid)
248275 return 0;
249276
....@@ -251,12 +278,20 @@
251278 spin_lock_irqsave(&s->lock, flags);
252279
253280 rc = 0;
254
- *sid = context_to_sid(s, context);
281
+ *sid = context_to_sid(s, context, hash);
255282 if (*sid)
256283 goto out_unlock;
257284
258
- /* read entries only after reading count */
259
- count = smp_load_acquire(&s->count);
285
+ if (unlikely(s->frozen)) {
286
+ /*
287
+ * This sidtab is now frozen - tell the caller to abort and
288
+ * get the new one.
289
+ */
290
+ rc = -ESTALE;
291
+ goto out_unlock;
292
+ }
293
+
294
+ count = s->count;
260295 convert = s->convert;
261296
262297 /* bail out if we already reached max entries */
....@@ -271,6 +306,7 @@
271306 goto out_unlock;
272307
273308 dst->sid = index_to_sid(count);
309
+ dst->hash = hash;
274310
275311 rc = context_cpy(&dst->context, context);
276312 if (rc)
....@@ -289,16 +325,17 @@
289325 }
290326
291327 rc = convert->func(context, &dst_convert->context,
292
- convert->args);
328
+ convert->args, GFP_ATOMIC);
293329 if (rc) {
294330 context_destroy(&dst->context);
295331 goto out_unlock;
296332 }
297333 dst_convert->sid = index_to_sid(count);
334
+ dst_convert->hash = context_compute_hash(&dst_convert->context);
298335 convert->target->count = count + 1;
299336
300337 hash_add_rcu(convert->target->context_to_sid,
301
- &dst_convert->list, dst_convert->context.hash);
338
+ &dst_convert->list, dst_convert->hash);
302339 }
303340
304341 if (context->len)
....@@ -309,7 +346,7 @@
309346
310347 /* write entries before updating count */
311348 smp_store_release(&s->count, count + 1);
312
- hash_add_rcu(s->context_to_sid, &dst->list, dst->context.hash);
349
+ hash_add_rcu(s->context_to_sid, &dst->list, dst->hash);
313350
314351 rc = 0;
315352 out_unlock:
....@@ -319,16 +356,15 @@
319356
320357 static void sidtab_convert_hashtable(struct sidtab *s, u32 count)
321358 {
322
- struct sidtab_entry_leaf *entry;
359
+ struct sidtab_entry *entry;
323360 u32 i;
324361
325362 for (i = 0; i < count; i++) {
326363 entry = sidtab_do_lookup(s, i, 0);
327364 entry->sid = index_to_sid(i);
365
+ entry->hash = context_compute_hash(&entry->context);
328366
329
- hash_add_rcu(s->context_to_sid, &entry->list,
330
- entry->context.hash);
331
-
367
+ hash_add_rcu(s->context_to_sid, &entry->list, entry->hash);
332368 }
333369 }
334370
....@@ -368,7 +404,7 @@
368404 while (i < SIDTAB_LEAF_ENTRIES && *pos < count) {
369405 rc = convert->func(&esrc->ptr_leaf->entries[i].context,
370406 &edst->ptr_leaf->entries[i].context,
371
- convert->args);
407
+ convert->args, GFP_KERNEL);
372408 if (rc)
373409 return rc;
374410 (*pos)++;
....@@ -376,7 +412,6 @@
376412 }
377413 cond_resched();
378414 }
379
-
380415 return 0;
381416 }
382417
....@@ -439,6 +474,35 @@
439474 return 0;
440475 }
441476
477
+void sidtab_cancel_convert(struct sidtab *s)
478
+{
479
+ unsigned long flags;
480
+
481
+ /* cancelling policy load - disable live convert of sidtab */
482
+ spin_lock_irqsave(&s->lock, flags);
483
+ s->convert = NULL;
484
+ spin_unlock_irqrestore(&s->lock, flags);
485
+}
486
+
487
+void sidtab_freeze_begin(struct sidtab *s, unsigned long *flags) __acquires(&s->lock)
488
+{
489
+ spin_lock_irqsave(&s->lock, *flags);
490
+ s->frozen = true;
491
+ s->convert = NULL;
492
+}
493
+void sidtab_freeze_end(struct sidtab *s, unsigned long *flags) __releases(&s->lock)
494
+{
495
+ spin_unlock_irqrestore(&s->lock, *flags);
496
+}
497
+
498
+static void sidtab_destroy_entry(struct sidtab_entry *entry)
499
+{
500
+ context_destroy(&entry->context);
501
+#if CONFIG_SECURITY_SELINUX_SID2STR_CACHE_SIZE > 0
502
+ kfree(rcu_dereference_raw(entry->cache));
503
+#endif
504
+}
505
+
442506 static void sidtab_destroy_tree(union sidtab_entry_inner entry, u32 level)
443507 {
444508 u32 i;
....@@ -459,7 +523,7 @@
459523 return;
460524
461525 for (i = 0; i < SIDTAB_LEAF_ENTRIES; i++)
462
- context_destroy(&node->entries[i].context);
526
+ sidtab_destroy_entry(&node->entries[i]);
463527 kfree(node);
464528 }
465529 }
....@@ -470,7 +534,7 @@
470534
471535 for (i = 0; i < SECINITSID_NUM; i++)
472536 if (s->isids[i].set)
473
- context_destroy(&s->isids[i].leaf.context);
537
+ sidtab_destroy_entry(&s->isids[i].entry);
474538
475539 level = SIDTAB_MAX_LEVEL;
476540 while (level && !s->roots[level].ptr_inner)
....@@ -483,3 +547,82 @@
483547 * to be cleaned up here.
484548 */
485549 }
550
+
551
+#if CONFIG_SECURITY_SELINUX_SID2STR_CACHE_SIZE > 0
552
+
553
+void sidtab_sid2str_put(struct sidtab *s, struct sidtab_entry *entry,
554
+ const char *str, u32 str_len)
555
+{
556
+ struct sidtab_str_cache *cache, *victim = NULL;
557
+ unsigned long flags;
558
+
559
+ /* do not cache invalid contexts */
560
+ if (entry->context.len)
561
+ return;
562
+
563
+ spin_lock_irqsave(&s->cache_lock, flags);
564
+
565
+ cache = rcu_dereference_protected(entry->cache,
566
+ lockdep_is_held(&s->cache_lock));
567
+ if (cache) {
568
+ /* entry in cache - just bump to the head of LRU list */
569
+ list_move(&cache->lru_member, &s->cache_lru_list);
570
+ goto out_unlock;
571
+ }
572
+
573
+ cache = kmalloc(sizeof(struct sidtab_str_cache) + str_len, GFP_ATOMIC);
574
+ if (!cache)
575
+ goto out_unlock;
576
+
577
+ if (s->cache_free_slots == 0) {
578
+ /* pop a cache entry from the tail and free it */
579
+ victim = container_of(s->cache_lru_list.prev,
580
+ struct sidtab_str_cache, lru_member);
581
+ list_del(&victim->lru_member);
582
+ rcu_assign_pointer(victim->parent->cache, NULL);
583
+ } else {
584
+ s->cache_free_slots--;
585
+ }
586
+ cache->parent = entry;
587
+ cache->len = str_len;
588
+ memcpy(cache->str, str, str_len);
589
+ list_add(&cache->lru_member, &s->cache_lru_list);
590
+
591
+ rcu_assign_pointer(entry->cache, cache);
592
+
593
+out_unlock:
594
+ spin_unlock_irqrestore(&s->cache_lock, flags);
595
+ kfree_rcu(victim, rcu_member);
596
+}
597
+
598
+int sidtab_sid2str_get(struct sidtab *s, struct sidtab_entry *entry,
599
+ char **out, u32 *out_len)
600
+{
601
+ struct sidtab_str_cache *cache;
602
+ int rc = 0;
603
+
604
+ if (entry->context.len)
605
+ return -ENOENT; /* do not cache invalid contexts */
606
+
607
+ rcu_read_lock();
608
+
609
+ cache = rcu_dereference(entry->cache);
610
+ if (!cache) {
611
+ rc = -ENOENT;
612
+ } else {
613
+ *out_len = cache->len;
614
+ if (out) {
615
+ *out = kmemdup(cache->str, cache->len, GFP_ATOMIC);
616
+ if (!*out)
617
+ rc = -ENOMEM;
618
+ }
619
+ }
620
+
621
+ rcu_read_unlock();
622
+
623
+ if (!rc && out)
624
+ sidtab_sid2str_put(s, entry, *out, *out_len);
625
+ return rc;
626
+}
627
+
628
+#endif /* CONFIG_SECURITY_SELINUX_SID2STR_CACHE_SIZE > 0 */