openeuler inclusion category: bugfix bugzilla: https://gitee.com/openeuler/intel-kernel/issues/I5RQLJ CVE: NA
Intel-SIG: mm: Fix kabi change caused by saved_auxv[] in mm_struct for x86_64.
--------------------------------
Use the KABI_DEPRECATE and KABI_USE macro to fix kabi change caused by commit 1c33bb050750 ("x86/elf: Support a new ELF aux vector AT_MINSIGSTKSZ").
The extended saved_auxv[] causes the kabi breakage, move the saved_auxv[] to the end of struct mm_struct. To avoid introducing too many size increase of mm_struct, use a pointer to indirectly reference the relocated saved_auxv[], then adapt the code where mm->saved_auxv is used.
Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com Signed-off-by: Lin Wang lin.x.wang@intel.com Signed-off-by: Aichun Shi aichun.shi@intel.com --- arch/x86/include/uapi/asm/auxvec.h | 2 ++ fs/binfmt_elf.c | 12 ++++++------ fs/proc/base.c | 6 +++--- include/linux/mm_types.h | 16 ++++++++++++++-- kernel/fork.c | 29 ++++++++++++++++++++++++++--- kernel/sys.c | 10 +++++----- 6 files changed, 56 insertions(+), 19 deletions(-)
diff --git a/arch/x86/include/uapi/asm/auxvec.h b/arch/x86/include/uapi/asm/auxvec.h index 6beb55bbefa4..6be5f6584119 100644 --- a/arch/x86/include/uapi/asm/auxvec.h +++ b/arch/x86/include/uapi/asm/auxvec.h @@ -13,8 +13,10 @@ /* entries in ARCH_DLINFO: */ #if defined(CONFIG_IA32_EMULATION) || !defined(CONFIG_X86_64) # define AT_VECTOR_SIZE_ARCH 3 +# define ORIG_AT_VECTOR_SIZE_ARCH 2 #else /* else it's non-compat x86-64 */ # define AT_VECTOR_SIZE_ARCH 2 +# define ORIG_AT_VECTOR_SIZE_ARCH 1 #endif
#endif /* _ASM_X86_AUXVEC_H */ diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index ed507d27034b..1813c6fc8487 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -236,7 +236,7 @@ create_elf_tables(struct linux_binprm *bprm, const struct elfhdr *exec, return -EFAULT;
/* Create the ELF interpreter info */ - elf_info = (elf_addr_t *)mm->saved_auxv; + elf_info = (elf_addr_t *)mm->mm_extend->saved_auxv; /* update AT_VECTOR_SIZE_BASE if the number of NEW_AUX_ENT() changes */ #define NEW_AUX_ENT(id, val) \ do { \ @@ -285,13 +285,13 @@ create_elf_tables(struct linux_binprm *bprm, const struct elfhdr *exec, } #undef NEW_AUX_ENT /* AT_NULL is zero; clear the rest too */ - memset(elf_info, 0, (char *)mm->saved_auxv + - sizeof(mm->saved_auxv) - (char *)elf_info); + memset(elf_info, 0, (char *)mm->mm_extend->saved_auxv + + sizeof(mm->mm_extend->saved_auxv) - (char *)elf_info);
/* And advance past the AT_NULL entry. */ elf_info += 2;
- ei_index = elf_info - (elf_addr_t *)mm->saved_auxv; + ei_index = elf_info - (elf_addr_t *)mm->mm_extend->saved_auxv; sp = STACK_ADD(p, ei_index);
items = (argc + 1) + (envc + 1) + 1; @@ -352,7 +352,7 @@ create_elf_tables(struct linux_binprm *bprm, const struct elfhdr *exec, mm->env_end = p;
/* Put the elf_info on the stack in the right place. */ - if (copy_to_user(sp, mm->saved_auxv, ei_index * sizeof(elf_addr_t))) + if (copy_to_user(sp, mm->mm_extend->saved_auxv, ei_index * sizeof(elf_addr_t))) return -EFAULT; return 0; } @@ -1586,7 +1586,7 @@ static int fill_psinfo(struct elf_prpsinfo *psinfo, struct task_struct *p,
static void fill_auxv_note(struct memelfnote *note, struct mm_struct *mm) { - elf_addr_t *auxv = (elf_addr_t *) mm->saved_auxv; + elf_addr_t *auxv = (elf_addr_t *) mm->mm_extend->saved_auxv; int i = 0; do i += 2; diff --git a/fs/proc/base.c b/fs/proc/base.c index cc1fdff2e136..83f04d728172 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -1038,9 +1038,9 @@ static ssize_t auxv_read(struct file *file, char __user *buf, return 0; do { nwords += 2; - } while (mm->saved_auxv[nwords - 2] != 0); /* AT_NULL */ - return simple_read_from_buffer(buf, count, ppos, mm->saved_auxv, - nwords * sizeof(mm->saved_auxv[0])); + } while (mm->mm_extend->saved_auxv[nwords - 2] != 0); /* AT_NULL */ + return simple_read_from_buffer(buf, count, ppos, mm->mm_extend->saved_auxv, + nwords * sizeof(mm->mm_extend->saved_auxv[0])); }
static const struct file_operations proc_auxv_operations = { diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h index 15ff1e20f5ca..64d1c1f62968 100644 --- a/include/linux/mm_types.h +++ b/include/linux/mm_types.h @@ -24,6 +24,13 @@ #endif #define AT_VECTOR_SIZE (2*(AT_VECTOR_SIZE_ARCH + AT_VECTOR_SIZE_BASE + 1))
+ +#ifndef ORIG_AT_VECTOR_SIZE_ARCH +#define ORIG_AT_VECTOR_SIZE_ARCH AT_VECTOR_SIZE_ARCH +#endif +/* To pass the check of check-kabi, define the macro instead of number "46". */ +#define ORIG_AT_VECTOR_SIZE (2*(ORIG_AT_VECTOR_SIZE_ARCH + AT_VECTOR_SIZE_BASE + 1)) + #define INIT_PASID 0
struct address_space; @@ -394,6 +401,11 @@ struct core_state { };
struct kioctx_table; + +struct mm_struct_extend { + unsigned long saved_auxv[AT_VECTOR_SIZE]; /* for /proc/PID/auxv */ +}; + struct mm_struct { struct { struct vm_area_struct *mmap; /* list of VMAs */ @@ -508,7 +520,7 @@ struct mm_struct { unsigned long start_brk, brk, start_stack; unsigned long arg_start, arg_end, env_start, env_end;
- unsigned long saved_auxv[AT_VECTOR_SIZE]; /* for /proc/PID/auxv */ + KABI_DEPRECATE(unsigned long, saved_auxv[ORIG_AT_VECTOR_SIZE])
/* * Special counters, in some configurations protected by the @@ -592,7 +604,7 @@ struct mm_struct { #endif } __randomize_layout;
- KABI_RESERVE(1) + KABI_USE(1, struct mm_struct_extend *mm_extend) KABI_RESERVE(2) KABI_RESERVE(3) KABI_RESERVE(4) diff --git a/kernel/fork.c b/kernel/fork.c index 8a2e827815b6..c46fef2b1d1d 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -1076,12 +1076,17 @@ static struct mm_struct *mm_init(struct mm_struct *mm, struct task_struct *p, struct mm_struct *mm_alloc(void) { struct mm_struct *mm; + off_t mm_extend_offset = sizeof(struct mm_struct) + cpumask_size();
mm = allocate_mm(); if (!mm) return NULL;
memset(mm, 0, sizeof(*mm)); + memset((void *)((unsigned long) mm + mm_extend_offset), + 0, sizeof(struct mm_struct_extend)); + mm->mm_extend = (struct mm_struct_extend *)((unsigned long) mm + mm_extend_offset); + return mm_init(mm, current, current_user_ns()); }
@@ -1367,6 +1372,22 @@ void exec_mm_release(struct task_struct *tsk, struct mm_struct *mm) mm_release(tsk, mm); }
+/* + * dup_mm_extend() copies the mm_struct_extend extra area at the bottom of + * the oldmm slab object over to the newly allocated mm struct, + * and resets mm->mm_extend accordingly. + */ +void dup_mm_extend(struct mm_struct *mm, struct mm_struct *oldmm) +{ + off_t mm_extend_offset = sizeof(struct mm_struct) + cpumask_size(); + + memcpy((void *)((unsigned long) mm + mm_extend_offset), + (void *)((unsigned long) oldmm + mm_extend_offset), + sizeof(struct mm_struct_extend)); + + mm->mm_extend = (struct mm_struct_extend *)((unsigned long) mm + mm_extend_offset); +} + /** * dup_mm() - duplicates an existing mm structure * @tsk: the task_struct with which the new mm will be associated. @@ -1388,6 +1409,7 @@ static struct mm_struct *dup_mm(struct task_struct *tsk, goto fail_nomem;
memcpy(mm, oldmm, sizeof(*mm)); + dup_mm_extend(mm, oldmm);
if (!mm_init(mm, tsk, mm->user_ns)) goto fail_nomem; @@ -2857,13 +2879,14 @@ void __init proc_caches_init(void) * dynamically sized based on the maximum CPU number this system * can have, taking hotplug into account (nr_cpu_ids). */ - mm_size = sizeof(struct mm_struct) + cpumask_size(); + mm_size = sizeof(struct mm_struct) + cpumask_size() + sizeof(struct mm_struct_extend);
mm_cachep = kmem_cache_create_usercopy("mm_struct", mm_size, ARCH_MIN_MMSTRUCT_ALIGN, SLAB_HWCACHE_ALIGN|SLAB_PANIC|SLAB_ACCOUNT, - offsetof(struct mm_struct, saved_auxv), - sizeof_field(struct mm_struct, saved_auxv), + sizeof(struct mm_struct) + cpumask_size() + + offsetof(struct mm_struct_extend, saved_auxv), + sizeof_field(struct mm_struct_extend, saved_auxv), NULL); vm_area_cachep = KMEM_CACHE(vm_area_struct, SLAB_PANIC|SLAB_ACCOUNT); mmap_init(); diff --git a/kernel/sys.c b/kernel/sys.c index c8a31e1037be..72c093d798c7 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -1962,7 +1962,7 @@ static int prctl_set_mm_map(int opt, const void __user *addr, unsigned long data struct mm_struct *mm = current->mm; int error;
- BUILD_BUG_ON(sizeof(user_auxv) != sizeof(mm->saved_auxv)); + BUILD_BUG_ON(sizeof(user_auxv) != sizeof(mm->mm_extend->saved_auxv)); BUILD_BUG_ON(sizeof(struct prctl_mm_map) > 256);
if (opt == PR_SET_MM_MAP_SIZE) @@ -1984,7 +1984,7 @@ static int prctl_set_mm_map(int opt, const void __user *addr, unsigned long data * Someone is trying to cheat the auxv vector. */ if (!prctl_map.auxv || - prctl_map.auxv_size > sizeof(mm->saved_auxv)) + prctl_map.auxv_size > sizeof(mm->mm_extend->saved_auxv)) return -EINVAL;
memset(user_auxv, 0, sizeof(user_auxv)); @@ -2056,7 +2056,7 @@ static int prctl_set_mm_map(int opt, const void __user *addr, unsigned long data * more complex. */ if (prctl_map.auxv_size) - memcpy(mm->saved_auxv, user_auxv, sizeof(user_auxv)); + memcpy(mm->mm_extend->saved_auxv, user_auxv, sizeof(user_auxv));
mmap_read_unlock(mm); return 0; @@ -2084,10 +2084,10 @@ static int prctl_set_auxv(struct mm_struct *mm, unsigned long addr, user_auxv[AT_VECTOR_SIZE - 2] = 0; user_auxv[AT_VECTOR_SIZE - 1] = 0;
- BUILD_BUG_ON(sizeof(user_auxv) != sizeof(mm->saved_auxv)); + BUILD_BUG_ON(sizeof(user_auxv) != sizeof(mm->mm_extend->saved_auxv));
task_lock(current); - memcpy(mm->saved_auxv, user_auxv, len); + memcpy(mm->mm_extend->saved_auxv, user_auxv, len); task_unlock(current);
return 0;