hulk inclusion category: bugfix bugzilla: https://gitee.com/openeuler/release-management/issues/ID5CMS -------------------------------- After the process exits and xcall is unregistered, kernel module cannot be unloaded properly. The reason is that mmput() just need to reduce the refcount of 'mm->mm_users' to 0 which indicates that no user-space processes are using this address space, but mmdrop() need to reduce the refcount of 'mm->mm_count' to zero, which indicates that all references to the memory descriptor have reached zero and the mm_struct can now be safely freed and this condition may not be satisfied after process exit. So put_xcall() in mmdrop() can not guarantee the refcount of xcall will be decremented immediately, so module_put() in put_xcall() may not called. Fix this problem by split clear_xcall_area() into free_xcall_area() and clear_xcall_area(), call put_xcall() in mmput() and free the mm->xcall in mmdrop(). Fixes: b05676644e95 ("xcall2.0: Add xcall_area") Signed-off-by: Jinjie Ruan <ruanjinjie@huawei.com> --- arch/arm64/kernel/xcall/core.c | 8 ++++++++ include/linux/xcall.h | 2 ++ kernel/fork.c | 3 ++- 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/arch/arm64/kernel/xcall/core.c b/arch/arm64/kernel/xcall/core.c index a88c4ed6e575..f3a381c9106a 100644 --- a/arch/arm64/kernel/xcall/core.c +++ b/arch/arm64/kernel/xcall/core.c @@ -279,6 +279,14 @@ void clear_xcall_area(struct mm_struct *mm) if (area->xcall) put_xcall(area->xcall); +} + +void free_xcall_area(struct mm_struct *mm) +{ + struct xcall_area *area = mm_xcall_area(mm); + + if (!area) + return; kfree(area); mm->xcall = NULL; diff --git a/include/linux/xcall.h b/include/linux/xcall.h index 510aebe4e7c0..15da16728278 100644 --- a/include/linux/xcall.h +++ b/include/linux/xcall.h @@ -33,6 +33,7 @@ extern int xcall_prog_register(struct xcall_prog *prog); extern void xcall_prog_unregister(struct xcall_prog *prog); extern void mm_init_xcall_area(struct mm_struct *mm, struct task_struct *p); extern void clear_xcall_area(struct mm_struct *mm); +extern void free_xcall_area(struct mm_struct *mm); extern int xcall_mmap(struct vm_area_struct *vma, struct mm_struct *mm); #else /* !CONFIG_DYNAMIC_XCALL */ static inline int xcall_prog_register(struct xcall_prog *prog) @@ -42,6 +43,7 @@ static inline int xcall_prog_register(struct xcall_prog *prog) static inline void xcall_prog_unregister(struct xcall_prog *prog) {} static inline void mm_init_xcall_area(struct mm_struct *mm, struct task_struct *p) {} static inline void clear_xcall_area(struct mm_struct *mm) {} +extern void free_xcall_area(struct mm_struct *mm) {} static inline int xcall_mmap(struct vm_area_struct *vma, struct mm_struct *mm) { return 0; diff --git a/kernel/fork.c b/kernel/fork.c index f0271e915b0e..4c820dfbfd75 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -975,7 +975,7 @@ void __mmdrop(struct mm_struct *mm) mm_free_pgd(mm); destroy_context(mm); mmu_notifier_subscriptions_destroy(mm); - clear_xcall_area(mm); + free_xcall_area(mm); check_mm(mm); put_user_ns(mm->user_ns); mm_pasid_drop(mm); @@ -1429,6 +1429,7 @@ static inline void __mmput(struct mm_struct *mm) { VM_BUG_ON(atomic_read(&mm->mm_users)); + clear_xcall_area(mm); uprobe_clear_state(mm); exit_aio(mm); ksm_exit(mm); -- 2.34.1