diff --git a/arch/arm64/kernel/xcall/core.c b/arch/arm64/kernel/xcall/core.c index a88c4ed6e575c9ed6cb53e3800dda8481ec4ce9d..f3a381c9106ab78f34c7d98003a7bdd4ef7da4a2 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/fs/proc/proc_xcall.c b/fs/proc/proc_xcall.c index 8f73752358031277c159012065c144fb97a8db66..7549cd463f5bab79b4d8bead87daab8898f6a918 100644 --- a/fs/proc/proc_xcall.c +++ b/fs/proc/proc_xcall.c @@ -61,12 +61,16 @@ static int xcall_open(struct inode *inode, struct file *filp) return single_open(filp, xcall_show, inode); } +static DEFINE_SPINLOCK(xcall_enable_write_lock); +#define MAX_SYSNO_DIGITS 3 +#define MAX_BUF_SIZE (1 + MAX_SYSNO_DIGITS + 1) // "!" + digits + '\0' static ssize_t xcall_write(struct file *file, const char __user *ubuf, size_t count, loff_t *offset) { unsigned int sc_no = __NR_syscalls; + char buf[MAX_BUF_SIZE]; struct task_struct *p; - char buf[5]; + int is_clear = 0; int ret = 0; if (!static_key_enabled(&xcall_enable)) @@ -76,13 +80,19 @@ static ssize_t xcall_write(struct file *file, const char __user *ubuf, if (!p || !TASK_XINFO(p)) return -ESRCH; - memset(buf, '\0', 5); - if (!count || (count > 5) || copy_from_user(buf, ubuf, count)) { + memset(buf, '\0', MAX_BUF_SIZE); + if (!count || (count > MAX_BUF_SIZE)) { ret = -EFAULT; goto out; } - if (kstrtouint((buf + (int)(buf[0] == '!')), 10, &sc_no)) { + if (copy_from_user(buf, ubuf, count > MAX_BUF_SIZE - 1 ? MAX_BUF_SIZE - 1 : count)) { + ret = -EFAULT; + goto out; + } + + is_clear = (buf[0] == '!'); + if (kstrtouint((buf + is_clear), 10, &sc_no)) { ret = -EINVAL; goto out; } @@ -92,7 +102,12 @@ static ssize_t xcall_write(struct file *file, const char __user *ubuf, goto out; } - (TASK_XINFO(p))->xcall_enable[sc_no] = (int)(buf[0] != '!'); + spin_lock(&xcall_enable_write_lock); + if (!is_clear && !(TASK_XINFO(p))->xcall_enable[sc_no]) + (TASK_XINFO(p))->xcall_enable[sc_no] = 1; + else if (is_clear && (TASK_XINFO(p))->xcall_enable[sc_no]) + (TASK_XINFO(p))->xcall_enable[sc_no] = 0; + spin_unlock(&xcall_enable_write_lock); ret = 0; out: diff --git a/include/linux/xcall.h b/include/linux/xcall.h index 510aebe4e7c0614daf828da0aa41e92511b9669d..15da167282784e6c5705577b41bbbc6434f74f3f 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 f0271e915b0ed07fb93aba7b2e91487d7ef69183..4c820dfbfd75e7558ca35e8c24a0e9e1fa7cca6d 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);