From: Naoya Horiguchi naoya.horiguchi@nec.com
mainline inclusion from mainline-v5.13 commit ea6d0630100b285f059d0a8d8e86f38a46407536 category: bugfix bugzilla: 188222, https://gitee.com/openeuler/kernel/issues/I69SHF CVE: NA
--------------------------------
Currently me_huge_page() temporary unlocks page to perform some actions then locks it again later. My testcase (which calls hard-offline on some tail page in a hugetlb, then accesses the address of the hugetlb range) showed that page allocation code detects this page lock on buddy page and printed out "BUG: Bad page state" message.
check_new_page_bad() does not consider a page with __PG_HWPOISON as bad page, so this flag works as kind of filter, but this filtering doesn't work in this case because the "bad page" is not the actual hwpoisoned page. So stop locking page again. Actions to be taken depend on the page type of the error, so page unlocking should be done in ->action() callbacks. So let's make it assumed and change all existing callbacks that way.
Link: https://lkml.kernel.org/r/20210609072029.74645-1-nao.horiguchi@gmail.com Fixes: commit 78bb920344b8 ("mm: hwpoison: dissolve in-use hugepage in unrecoverable memory error") Signed-off-by: Naoya Horiguchi naoya.horiguchi@nec.com Cc: Oscar Salvador osalvador@suse.de Cc: Michal Hocko mhocko@suse.com Cc: Tony Luck tony.luck@intel.com Cc: "Aneesh Kumar K.V" aneesh.kumar@linux.vnet.ibm.com Cc: stable@vger.kernel.org Signed-off-by: Andrew Morton akpm@linux-foundation.org Signed-off-by: Linus Torvalds torvalds@linux-foundation.org Signed-off-by: Ma Wupeng mawupeng1@huawei.com Reviewed-by: Kefeng Wang wangkefeng.wang@huawei.com Signed-off-by: Yongqiang Liu liuyongqiang13@huawei.com --- mm/memory-failure.c | 44 ++++++++++++++++++++++++++++++-------------- 1 file changed, 30 insertions(+), 14 deletions(-)
diff --git a/mm/memory-failure.c b/mm/memory-failure.c index 55c175f57223..9a816fdf812d 100644 --- a/mm/memory-failure.c +++ b/mm/memory-failure.c @@ -662,6 +662,7 @@ static int truncate_error_page(struct page *p, unsigned long pfn, */ static int me_kernel(struct page *p, unsigned long pfn) { + unlock_page(p); return MF_IGNORED; }
@@ -671,6 +672,7 @@ static int me_kernel(struct page *p, unsigned long pfn) static int me_unknown(struct page *p, unsigned long pfn) { pr_err("Memory failure: %#lx: Unknown page state\n", pfn); + unlock_page(p); return MF_FAILED; }
@@ -679,6 +681,7 @@ static int me_unknown(struct page *p, unsigned long pfn) */ static int me_pagecache_clean(struct page *p, unsigned long pfn) { + int ret; struct address_space *mapping;
delete_from_lru_cache(p); @@ -687,8 +690,10 @@ static int me_pagecache_clean(struct page *p, unsigned long pfn) * For anonymous pages we're done the only reference left * should be the one m_f() holds. */ - if (PageAnon(p)) - return MF_RECOVERED; + if (PageAnon(p)) { + ret = MF_RECOVERED; + goto out; + }
/* * Now truncate the page in the page cache. This is really @@ -702,7 +707,8 @@ static int me_pagecache_clean(struct page *p, unsigned long pfn) /* * Page has been teared down in the meanwhile */ - return MF_FAILED; + ret = MF_FAILED; + goto out; }
/* @@ -710,7 +716,10 @@ static int me_pagecache_clean(struct page *p, unsigned long pfn) * * Open: to take i_mutex or not for this? Right now we don't. */ - return truncate_error_page(p, pfn, mapping); + ret = truncate_error_page(p, pfn, mapping); +out: + unlock_page(p); + return ret; }
/* @@ -786,24 +795,26 @@ static int me_pagecache_dirty(struct page *p, unsigned long pfn) */ static int me_swapcache_dirty(struct page *p, unsigned long pfn) { + int ret; + ClearPageDirty(p); /* Trigger EIO in shmem: */ ClearPageUptodate(p);
- if (!delete_from_lru_cache(p)) - return MF_DELAYED; - else - return MF_FAILED; + ret = delete_from_lru_cache(p) ? MF_FAILED : MF_DELAYED; + unlock_page(p); + return ret; }
static int me_swapcache_clean(struct page *p, unsigned long pfn) { + int ret; + delete_from_swap_cache(p);
- if (!delete_from_lru_cache(p)) - return MF_RECOVERED; - else - return MF_FAILED; + ret = delete_from_lru_cache(p) ? MF_FAILED : MF_RECOVERED; + unlock_page(p); + return ret; }
/* @@ -824,6 +835,7 @@ static int me_huge_page(struct page *p, unsigned long pfn) mapping = page_mapping(hpage); if (mapping) { res = truncate_error_page(hpage, pfn, mapping); + unlock_page(hpage); } else { res = MF_FAILED; unlock_page(hpage); @@ -838,7 +850,6 @@ static int me_huge_page(struct page *p, unsigned long pfn) page_ref_inc(p); res = MF_RECOVERED; } - lock_page(hpage); }
return res; @@ -871,6 +882,8 @@ static struct page_state { unsigned long mask; unsigned long res; enum mf_action_page_type type; + + /* Callback ->action() has to unlock the relevant page inside it. */ int (*action)(struct page *p, unsigned long pfn); } error_states[] = { { reserved, reserved, MF_MSG_KERNEL, me_kernel }, @@ -935,6 +948,7 @@ static int page_action(struct page_state *ps, struct page *p, int result; int count;
+ /* page p should be unlocked after returning from ps->action(). */ result = ps->action(p, pfn);
count = page_count(p) - 1; @@ -1235,7 +1249,7 @@ static int memory_failure_hugetlb(unsigned long pfn, int flags) goto out; }
- res = identify_page_state(pfn, p, page_flags); + return identify_page_state(pfn, p, page_flags); out: unlock_page(head); return res; @@ -1533,6 +1547,8 @@ int memory_failure(unsigned long pfn, int flags)
identify_page_state: res = identify_page_state(pfn, p, page_flags); + mutex_unlock(&mf_mutex); + return res; unlock_page: unlock_page(p); unlock_mutex:
From: Janne Karhunen janne.karhunen@gmail.com
mainline inclusion from mainline-v5.3-rc1 commit 42df744c4166af6959eda2df1ee5cde744d4a1c3 category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/I692HU CVE: NA
--------------------------------
Atomic policy updaters are not very useful as they cannot usually perform the policy updates on their own. Since it seems that there is no strict need for the atomicity, switch to the blocking variant. While doing so, rename the functions accordingly.
Signed-off-by: Janne Karhunen janne.karhunen@gmail.com Acked-by: Paul Moore paul@paul-moore.com Acked-by: James Morris jamorris@linux.microsoft.com Signed-off-by: Mimi Zohar zohar@linux.ibm.com Conflicts: drivers/infiniband/core/security.c Signed-off-by: GUO Zihua guozihua@huawei.com Reviewed-by: Xiu Jianfeng xiujianfeng@huawei.com Signed-off-by: Yongqiang Liu liuyongqiang13@huawei.com --- drivers/infiniband/core/device.c | 4 ++-- drivers/infiniband/core/security.c | 4 ++-- include/linux/security.h | 12 ++++++------ security/security.c | 23 +++++++++++++---------- security/selinux/hooks.c | 2 +- security/selinux/selinuxfs.c | 2 +- 6 files changed, 25 insertions(+), 22 deletions(-)
diff --git a/drivers/infiniband/core/device.c b/drivers/infiniband/core/device.c index 92ab95383d00..9edc85d44264 100644 --- a/drivers/infiniband/core/device.c +++ b/drivers/infiniband/core/device.c @@ -1226,7 +1226,7 @@ static int __init ib_core_init(void) goto err_mad; }
- ret = register_lsm_notifier(&ibdev_lsm_nb); + ret = register_blocking_lsm_notifier(&ibdev_lsm_nb); if (ret) { pr_warn("Couldn't register LSM notifier. ret %d\n", ret); goto err_sa; @@ -1262,7 +1262,7 @@ static void __exit ib_core_cleanup(void) roce_gid_mgmt_cleanup(); nldev_exit(); rdma_nl_unregister(RDMA_NL_LS); - unregister_lsm_notifier(&ibdev_lsm_nb); + unregister_blocking_lsm_notifier(&ibdev_lsm_nb); ib_sa_cleanup(); ib_mad_cleanup(); addr_cleanup(); diff --git a/drivers/infiniband/core/security.c b/drivers/infiniband/core/security.c index b79b61bd6ee4..981de7e3338e 100644 --- a/drivers/infiniband/core/security.c +++ b/drivers/infiniband/core/security.c @@ -714,7 +714,7 @@ int ib_mad_agent_security_setup(struct ib_mad_agent *agent, goto free_security;
agent->lsm_nb.notifier_call = ib_mad_agent_security_change; - ret = register_lsm_notifier(&agent->lsm_nb); + ret = register_blocking_lsm_notifier(&agent->lsm_nb); if (ret) goto free_security;
@@ -733,7 +733,7 @@ void ib_mad_agent_security_cleanup(struct ib_mad_agent *agent) return;
if (agent->lsm_nb_reg) - unregister_lsm_notifier(&agent->lsm_nb); + unregister_blocking_lsm_notifier(&agent->lsm_nb);
security_ib_free_security(agent->security); } diff --git a/include/linux/security.h b/include/linux/security.h index 9eb1c7a0f280..937b130fa17b 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -188,9 +188,9 @@ struct security_mnt_opts { int num_mnt_opts; };
-int call_lsm_notifier(enum lsm_event event, void *data); -int register_lsm_notifier(struct notifier_block *nb); -int unregister_lsm_notifier(struct notifier_block *nb); +int call_blocking_lsm_notifier(enum lsm_event event, void *data); +int register_blocking_lsm_notifier(struct notifier_block *nb); +int unregister_blocking_lsm_notifier(struct notifier_block *nb);
static inline void security_init_mnt_opts(struct security_mnt_opts *opts) { @@ -406,17 +406,17 @@ int security_inode_getsecctx(struct inode *inode, void **ctx, u32 *ctxlen); struct security_mnt_opts { };
-static inline int call_lsm_notifier(enum lsm_event event, void *data) +static inline int call_blocking_lsm_notifier(enum lsm_event event, void *data) { return 0; }
-static inline int register_lsm_notifier(struct notifier_block *nb) +static inline int register_blocking_lsm_notifier(struct notifier_block *nb) { return 0; }
-static inline int unregister_lsm_notifier(struct notifier_block *nb) +static inline int unregister_blocking_lsm_notifier(struct notifier_block *nb) { return 0; } diff --git a/security/security.c b/security/security.c index 8a9e1ececd7d..cc190c983fcb 100644 --- a/security/security.c +++ b/security/security.c @@ -38,7 +38,7 @@ #define SECURITY_NAME_MAX 10
struct security_hook_heads security_hook_heads __lsm_ro_after_init; -static ATOMIC_NOTIFIER_HEAD(lsm_notifier_chain); +static BLOCKING_NOTIFIER_HEAD(blocking_lsm_notifier_chain);
char *lsm_names; /* Boot-time LSM user choice */ @@ -180,23 +180,26 @@ void __init security_add_hooks(struct security_hook_list *hooks, int count, panic("%s - Cannot get early memory.\n", __func__); }
-int call_lsm_notifier(enum lsm_event event, void *data) +int call_blocking_lsm_notifier(enum lsm_event event, void *data) { - return atomic_notifier_call_chain(&lsm_notifier_chain, event, data); + return blocking_notifier_call_chain(&blocking_lsm_notifier_chain, + event, data); } -EXPORT_SYMBOL(call_lsm_notifier); +EXPORT_SYMBOL(call_blocking_lsm_notifier);
-int register_lsm_notifier(struct notifier_block *nb) +int register_blocking_lsm_notifier(struct notifier_block *nb) { - return atomic_notifier_chain_register(&lsm_notifier_chain, nb); + return blocking_notifier_chain_register(&blocking_lsm_notifier_chain, + nb); } -EXPORT_SYMBOL(register_lsm_notifier); +EXPORT_SYMBOL(register_blocking_lsm_notifier);
-int unregister_lsm_notifier(struct notifier_block *nb) +int unregister_blocking_lsm_notifier(struct notifier_block *nb) { - return atomic_notifier_chain_unregister(&lsm_notifier_chain, nb); + return blocking_notifier_chain_unregister(&blocking_lsm_notifier_chain, + nb); } -EXPORT_SYMBOL(unregister_lsm_notifier); +EXPORT_SYMBOL(unregister_blocking_lsm_notifier);
/* * Hook list operation macros. diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 056f5de53e7e..c44c95896f33 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -199,7 +199,7 @@ static int selinux_lsm_notifier_avc_callback(u32 event) { if (event == AVC_CALLBACK_RESET) { sel_ib_pkey_flush(); - call_lsm_notifier(LSM_POLICY_CHANGE, NULL); + call_blocking_lsm_notifier(LSM_POLICY_CHANGE, NULL); }
return 0; diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index 60b3f16bb5c7..4f72d0998580 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c @@ -180,7 +180,7 @@ static ssize_t sel_write_enforce(struct file *file, const char __user *buf, selnl_notify_setenforce(new_value); selinux_status_update_setenforce(state, new_value); if (!new_value) - call_lsm_notifier(LSM_POLICY_CHANGE, NULL); + call_blocking_lsm_notifier(LSM_POLICY_CHANGE, NULL); } length = count; out:
From: Janne Karhunen janne.karhunen@gmail.com
mainline inclusion from mainline-v5.3-rc1 commit b169424551930a9325f700f502802f4d515194e5 category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/I692HU CVE: NA
--------------------------------
Don't do lazy policy updates while running the rule matching, run the updates as they happen.
Depends on commit f242064c5df3 ("LSM: switch to blocking policy update notifiers")
Signed-off-by: Janne Karhunen janne.karhunen@gmail.com Signed-off-by: Mimi Zohar zohar@linux.ibm.com Conflicts: security/integrity/ima/ima_main.c Signed-off-by: GUO Zihua guozihua@huawei.com Reviewed-by: Xiu Jianfeng xiujianfeng@huawei.com Signed-off-by: Yongqiang Liu liuyongqiang13@huawei.com --- security/integrity/ima/ima.h | 2 + security/integrity/ima/ima_main.c | 8 ++ security/integrity/ima/ima_policy.c | 118 ++++++++++++++++++++++------ 3 files changed, 105 insertions(+), 23 deletions(-)
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index 0f1dabddb640..d821ef9df082 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -154,6 +154,8 @@ int ima_measurements_show(struct seq_file *m, void *v); unsigned long ima_get_binary_runtime_size(void); int ima_init_template(void); void ima_init_template_list(void); +int ima_lsm_policy_change(struct notifier_block *nb, unsigned long event, + void *lsm_data);
/* * used to protect h_table and sha_table diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index c08dbc55e5f9..c33f7eb6b544 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -41,6 +41,10 @@ int ima_appraise; int ima_hash_algo = HASH_ALGO_SHA256; static int hash_setup_done;
+static struct notifier_block ima_lsm_policy_notifier = { + .notifier_call = ima_lsm_policy_change, +}; + static int __init hash_setup(char *str) { struct ima_template_desc *template_desc = ima_template_desc_current(); @@ -556,6 +560,10 @@ static int __init init_ima(void) error = ima_init(); }
+ error = register_blocking_lsm_notifier(&ima_lsm_policy_notifier); + if (error) + pr_warn("Couldn't register LSM notifier, error %d\n", error); + if (!error) ima_update_policy_flag();
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index f68c9f3751f2..49b154f8ab67 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -241,46 +241,124 @@ static int __init default_appraise_policy_setup(char *str) } __setup("ima_appraise_tcb", default_appraise_policy_setup);
-static void ima_free_rule(struct ima_rule_entry *entry) +static void ima_lsm_free_rule(struct ima_rule_entry *entry) { int i;
+ for (i = 0; i < MAX_LSM_RULES; i++) { + security_filter_rule_free(entry->lsm[i].rule); + kfree(entry->lsm[i].args_p); + } +} + +static void ima_free_rule(struct ima_rule_entry *entry) +{ if (!entry) return;
kfree(entry->fsname); + ima_lsm_free_rule(entry); + kfree(entry); +} + +static struct ima_rule_entry *ima_lsm_copy_rule(struct ima_rule_entry *entry) +{ + struct ima_rule_entry *nentry; + int i, result; + + nentry = kmalloc(sizeof(*nentry), GFP_KERNEL); + if (!nentry) + return NULL; + + /* + * Immutable elements are copied over as pointers and data; only + * lsm rules can change + */ + memcpy(nentry, entry, sizeof(*nentry)); + memset(nentry->lsm, 0, FIELD_SIZEOF(struct ima_rule_entry, lsm)); + for (i = 0; i < MAX_LSM_RULES; i++) { - security_filter_rule_free(entry->lsm[i].rule); - kfree(entry->lsm[i].args_p); + if (!entry->lsm[i].rule) + continue; + + nentry->lsm[i].type = entry->lsm[i].type; + nentry->lsm[i].args_p = kstrdup(entry->lsm[i].args_p, + GFP_KERNEL); + if (!nentry->lsm[i].args_p) + goto out_err; + + result = security_filter_rule_init(nentry->lsm[i].type, + Audit_equal, + nentry->lsm[i].args_p, + &nentry->lsm[i].rule); + if (result == -EINVAL) + pr_warn("ima: rule for LSM '%d' is undefined\n", + entry->lsm[i].type); } + return nentry; + +out_err: + ima_lsm_free_rule(nentry); + kfree(nentry); + return NULL; +} + +static int ima_lsm_update_rule(struct ima_rule_entry *entry) +{ + struct ima_rule_entry *nentry; + + nentry = ima_lsm_copy_rule(entry); + if (!nentry) + return -ENOMEM; + + list_replace_rcu(&entry->list, &nentry->list); + synchronize_rcu(); + ima_lsm_free_rule(entry); kfree(entry); + + return 0; }
/* * The LSM policy can be reloaded, leaving the IMA LSM based rules referring * to the old, stale LSM policy. Update the IMA LSM based rules to reflect - * the reloaded LSM policy. We assume the rules still exist; and BUG_ON() if - * they don't. + * the reloaded LSM policy. */ static void ima_lsm_update_rules(void) { - struct ima_rule_entry *entry; - int result; - int i; + struct ima_rule_entry *entry, *e; + int i, result, needs_update;
- list_for_each_entry(entry, &ima_policy_rules, list) { + list_for_each_entry_safe(entry, e, &ima_policy_rules, list) { + needs_update = 0; for (i = 0; i < MAX_LSM_RULES; i++) { - if (!entry->lsm[i].rule) - continue; - result = security_filter_rule_init(entry->lsm[i].type, - Audit_equal, - entry->lsm[i].args_p, - &entry->lsm[i].rule); - BUG_ON(!entry->lsm[i].rule); + if (entry->lsm[i].rule) { + needs_update = 1; + break; + } + } + if (!needs_update) + continue; + + result = ima_lsm_update_rule(entry); + if (result) { + pr_err("ima: lsm rule update error %d\n", + result); + return; } } }
+int ima_lsm_policy_change(struct notifier_block *nb, unsigned long event, + void *lsm_data) +{ + if (event != LSM_POLICY_CHANGE) + return NOTIFY_DONE; + + ima_lsm_update_rules(); + return NOTIFY_OK; +} + /** * ima_match_rules - determine whether an inode matches the measure rule. * @rule: a pointer to a rule @@ -334,11 +412,10 @@ static bool ima_match_rules(struct ima_rule_entry *rule, struct inode *inode, for (i = 0; i < MAX_LSM_RULES; i++) { int rc = 0; u32 osid; - int retried = 0;
if (!rule->lsm[i].rule) continue; -retry: + switch (i) { case LSM_OBJ_USER: case LSM_OBJ_ROLE: @@ -361,11 +438,6 @@ static bool ima_match_rules(struct ima_rule_entry *rule, struct inode *inode, default: break; } - if ((rc < 0) && (!retried)) { - retried = 1; - ima_lsm_update_rules(); - goto retry; - } if (!rc) return false; }
From: GUO Zihua guozihua@huawei.com
mainline inclusion from mainline-v6.2-rc1 commit c7423dbdbc9ecef7fff5239d144cad4b9887f4de category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/I692HU CVE: NA
--------------------------------
IMA relies on the blocking LSM policy notifier callback to update the LSM based IMA policy rules.
When SELinux update its policies, IMA would be notified and starts updating all its lsm rules one-by-one. During this time, -ESTALE would be returned by ima_filter_rule_match() if it is called with a LSM rule that has not yet been updated. In ima_match_rules(), -ESTALE is not handled, and the LSM rule is considered a match, causing extra files to be measured by IMA.
Fix it by re-initializing a temporary rule if -ESTALE is returned by ima_filter_rule_match(). The origin rule in the rule list would be updated by the LSM policy notifier callback.
Fixes: b16942455193 ("ima: use the lsm policy update notifier") Signed-off-by: GUO Zihua guozihua@huawei.com Reviewed-by: Roberto Sassu roberto.sassu@huawei.com Signed-off-by: Mimi Zohar zohar@linux.ibm.com Conflicts: security/integrity/ima/ima_policy.c Signed-off-by: GUO Zihua guozihua@huawei.com Reviewed-by: Xiu Jianfeng xiujianfeng@huawei.com Signed-off-by: Yongqiang Liu liuyongqiang13@huawei.com --- security/integrity/ima/ima_policy.c | 37 ++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 8 deletions(-)
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index 49b154f8ab67..b3ce17203db9 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -375,6 +375,9 @@ static bool ima_match_rules(struct ima_rule_entry *rule, struct inode *inode, enum ima_hooks func, int mask) { int i; + bool result = false; + struct ima_rule_entry *lsm_rule = rule; + bool rule_reinitialized = false;
if ((rule->flags & IMA_FUNC) && (rule->func != func && func != POST_SETATTR)) @@ -413,35 +416,53 @@ static bool ima_match_rules(struct ima_rule_entry *rule, struct inode *inode, int rc = 0; u32 osid;
- if (!rule->lsm[i].rule) + if (!lsm_rule->lsm[i].rule) continue;
+retry: switch (i) { case LSM_OBJ_USER: case LSM_OBJ_ROLE: case LSM_OBJ_TYPE: security_inode_getsecid(inode, &osid); rc = security_filter_rule_match(osid, - rule->lsm[i].type, + lsm_rule->lsm[i].type, Audit_equal, - rule->lsm[i].rule, + lsm_rule->lsm[i].rule, NULL); break; case LSM_SUBJ_USER: case LSM_SUBJ_ROLE: case LSM_SUBJ_TYPE: rc = security_filter_rule_match(secid, - rule->lsm[i].type, + lsm_rule->lsm[i].type, Audit_equal, - rule->lsm[i].rule, + lsm_rule->lsm[i].rule, NULL); default: break; } - if (!rc) - return false; + + if (rc == -ESTALE && !rule_reinitialized) { + lsm_rule = ima_lsm_copy_rule(rule); + if (lsm_rule) { + rule_reinitialized = true; + goto retry; + } + } + if (!rc) { + result = false; + goto out; + } + } + result = true; + +out: + if (rule_reinitialized) { + ima_lsm_free_rule(lsm_rule); + kfree(lsm_rule); } - return true; + return result; }
/*
From: Janne Karhunen janne.karhunen@gmail.com
mainline inclusion from mainline-v5.6-rc1 commit 483ec26eed42bf050931d9a5c5f9f0b5f2ad5f3b category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/I692HU CVE: NA
--------------------------------
Keep the ima policy rules around from the beginning even if they appear invalid at the time of loading, as they may become active after an lsm policy load. However, loading a custom IMA policy with unknown LSM labels is only safe after we have transitioned from the "built-in" policy rules to a custom IMA policy.
Patch also fixes the rule re-use during the lsm policy reload and makes some prints a bit more human readable.
Changelog: v4: - Do not allow the initial policy load refer to non-existing lsm rules. v3: - Fix too wide policy rule matching for non-initialized LSMs v2: - Fix log prints
Fixes: b16942455193 ("ima: use the lsm policy update notifier") Cc: Casey Schaufler casey@schaufler-ca.com Reported-by: Mimi Zohar zohar@linux.ibm.com Signed-off-by: Janne Karhunen janne.karhunen@gmail.com Signed-off-by: Konsta Karsisto konsta.karsisto@gmail.com Signed-off-by: Mimi Zohar zohar@linux.ibm.com Conflicts: security/integrity/ima/ima_policy.c Signed-off-by: GUO Zihua guozihua@huawei.com Reviewed-by: Xiu Jianfeng xiujianfeng@huawei.com Signed-off-by: Yongqiang Liu liuyongqiang13@huawei.com --- security/integrity/ima/ima_policy.c | 45 +++++++++++++++++------------ 1 file changed, 27 insertions(+), 18 deletions(-)
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index b3ce17203db9..2aff6f9ef0e9 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -264,7 +264,7 @@ static void ima_free_rule(struct ima_rule_entry *entry) static struct ima_rule_entry *ima_lsm_copy_rule(struct ima_rule_entry *entry) { struct ima_rule_entry *nentry; - int i, result; + int i;
nentry = kmalloc(sizeof(*nentry), GFP_KERNEL); if (!nentry) @@ -278,7 +278,7 @@ static struct ima_rule_entry *ima_lsm_copy_rule(struct ima_rule_entry *entry) memset(nentry->lsm, 0, FIELD_SIZEOF(struct ima_rule_entry, lsm));
for (i = 0; i < MAX_LSM_RULES; i++) { - if (!entry->lsm[i].rule) + if (!entry->lsm[i].args_p) continue;
nentry->lsm[i].type = entry->lsm[i].type; @@ -287,13 +287,13 @@ static struct ima_rule_entry *ima_lsm_copy_rule(struct ima_rule_entry *entry) if (!nentry->lsm[i].args_p) goto out_err;
- result = security_filter_rule_init(nentry->lsm[i].type, - Audit_equal, - nentry->lsm[i].args_p, - &nentry->lsm[i].rule); - if (result == -EINVAL) - pr_warn("ima: rule for LSM '%d' is undefined\n", - entry->lsm[i].type); + security_filter_rule_init(nentry->lsm[i].type, + Audit_equal, + nentry->lsm[i].args_p, + &nentry->lsm[i].rule); + if (!nentry->lsm[i].rule) + pr_warn("rule for LSM '%s' is undefined\n", + (char *)entry->lsm[i].args_p); } return nentry;
@@ -332,7 +332,7 @@ static void ima_lsm_update_rules(void) list_for_each_entry_safe(entry, e, &ima_policy_rules, list) { needs_update = 0; for (i = 0; i < MAX_LSM_RULES; i++) { - if (entry->lsm[i].rule) { + if (entry->lsm[i].args_p) { needs_update = 1; break; } @@ -342,8 +342,7 @@ static void ima_lsm_update_rules(void)
result = ima_lsm_update_rule(entry); if (result) { - pr_err("ima: lsm rule update error %d\n", - result); + pr_err("lsm rule update error %d\n", result); return; } } @@ -360,7 +359,7 @@ int ima_lsm_policy_change(struct notifier_block *nb, unsigned long event, }
/** - * ima_match_rules - determine whether an inode matches the measure rule. + * ima_match_rules - determine whether an inode matches the policy rule. * @rule: a pointer to a rule * @inode: a pointer to an inode * @cred: a pointer to a credentials structure for user validation @@ -416,8 +415,12 @@ static bool ima_match_rules(struct ima_rule_entry *rule, struct inode *inode, int rc = 0; u32 osid;
- if (!lsm_rule->lsm[i].rule) - continue; + if (!lsm_rule->lsm[i].rule) { + if (!lsm_rule->lsm[i].args_p) + continue; + else + return false; + }
retry: switch (i) { @@ -755,9 +758,15 @@ static int ima_lsm_rule_init(struct ima_rule_entry *entry, entry->lsm[lsm_rule].args_p, &entry->lsm[lsm_rule].rule); if (!entry->lsm[lsm_rule].rule) { - kfree(entry->lsm[lsm_rule].args_p); - entry->lsm[lsm_rule].args_p = NULL; - return -EINVAL; + pr_warn("rule for LSM '%s' is undefined\n", + (char *)entry->lsm[lsm_rule].args_p); + + if (ima_rules == &ima_default_rules) { + kfree(entry->lsm[lsm_rule].args_p); + entry->lsm[lsm_rule].args_p = NULL; + result = -EINVAL; + } else + result = 0; }
return result;
From: Roberto Sassu roberto.sassu@huawei.com
mainline inclusion from mainline-v5.8-rc1 commit e144d6b265415ddbdc54b3f17f4f95133effa5a8 category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/I692HU CVE: NA
--------------------------------
Evaluate error in init_ima() before register_blocking_lsm_notifier() and return if not zero.
Cc: stable@vger.kernel.org # 5.3.x Fixes: b16942455193 ("ima: use the lsm policy update notifier") Signed-off-by: Roberto Sassu roberto.sassu@huawei.com Reviewed-by: James Morris jamorris@linux.microsoft.com Signed-off-by: Mimi Zohar zohar@linux.ibm.com Signed-off-by: GUO Zihua guozihua@huawei.com Reviewed-by: Xiu Jianfeng xiujianfeng@huawei.com Signed-off-by: Yongqiang Liu liuyongqiang13@huawei.com --- security/integrity/ima/ima_main.c | 3 +++ 1 file changed, 3 insertions(+)
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index c33f7eb6b544..834c8c927a1d 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -560,6 +560,9 @@ static int __init init_ima(void) error = ima_init(); }
+ if (error) + return error; + error = register_blocking_lsm_notifier(&ima_lsm_policy_notifier); if (error) pr_warn("Couldn't register LSM notifier, error %d\n", error);
From: GUO Zihua guozihua@huawei.com
hulk inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I692HU CVE: NA
--------------------------------
This patch is merged to resolve KABI changes introduces by the backporting of commit 42df744c4166 ("LSM: switch to blocking policy update notifiers")
Signed-off-by: GUO Zihua guozihua@huawei.com Reviewed-by: Xiu Jianfeng xiujianfeng@huawei.com Signed-off-by: Yongqiang Liu liuyongqiang13@huawei.com --- drivers/infiniband/core/device.c | 4 ++-- drivers/infiniband/core/security.c | 4 ++-- include/linux/security.h | 8 ++++---- security/integrity/ima/ima_main.c | 2 +- security/security.c | 8 ++++---- 5 files changed, 13 insertions(+), 13 deletions(-)
diff --git a/drivers/infiniband/core/device.c b/drivers/infiniband/core/device.c index 9edc85d44264..92ab95383d00 100644 --- a/drivers/infiniband/core/device.c +++ b/drivers/infiniband/core/device.c @@ -1226,7 +1226,7 @@ static int __init ib_core_init(void) goto err_mad; }
- ret = register_blocking_lsm_notifier(&ibdev_lsm_nb); + ret = register_lsm_notifier(&ibdev_lsm_nb); if (ret) { pr_warn("Couldn't register LSM notifier. ret %d\n", ret); goto err_sa; @@ -1262,7 +1262,7 @@ static void __exit ib_core_cleanup(void) roce_gid_mgmt_cleanup(); nldev_exit(); rdma_nl_unregister(RDMA_NL_LS); - unregister_blocking_lsm_notifier(&ibdev_lsm_nb); + unregister_lsm_notifier(&ibdev_lsm_nb); ib_sa_cleanup(); ib_mad_cleanup(); addr_cleanup(); diff --git a/drivers/infiniband/core/security.c b/drivers/infiniband/core/security.c index 981de7e3338e..b79b61bd6ee4 100644 --- a/drivers/infiniband/core/security.c +++ b/drivers/infiniband/core/security.c @@ -714,7 +714,7 @@ int ib_mad_agent_security_setup(struct ib_mad_agent *agent, goto free_security;
agent->lsm_nb.notifier_call = ib_mad_agent_security_change; - ret = register_blocking_lsm_notifier(&agent->lsm_nb); + ret = register_lsm_notifier(&agent->lsm_nb); if (ret) goto free_security;
@@ -733,7 +733,7 @@ void ib_mad_agent_security_cleanup(struct ib_mad_agent *agent) return;
if (agent->lsm_nb_reg) - unregister_blocking_lsm_notifier(&agent->lsm_nb); + unregister_lsm_notifier(&agent->lsm_nb);
security_ib_free_security(agent->security); } diff --git a/include/linux/security.h b/include/linux/security.h index 937b130fa17b..2960258279cb 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -189,8 +189,8 @@ struct security_mnt_opts { };
int call_blocking_lsm_notifier(enum lsm_event event, void *data); -int register_blocking_lsm_notifier(struct notifier_block *nb); -int unregister_blocking_lsm_notifier(struct notifier_block *nb); +int register_lsm_notifier(struct notifier_block *nb); +int unregister_lsm_notifier(struct notifier_block *nb);
static inline void security_init_mnt_opts(struct security_mnt_opts *opts) { @@ -411,12 +411,12 @@ static inline int call_blocking_lsm_notifier(enum lsm_event event, void *data) return 0; }
-static inline int register_blocking_lsm_notifier(struct notifier_block *nb) +static inline int register_lsm_notifier(struct notifier_block *nb) { return 0; }
-static inline int unregister_blocking_lsm_notifier(struct notifier_block *nb) +static inline int unregister_lsm_notifier(struct notifier_block *nb) { return 0; } diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index 834c8c927a1d..acdd5dcd74ae 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -563,7 +563,7 @@ static int __init init_ima(void) if (error) return error;
- error = register_blocking_lsm_notifier(&ima_lsm_policy_notifier); + error = register_lsm_notifier(&ima_lsm_policy_notifier); if (error) pr_warn("Couldn't register LSM notifier, error %d\n", error);
diff --git a/security/security.c b/security/security.c index cc190c983fcb..915f746d6fa6 100644 --- a/security/security.c +++ b/security/security.c @@ -187,19 +187,19 @@ int call_blocking_lsm_notifier(enum lsm_event event, void *data) } EXPORT_SYMBOL(call_blocking_lsm_notifier);
-int register_blocking_lsm_notifier(struct notifier_block *nb) +int register_lsm_notifier(struct notifier_block *nb) { return blocking_notifier_chain_register(&blocking_lsm_notifier_chain, nb); } -EXPORT_SYMBOL(register_blocking_lsm_notifier); +EXPORT_SYMBOL(register_lsm_notifier);
-int unregister_blocking_lsm_notifier(struct notifier_block *nb) +int unregister_lsm_notifier(struct notifier_block *nb) { return blocking_notifier_chain_unregister(&blocking_lsm_notifier_chain, nb); } -EXPORT_SYMBOL(unregister_blocking_lsm_notifier); +EXPORT_SYMBOL(unregister_lsm_notifier);
/* * Hook list operation macros.
From: Stefan Berger stefanb@linux.ibm.com
mainline inclusion from mainline-v5.17-rc4 commit 89677197ae709eb1ab3646952c44f6a171c9e74c category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/I692HU CVE: NA
--------------------------------
Before printing a policy rule scan for inactive LSM labels in the policy rule. Inactive LSM labels are identified by args_p != NULL and rule == NULL.
Fixes: 483ec26eed42 ("ima: ima/lsm policy rule loading logic bug fixes") Signed-off-by: Stefan Berger stefanb@linux.ibm.com Cc: stable@vger.kernel.org # v5.6+ Acked-by: Christian Brauner brauner@kernel.org [zohar@linux.ibm.com: Updated "Fixes" tag] Signed-off-by: Mimi Zohar zohar@linux.ibm.com Signed-off-by: GUO Zihua guozihua@huawei.com Reviewed-by: Xiu Jianfeng xiujianfeng@huawei.com Signed-off-by: Yongqiang Liu liuyongqiang13@huawei.com --- security/integrity/ima/ima_policy.c | 8 ++++++++ 1 file changed, 8 insertions(+)
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index 2aff6f9ef0e9..6cc862ce3563 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -1240,6 +1240,14 @@ int ima_policy_show(struct seq_file *m, void *v)
rcu_read_lock();
+ /* Do not print rules with inactive LSM labels */ + for (i = 0; i < MAX_LSM_RULES; i++) { + if (entry->lsm[i].args_p && !entry->lsm[i].rule) { + rcu_read_unlock(); + return 0; + } + } + if (entry->action & MEASURE) seq_puts(m, pt(Opt_measure)); if (entry->action & DONT_MEASURE)