1 // SPDX-License-Identifier: GPL-2.0
3 * Implementation of the SID table type.
5 * Original author: Stephen Smalley, <sds@tycho.nsa.gov>
6 * Author: Ondrej Mosnacek, <omosnacek@gmail.com>
8 * Copyright (C) 2018 Red Hat, Inc.
10 #include <linux/errno.h>
11 #include <linux/kernel.h>
12 #include <linux/slab.h>
13 #include <linux/sched.h>
14 #include <linux/spinlock.h>
15 #include <asm/barrier.h>
20 int sidtab_init(struct sidtab
*s
)
24 memset(s
->roots
, 0, sizeof(s
->roots
));
26 /* max count is SIDTAB_MAX so valid index is always < SIDTAB_MAX */
27 for (i
= 0; i
< SIDTAB_RCACHE_SIZE
; i
++)
28 s
->rcache
[i
] = SIDTAB_MAX
;
30 for (i
= 0; i
< SECINITSID_NUM
; i
++)
36 spin_lock_init(&s
->lock
);
40 int sidtab_set_initial(struct sidtab
*s
, u32 sid
, struct context
*context
)
42 struct sidtab_isid_entry
*entry
;
45 if (sid
== 0 || sid
> SECINITSID_NUM
)
48 entry
= &s
->isids
[sid
- 1];
50 rc
= context_cpy(&entry
->context
, context
);
58 static u32
sidtab_level_from_count(u32 count
)
60 u32 capacity
= SIDTAB_LEAF_ENTRIES
;
63 while (count
> capacity
) {
64 capacity
<<= SIDTAB_INNER_SHIFT
;
70 static int sidtab_alloc_roots(struct sidtab
*s
, u32 level
)
74 if (!s
->roots
[0].ptr_leaf
) {
75 s
->roots
[0].ptr_leaf
= kzalloc(SIDTAB_NODE_ALLOC_SIZE
,
77 if (!s
->roots
[0].ptr_leaf
)
80 for (l
= 1; l
<= level
; ++l
)
81 if (!s
->roots
[l
].ptr_inner
) {
82 s
->roots
[l
].ptr_inner
= kzalloc(SIDTAB_NODE_ALLOC_SIZE
,
84 if (!s
->roots
[l
].ptr_inner
)
86 s
->roots
[l
].ptr_inner
->entries
[0] = s
->roots
[l
- 1];
91 static struct context
*sidtab_do_lookup(struct sidtab
*s
, u32 index
, int alloc
)
93 union sidtab_entry_inner
*entry
;
94 u32 level
, capacity_shift
, leaf_index
= index
/ SIDTAB_LEAF_ENTRIES
;
96 /* find the level of the subtree we need */
97 level
= sidtab_level_from_count(index
+ 1);
98 capacity_shift
= level
* SIDTAB_INNER_SHIFT
;
100 /* allocate roots if needed */
101 if (alloc
&& sidtab_alloc_roots(s
, level
) != 0)
104 /* lookup inside the subtree */
105 entry
= &s
->roots
[level
];
107 capacity_shift
-= SIDTAB_INNER_SHIFT
;
110 entry
= &entry
->ptr_inner
->entries
[leaf_index
>> capacity_shift
];
111 leaf_index
&= ((u32
)1 << capacity_shift
) - 1;
113 if (!entry
->ptr_inner
) {
115 entry
->ptr_inner
= kzalloc(SIDTAB_NODE_ALLOC_SIZE
,
117 if (!entry
->ptr_inner
)
121 if (!entry
->ptr_leaf
) {
123 entry
->ptr_leaf
= kzalloc(SIDTAB_NODE_ALLOC_SIZE
,
125 if (!entry
->ptr_leaf
)
128 return &entry
->ptr_leaf
->entries
[index
% SIDTAB_LEAF_ENTRIES
].context
;
131 static struct context
*sidtab_lookup(struct sidtab
*s
, u32 index
)
133 /* read entries only after reading count */
134 u32 count
= smp_load_acquire(&s
->count
);
139 return sidtab_do_lookup(s
, index
, 0);
142 static struct context
*sidtab_lookup_initial(struct sidtab
*s
, u32 sid
)
144 return s
->isids
[sid
- 1].set
? &s
->isids
[sid
- 1].context
: NULL
;
147 static struct context
*sidtab_search_core(struct sidtab
*s
, u32 sid
, int force
)
149 struct context
*context
;
152 if (sid
> SECINITSID_NUM
)
153 context
= sidtab_lookup(s
, sid
- (SECINITSID_NUM
+ 1));
155 context
= sidtab_lookup_initial(s
, sid
);
156 if (context
&& (!context
->len
|| force
))
160 return sidtab_lookup_initial(s
, SECINITSID_UNLABELED
);
163 struct context
*sidtab_search(struct sidtab
*s
, u32 sid
)
165 return sidtab_search_core(s
, sid
, 0);
168 struct context
*sidtab_search_force(struct sidtab
*s
, u32 sid
)
170 return sidtab_search_core(s
, sid
, 1);
173 static int sidtab_find_context(union sidtab_entry_inner entry
,
174 u32
*pos
, u32 count
, u32 level
,
175 struct context
*context
, u32
*index
)
181 struct sidtab_node_inner
*node
= entry
.ptr_inner
;
184 while (i
< SIDTAB_INNER_ENTRIES
&& *pos
< count
) {
185 rc
= sidtab_find_context(node
->entries
[i
],
186 pos
, count
, level
- 1,
193 struct sidtab_node_leaf
*node
= entry
.ptr_leaf
;
196 while (i
< SIDTAB_LEAF_ENTRIES
&& *pos
< count
) {
197 if (context_cmp(&node
->entries
[i
].context
, context
)) {
208 static void sidtab_rcache_update(struct sidtab
*s
, u32 index
, u32 pos
)
211 WRITE_ONCE(s
->rcache
[pos
], READ_ONCE(s
->rcache
[pos
- 1]));
214 WRITE_ONCE(s
->rcache
[0], index
);
217 static void sidtab_rcache_push(struct sidtab
*s
, u32 index
)
219 sidtab_rcache_update(s
, index
, SIDTAB_RCACHE_SIZE
- 1);
222 static int sidtab_rcache_search(struct sidtab
*s
, struct context
*context
,
227 for (i
= 0; i
< SIDTAB_RCACHE_SIZE
; i
++) {
228 u32 v
= READ_ONCE(s
->rcache
[i
]);
233 if (context_cmp(sidtab_do_lookup(s
, v
, 0), context
)) {
234 sidtab_rcache_update(s
, v
, i
);
242 static int sidtab_reverse_lookup(struct sidtab
*s
, struct context
*context
,
246 u32 count
, count_locked
, level
, pos
;
247 struct sidtab_convert_params
*convert
;
248 struct context
*dst
, *dst_convert
;
251 rc
= sidtab_rcache_search(s
, context
, index
);
255 /* read entries only after reading count */
256 count
= smp_load_acquire(&s
->count
);
257 level
= sidtab_level_from_count(count
);
260 rc
= sidtab_find_context(s
->roots
[level
], &pos
, count
, level
,
263 sidtab_rcache_push(s
, *index
);
267 /* lock-free search failed: lock, re-search, and insert if not found */
268 spin_lock_irqsave(&s
->lock
, flags
);
270 convert
= s
->convert
;
271 count_locked
= s
->count
;
272 level
= sidtab_level_from_count(count_locked
);
274 /* if count has changed before we acquired the lock, then catch up */
275 while (count
< count_locked
) {
276 if (context_cmp(sidtab_do_lookup(s
, count
, 0), context
)) {
277 sidtab_rcache_push(s
, count
);
285 /* bail out if we already reached max entries */
287 if (count
>= SIDTAB_MAX
)
290 /* insert context into new entry */
292 dst
= sidtab_do_lookup(s
, count
, 1);
296 rc
= context_cpy(dst
, context
);
301 * if we are building a new sidtab, we need to convert the context
302 * and insert it there as well
306 dst_convert
= sidtab_do_lookup(convert
->target
, count
, 1);
308 context_destroy(dst
);
312 rc
= convert
->func(context
, dst_convert
, convert
->args
);
314 context_destroy(dst
);
318 /* at this point we know the insert won't fail */
319 convert
->target
->count
= count
+ 1;
323 pr_info("SELinux: Context %s is not valid (left unmapped).\n",
326 sidtab_rcache_push(s
, count
);
329 /* write entries before writing new count */
330 smp_store_release(&s
->count
, count
+ 1);
334 spin_unlock_irqrestore(&s
->lock
, flags
);
338 int sidtab_context_to_sid(struct sidtab
*s
, struct context
*context
, u32
*sid
)
343 for (i
= 0; i
< SECINITSID_NUM
; i
++) {
344 struct sidtab_isid_entry
*entry
= &s
->isids
[i
];
346 if (entry
->set
&& context_cmp(context
, &entry
->context
)) {
352 rc
= sidtab_reverse_lookup(s
, context
, sid
);
355 *sid
+= SECINITSID_NUM
+ 1;
359 static int sidtab_convert_tree(union sidtab_entry_inner
*edst
,
360 union sidtab_entry_inner
*esrc
,
361 u32
*pos
, u32 count
, u32 level
,
362 struct sidtab_convert_params
*convert
)
368 if (!edst
->ptr_inner
) {
369 edst
->ptr_inner
= kzalloc(SIDTAB_NODE_ALLOC_SIZE
,
371 if (!edst
->ptr_inner
)
375 while (i
< SIDTAB_INNER_ENTRIES
&& *pos
< count
) {
376 rc
= sidtab_convert_tree(&edst
->ptr_inner
->entries
[i
],
377 &esrc
->ptr_inner
->entries
[i
],
378 pos
, count
, level
- 1,
385 if (!edst
->ptr_leaf
) {
386 edst
->ptr_leaf
= kzalloc(SIDTAB_NODE_ALLOC_SIZE
,
392 while (i
< SIDTAB_LEAF_ENTRIES
&& *pos
< count
) {
393 rc
= convert
->func(&esrc
->ptr_leaf
->entries
[i
].context
,
394 &edst
->ptr_leaf
->entries
[i
].context
,
406 int sidtab_convert(struct sidtab
*s
, struct sidtab_convert_params
*params
)
409 u32 count
, level
, pos
;
412 spin_lock_irqsave(&s
->lock
, flags
);
414 /* concurrent policy loads are not allowed */
416 spin_unlock_irqrestore(&s
->lock
, flags
);
421 level
= sidtab_level_from_count(count
);
423 /* allocate last leaf in the new sidtab (to avoid race with
426 rc
= sidtab_do_lookup(params
->target
, count
- 1, 1) ? 0 : -ENOMEM
;
428 spin_unlock_irqrestore(&s
->lock
, flags
);
432 /* set count in case no new entries are added during conversion */
433 params
->target
->count
= count
;
435 /* enable live convert of new entries */
438 /* we can safely do the rest of the conversion outside the lock */
439 spin_unlock_irqrestore(&s
->lock
, flags
);
441 pr_info("SELinux: Converting %u SID table entries...\n", count
);
443 /* convert all entries not covered by live convert */
445 rc
= sidtab_convert_tree(¶ms
->target
->roots
[level
],
446 &s
->roots
[level
], &pos
, count
, level
, params
);
448 /* we need to keep the old table - disable live convert */
449 spin_lock_irqsave(&s
->lock
, flags
);
451 spin_unlock_irqrestore(&s
->lock
, flags
);
456 static void sidtab_destroy_tree(union sidtab_entry_inner entry
, u32 level
)
461 struct sidtab_node_inner
*node
= entry
.ptr_inner
;
466 for (i
= 0; i
< SIDTAB_INNER_ENTRIES
; i
++)
467 sidtab_destroy_tree(node
->entries
[i
], level
- 1);
470 struct sidtab_node_leaf
*node
= entry
.ptr_leaf
;
475 for (i
= 0; i
< SIDTAB_LEAF_ENTRIES
; i
++)
476 context_destroy(&node
->entries
[i
].context
);
481 void sidtab_destroy(struct sidtab
*s
)
485 for (i
= 0; i
< SECINITSID_NUM
; i
++)
487 context_destroy(&s
->isids
[i
].context
);
489 level
= SIDTAB_MAX_LEVEL
;
490 while (level
&& !s
->roots
[level
].ptr_inner
)
493 sidtab_destroy_tree(s
->roots
[level
], level
);