From cc5ba517af57094d5c6f092e9e4fb5acc44a2ccd Mon Sep 17 00:00:00 2001 From: Paul Renauld Date: Tue, 25 Aug 2020 17:27:04 +0200 Subject: [PATCH] security: Allow more LSM callbacks than available slots Introduce a slow path fallback when the number of callbacks for a LSM hook is greater than the number of defined static slots. The static slots are filled, and the callbacks that do not fit are called using the linked list before the static calls. --- security/security.c | 45 +++++++++++++++++++++++++++++++++------------ 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/security/security.c b/security/security.c index 45a3998c09502f..e061c75d387e83 100644 --- a/security/security.c +++ b/security/security.c @@ -436,28 +436,25 @@ static void __init lsm_init_hook_static_slot(struct static_slot *slots, int *first_slot_idx) { struct security_hook_list *pos; - struct static_slot *slot; - int slot_cnt; + int slot_cnt, slot_idx; slot_cnt = 0; hlist_for_each_entry_rcu(pos, head, list) slot_cnt++; - if (slot_cnt > SECURITY_STATIC_SLOT_COUNT) - panic("%s - No static hook slot remaining to add LSM hook.\n", - __func__); - if (slot_cnt == 0) { *first_slot_idx = INT_MAX; return; } *first_slot_idx = SECURITY_STATIC_SLOT_COUNT - slot_cnt; - slot = slots + *first_slot_idx; + slot_idx = *first_slot_idx; hlist_for_each_entry_rcu(pos, head, list) { - __static_call_update(slot->key, slot->trampoline, - pos->hook.generic_func); - slot++; + if (slot_idx >= 0) + __static_call_update(slots[slot_idx].key, + slots[slot_idx].trampoline, + pos->hook.generic_func); + slot_idx++; } } @@ -869,9 +866,20 @@ static void __init lsm_early_task(struct task_struct *task) #define call_void_hook(FUNC, ...) do { \ switch (base_slot_idx.FUNC) { \ + default : { /* Callbacks that don't fit in the static slots */ \ + struct security_hook_list *__hook_list_pos; \ + int __remaining = base_slot_idx.FUNC; \ + hlist_for_each_entry(__hook_list_pos, \ + &security_hook_heads.FUNC, list) { \ + __hook_list_pos->hook.FUNC(__VA_ARGS__); \ + if (++__remaining >= 0) \ + break; \ + } \ + fallthrough; \ + } \ SECURITY_FOREACH_STATIC_SLOT(__CASE_CALL_STATIC_VOID, \ FUNC, __VA_ARGS__) \ - default : \ + case INT_MAX: /* No callback registered */ \ break; \ } \ } while (0) @@ -886,9 +894,22 @@ static void __init lsm_early_task(struct task_struct *task) #define call_int_hook(FUNC, IRC, ...) ({ \ int RC = IRC; \ switch (base_slot_idx.FUNC) { \ + default : { /* Callbacks that don't fit in the static slots */ \ + struct security_hook_list *__hook_list_pos; \ + int __remaining = base_slot_idx.FUNC; \ + hlist_for_each_entry(__hook_list_pos, \ + &security_hook_heads.FUNC, list) { \ + RC = __hook_list_pos->hook.FUNC(__VA_ARGS__); \ + if (RC != 0 || ++__remaining >= 0) \ + break; \ + } \ + if (RC != 0) \ + break; \ + fallthrough; \ + } \ SECURITY_FOREACH_STATIC_SLOT(__CASE_CALL_STATIC_INT, \ RC, FUNC, __VA_ARGS__) \ - default : \ + case INT_MAX: /* No callback registered */ \ break; \ } \ RC; \