There are some LSM hooks which do not use the common pattern followed by other LSM hooks and thus cannot use call_{int, void}_hook macros and instead use lsm_for_each_hook macro which still results in indirect call. There is one additional generalizable pattern where a hook matching an lsmid is called and the indirect calls for these are addressed with the newly added call_hook_with_lsmid macro which internally uses an implementation similar to call_int_hook but has an additional check that matches the lsmid. For the generic case the lsm_for_each_hook macro is updated to accept logic before and after the invocation of the LSM hook (static call) in the unrolled loop. Signed-off-by: KP Singh <kpsingh@xxxxxxxxxx> --- security/security.c | 229 ++++++++++++++++++++++++-------------------- 1 file changed, 125 insertions(+), 104 deletions(-) diff --git a/security/security.c b/security/security.c index 39ffe949e509..491b807a8a63 100644 --- a/security/security.c +++ b/security/security.c @@ -945,10 +945,41 @@ out: \ RC; \ }) -#define lsm_for_each_hook(scall, NAME) \ - for (scall = static_calls_table.NAME; \ - scall - static_calls_table.NAME < MAX_LSM_COUNT; scall++) \ - if (static_key_enabled(&scall->active->key)) +/* + * Can be used in the context passed to lsm_for_each_hook to get the lsmid of the + * current hook + */ +#define current_lsmid() _hook_lsmid + +#define __CALL_HOOK(NUM, HOOK, RC, BODY_BEFORE, BODY_AFTER, ...) \ +do { \ + int __maybe_unused _hook_lsmid; \ + \ + if (static_branch_unlikely(&SECURITY_HOOK_ACTIVE_KEY(HOOK, NUM))) { \ + _hook_lsmid = static_calls_table.HOOK[NUM].hl->lsmid->id; \ + BODY_BEFORE \ + RC = static_call(LSM_STATIC_CALL(HOOK, NUM))(__VA_ARGS__); \ + BODY_AFTER \ + } \ +} while (0); + +#define lsm_for_each_hook(HOOK, RC, BODY, ...) \ + LSM_LOOP_UNROLL(__CALL_HOOK, HOOK, RC, ;, BODY, __VA_ARGS__) + +#define call_hook_with_lsmid(HOOK, LSMID, ...) \ +({ \ + __label__ out; \ + int RC = LSM_RET_DEFAULT(HOOK); \ + \ + LSM_LOOP_UNROLL(__CALL_HOOK, HOOK, RC, { \ + if (current_lsmid() != LSMID) \ + continue; \ + }, { \ + goto out; \ + }, __VA_ARGS__); \ +out: \ + RC; \ +}) /* Security operations */ @@ -1184,7 +1215,6 @@ int security_settime64(const struct timespec64 *ts, const struct timezone *tz) */ int security_vm_enough_memory_mm(struct mm_struct *mm, long pages) { - struct lsm_static_call *scall; int cap_sys_admin = 1; int rc; @@ -1195,13 +1225,18 @@ int security_vm_enough_memory_mm(struct mm_struct *mm, long pages) * agree that it should be set it will. If any module * thinks it should not be set it won't. */ - lsm_for_each_hook(scall, vm_enough_memory) { - rc = scall->hl->hook.vm_enough_memory(mm, pages); - if (rc <= 0) { - cap_sys_admin = 0; - break; - } - } + + lsm_for_each_hook( + vm_enough_memory, rc, + { + if (rc <= 0) { + cap_sys_admin = 0; + goto out; + } + }, + mm, pages); + +out: return __vm_enough_memory(mm, pages, cap_sys_admin); } @@ -1343,17 +1378,19 @@ int security_fs_context_dup(struct fs_context *fc, struct fs_context *src_fc) int security_fs_context_parse_param(struct fs_context *fc, struct fs_parameter *param) { - struct lsm_static_call *scall; - int trc; + int trc = LSM_RET_DEFAULT(fs_context_parse_param); int rc = -ENOPARAM; - lsm_for_each_hook(scall, fs_context_parse_param) { - trc = scall->hl->hook.fs_context_parse_param(fc, param); - if (trc == 0) - rc = 0; - else if (trc != -ENOPARAM) - return trc; - } + lsm_for_each_hook( + fs_context_parse_param, trc, + { + if (trc == 0) + rc = 0; + else if (trc != -ENOPARAM) + return trc; + }, + fc, param); + return rc; } @@ -1578,15 +1615,17 @@ int security_sb_set_mnt_opts(struct super_block *sb, unsigned long kern_flags, unsigned long *set_kern_flags) { - struct lsm_static_call *scall; int rc = mnt_opts ? -EOPNOTSUPP : LSM_RET_DEFAULT(sb_set_mnt_opts); - lsm_for_each_hook(scall, sb_set_mnt_opts) { - rc = scall->hl->hook.sb_set_mnt_opts(sb, mnt_opts, kern_flags, - set_kern_flags); - if (rc != LSM_RET_DEFAULT(sb_set_mnt_opts)) - break; - } + lsm_for_each_hook( + sb_set_mnt_opts, rc, + { + if (rc != LSM_RET_DEFAULT(sb_set_mnt_opts)) + goto out; + }, + sb, mnt_opts, kern_flags, set_kern_flags); + +out: return rc; } EXPORT_SYMBOL(security_sb_set_mnt_opts); @@ -1777,7 +1816,6 @@ int security_inode_init_security(struct inode *inode, struct inode *dir, const struct qstr *qstr, const initxattrs initxattrs, void *fs_data) { - struct lsm_static_call *scall; struct xattr *new_xattrs = NULL; int ret = -EOPNOTSUPP, xattr_count = 0; @@ -1795,18 +1833,19 @@ int security_inode_init_security(struct inode *inode, struct inode *dir, return -ENOMEM; } - lsm_for_each_hook(scall, inode_init_security) { - ret = scall->hl->hook.inode_init_security(inode, dir, qstr, new_xattrs, - &xattr_count); - if (ret && ret != -EOPNOTSUPP) - goto out; + lsm_for_each_hook( + inode_init_security, ret, + { /* * As documented in lsm_hooks.h, -EOPNOTSUPP in this context * means that the LSM is not willing to provide an xattr, not * that it wants to signal an error. Thus, continue to invoke * the remaining LSMs. */ - } + if (ret && ret != -EOPNOTSUPP) + goto out; + }, + inode, dir, qstr, new_xattrs, &xattr_count); /* If initxattrs() is NULL, xattr_count is zero, skip the call. */ if (!xattr_count) @@ -3601,16 +3640,19 @@ int security_task_prctl(int option, unsigned long arg2, unsigned long arg3, { int thisrc; int rc = LSM_RET_DEFAULT(task_prctl); - struct lsm_static_call *scall; - - lsm_for_each_hook(scall, task_prctl) { - thisrc = scall->hl->hook.task_prctl(option, arg2, arg3, arg4, arg5); - if (thisrc != LSM_RET_DEFAULT(task_prctl)) { - rc = thisrc; - if (thisrc != 0) - break; - } - } + + lsm_for_each_hook( + task_prctl, thisrc, + { + if (thisrc != LSM_RET_DEFAULT(task_prctl)) { + rc = thisrc; + if (thisrc != 0) + goto out; + } + }, + option, arg2, arg3, arg4, arg5); + +out: return rc; } @@ -4010,7 +4052,6 @@ EXPORT_SYMBOL(security_d_instantiate); int security_getselfattr(unsigned int attr, struct lsm_ctx __user *uctx, u32 __user *size, u32 flags) { - struct lsm_static_call *scall; struct lsm_ctx lctx = { .id = LSM_ID_UNDEF, }; u8 __user *base = (u8 __user *)uctx; u32 entrysize; @@ -4048,31 +4089,40 @@ int security_getselfattr(unsigned int attr, struct lsm_ctx __user *uctx, * In the usual case gather all the data from the LSMs. * In the single case only get the data from the LSM specified. */ - lsm_for_each_hook(scall, getselfattr) { - if (single && lctx.id != scall->hl->lsmid->id) - continue; - entrysize = left; - if (base) - uctx = (struct lsm_ctx __user *)(base + total); - rc = scall->hl->hook.getselfattr(attr, uctx, &entrysize, flags); - if (rc == -EOPNOTSUPP) { - rc = 0; - continue; - } - if (rc == -E2BIG) { - rc = 0; - left = 0; - toobig = true; - } else if (rc < 0) - return rc; - else - left -= entrysize; + LSM_LOOP_UNROLL( + __CALL_HOOK, getselfattr, rc, + /* BODY_BEFORE */ + { + if (single && lctx.id != current_lsmid()) + continue; + entrysize = left; + if (base) + uctx = (struct lsm_ctx __user *)(base + total); + }, + /* BODY_AFTER */ + { + if (rc == -EOPNOTSUPP) { + rc = 0; + } else { + if (rc == -E2BIG) { + rc = 0; + left = 0; + toobig = true; + } else if (rc < 0) + return rc; + else + left -= entrysize; + + total += entrysize; + count += rc; + if (single) + goto out; + } + }, + attr, uctx, &entrysize, flags); + +out: - total += entrysize; - count += rc; - if (single) - break; - } if (put_user(total, size)) return -EFAULT; if (toobig) @@ -4103,9 +4153,8 @@ int security_getselfattr(unsigned int attr, struct lsm_ctx __user *uctx, int security_setselfattr(unsigned int attr, struct lsm_ctx __user *uctx, u32 size, u32 flags) { - struct lsm_static_call *scall; struct lsm_ctx *lctx; - int rc = LSM_RET_DEFAULT(setselfattr); + int rc; u64 required_len; if (flags) @@ -4126,11 +4175,7 @@ int security_setselfattr(unsigned int attr, struct lsm_ctx __user *uctx, goto free_out; } - lsm_for_each_hook(scall, setselfattr) - if ((scall->hl->lsmid->id) == lctx->id) { - rc = scall->hl->hook.setselfattr(attr, lctx, size, flags); - break; - } + rc = call_hook_with_lsmid(setselfattr, lctx->id, attr, lctx, size, flags); free_out: kfree(lctx); @@ -4151,14 +4196,7 @@ int security_setselfattr(unsigned int attr, struct lsm_ctx __user *uctx, int security_getprocattr(struct task_struct *p, int lsmid, const char *name, char **value) { - struct lsm_static_call *scall; - - lsm_for_each_hook(scall, getprocattr) { - if (lsmid != 0 && lsmid != scall->hl->lsmid->id) - continue; - return scall->hl->hook.getprocattr(p, name, value); - } - return LSM_RET_DEFAULT(getprocattr); + return call_hook_with_lsmid(getprocattr, lsmid, p, name, value); } /** @@ -4175,14 +4213,7 @@ int security_getprocattr(struct task_struct *p, int lsmid, const char *name, */ int security_setprocattr(int lsmid, const char *name, void *value, size_t size) { - struct lsm_static_call *scall; - - lsm_for_each_hook(scall, setprocattr) { - if (lsmid != 0 && lsmid != scall->hl->lsmid->id) - continue; - return scall->hl->hook.setprocattr(name, value, size); - } - return LSM_RET_DEFAULT(setprocattr); + return call_hook_with_lsmid(setprocattr, lsmid, name, value, size); } /** @@ -5267,23 +5298,13 @@ int security_xfrm_state_pol_flow_match(struct xfrm_state *x, struct xfrm_policy *xp, const struct flowi_common *flic) { - struct lsm_static_call *scall; - int rc = LSM_RET_DEFAULT(xfrm_state_pol_flow_match); - /* * Since this function is expected to return 0 or 1, the judgment * becomes difficult if multiple LSMs supply this call. Fortunately, * we can use the first LSM's judgment because currently only SELinux * supplies this call. - * - * For speed optimization, we explicitly break the loop rather than - * using the macro */ - lsm_for_each_hook(scall, xfrm_state_pol_flow_match) { - rc = scall->hl->hook.xfrm_state_pol_flow_match(x, xp, flic); - break; - } - return rc; + return call_int_hook(xfrm_state_pol_flow_match, x, xp, flic); } /** -- 2.45.0.118.g7fe29c98d7-goog