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/binfmt_elf_fdpic.c | 2 +- fs/proc/base.c | 6 ++-- include/linux/mm_types.h | 30 ++++++++++++++++++++ kernel/fork.c | 44 ++++++++++++++++++++++++++---- kernel/sys.c | 10 +++---- 7 files changed, 86 insertions(+), 20 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..8e56bb649f0e 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_SAVED_AUXV(mm); /* 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_SAVED_AUXV(mm) + + sizeof(MM_SAVED_AUXV(mm)) - (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_SAVED_AUXV(mm); 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_SAVED_AUXV(mm), 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_SAVED_AUXV(mm); int i = 0; do i += 2; diff --git a/fs/binfmt_elf_fdpic.c b/fs/binfmt_elf_fdpic.c index be4062b8ba75..06d45339676f 100644 --- a/fs/binfmt_elf_fdpic.c +++ b/fs/binfmt_elf_fdpic.c @@ -1550,7 +1550,7 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm) fill_note(&psinfo_note, "CORE", NT_PRPSINFO, sizeof(*psinfo), psinfo); thread_status_size += notesize(&psinfo_note);
- auxv = (elf_addr_t *) current->mm->saved_auxv; + auxv = (elf_addr_t *) current->MM_SAVED_AUXV(mm); i = 0; do i += 2; diff --git a/fs/proc/base.c b/fs/proc/base.c index cc1fdff2e136..fe2633176ed5 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_SAVED_AUXV(mm)[nwords - 2] != 0); /* AT_NULL */ + return simple_read_from_buffer(buf, count, ppos, MM_SAVED_AUXV(mm), + nwords * sizeof(MM_SAVED_AUXV(mm)[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..ba69c5ecd4ce 100644 --- a/include/linux/mm_types.h +++ b/include/linux/mm_types.h @@ -24,6 +24,21 @@ #endif #define AT_VECTOR_SIZE (2*(AT_VECTOR_SIZE_ARCH + AT_VECTOR_SIZE_BASE + 1))
+#define _MM_STRUCT_SIZE (sizeof(struct mm_struct) + cpumask_size()) + +#if defined(CONFIG_X86_64) + #define ORIG_AT_VECTOR_SIZE (2*(ORIG_AT_VECTOR_SIZE_ARCH + AT_VECTOR_SIZE_BASE + 1)) + #define MM_SAVED_AUXV(mm) mm->mm_extend->saved_auxv + #define MM_STRUCT_SIZE (_MM_STRUCT_SIZE + sizeof(struct mm_struct_extend)) + #define OFFSET_OF_MM_SAVED_AUXV (_MM_STRUCT_SIZE + offsetof(struct mm_struct_extend, saved_auxv)) + #define SIZE_OF_MM_SAVED_AUXV sizeof_field(struct mm_struct_extend, saved_auxv) +#else + #define MM_SAVED_AUXV(mm) mm->saved_auxv + #define MM_STRUCT_SIZE _MM_STRUCT_SIZE + #define OFFSET_OF_MM_SAVED_AUXV offsetof(struct mm_struct, saved_auxv) + #define SIZE_OF_MM_SAVED_AUXV sizeof_field(struct mm_struct, saved_auxv) +#endif + #define INIT_PASID 0
struct address_space; @@ -394,6 +409,13 @@ struct core_state { };
struct kioctx_table; + +#if defined(CONFIG_X86_64) +struct mm_struct_extend { + unsigned long saved_auxv[AT_VECTOR_SIZE]; /* for /proc/PID/auxv */ +}; +#endif + struct mm_struct { struct { struct vm_area_struct *mmap; /* list of VMAs */ @@ -508,7 +530,11 @@ struct mm_struct { unsigned long start_brk, brk, start_stack; unsigned long arg_start, arg_end, env_start, env_end;
+#if defined(CONFIG_X86_64) + KABI_DEPRECATE(unsigned long, saved_auxv[ORIG_AT_VECTOR_SIZE]) +#else unsigned long saved_auxv[AT_VECTOR_SIZE]; /* for /proc/PID/auxv */ +#endif
/* * Special counters, in some configurations protected by the @@ -592,7 +618,11 @@ struct mm_struct { #endif } __randomize_layout;
+#if defined(CONFIG_X86_64) + KABI_USE(1, struct mm_struct_extend *mm_extend) +#else KABI_RESERVE(1) +#endif KABI_RESERVE(2) KABI_RESERVE(3) KABI_RESERVE(4) diff --git a/kernel/fork.c b/kernel/fork.c index 8a40bfda35e1..c2f13f635135 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -1088,6 +1088,21 @@ static struct mm_struct *mm_init(struct mm_struct *mm, struct task_struct *p, return NULL; }
+static inline void mm_struct_clear(struct mm_struct *mm) +{ + memset(mm, 0, sizeof(*mm)); + +#if (defined(CONFIG_X86_64)) + /* + * init the mm_struct_extend extra area at the bottom of + * the allocated mm struct and reset mm->mm_extend accordingly. + */ + memset((void *)((unsigned long) mm + _MM_STRUCT_SIZE), + 0, sizeof(struct mm_struct_extend)); + mm->mm_extend = (struct mm_struct_extend *)((unsigned long) mm + _MM_STRUCT_SIZE); +#endif +} + /* * Allocate and initialize an mm_struct. */ @@ -1099,7 +1114,8 @@ struct mm_struct *mm_alloc(void) if (!mm) return NULL;
- memset(mm, 0, sizeof(*mm)); + mm_struct_clear(mm); + return mm_init(mm, current, current_user_ns()); }
@@ -1382,6 +1398,24 @@ void exec_mm_release(struct task_struct *tsk, struct mm_struct *mm) mm_release(tsk, mm); }
+static inline void mm_struct_copy(struct mm_struct *mm, struct mm_struct *oldmm) +{ + memcpy(mm, oldmm, sizeof(*mm)); + +#if defined(CONFIG_X86_64) + /* + * copy the mm_struct_extend extra area at the bottom of + * the oldmm slab object over to the newly allocated mm struct, + * and reset mm->mm_extend accordingly. + */ + memcpy((void *)((unsigned long) mm + _MM_STRUCT_SIZE), + (void *)((unsigned long) oldmm + _MM_STRUCT_SIZE), + sizeof(struct mm_struct_extend)); + + mm->mm_extend = (struct mm_struct_extend *)((unsigned long) mm + _MM_STRUCT_SIZE); +#endif +} + /** * dup_mm() - duplicates an existing mm structure * @tsk: the task_struct with which the new mm will be associated. @@ -1402,7 +1436,7 @@ static struct mm_struct *dup_mm(struct task_struct *tsk, if (!mm) goto fail_nomem;
- memcpy(mm, oldmm, sizeof(*mm)); + mm_struct_copy(mm, oldmm);
if (!mm_init(mm, tsk, mm->user_ns)) goto fail_nomem; @@ -2872,13 +2906,13 @@ 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 = MM_STRUCT_SIZE;
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), + OFFSET_OF_MM_SAVED_AUXV, + SIZE_OF_MM_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..10c5a6d2acdc 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_SAVED_AUXV(mm))); 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_SAVED_AUXV(mm))) 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_SAVED_AUXV(mm), 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_SAVED_AUXV(mm)));
task_lock(current); - memcpy(mm->saved_auxv, user_auxv, len); + memcpy(MM_SAVED_AUXV(mm), user_auxv, len); task_unlock(current);
return 0;