Skip to content

Commit 1b028f7

Browse files
0x7f454c46KAGA-KOKO
authored andcommitted
x86/mm: Introduce mmap_compat_base() for 32-bit mmap()
mmap() uses a base address, from which it starts to look for a free space for allocation. The base address is stored in mm->mmap_base, which is calculated during exec(). The address depends on task's size, set rlimit for stack, ASLR randomization. The base depends on the task size and the number of random bits which are different for 64-bit and 32bit applications. Due to the fact, that the base address is fixed, its mmap() from a compat (32bit) syscall issued by a 64bit task will return a address which is based on the 64bit base address and does not fit into the 32bit address space (4GB). The returned pointer is truncated to 32bit, which results in an invalid address. To solve store a seperate compat address base plus a compat legacy address base in mm_struct. These bases are calculated at exec() time and can be used later to address the 32bit compat mmap() issued by 64 bit applications. As a consequence of this change 32-bit applications issuing a 64-bit syscall (after doing a long jump) will get a 64-bit mapping now. Before this change 32-bit applications always got a 32bit mapping. [ tglx: Massaged changelog and added a comment ] Signed-off-by: Dmitry Safonov <[email protected]> Cc: [email protected] Cc: [email protected] Cc: Andy Lutomirski <[email protected]> Cc: Cyrill Gorcunov <[email protected]> Cc: Borislav Petkov <[email protected]> Cc: "Kirill A. Shutemov" <[email protected]> Link: http://lkml.kernel.org/r/[email protected] Signed-off-by: Thomas Gleixner <[email protected]>
1 parent 8f3e474 commit 1b028f7

File tree

6 files changed

+69
-17
lines changed

6 files changed

+69
-17
lines changed

arch/Kconfig

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -700,6 +700,13 @@ config ARCH_MMAP_RND_COMPAT_BITS
700700
This value can be changed after boot using the
701701
/proc/sys/vm/mmap_rnd_compat_bits tunable
702702

703+
config HAVE_ARCH_COMPAT_MMAP_BASES
704+
bool
705+
help
706+
This allows 64bit applications to invoke 32-bit mmap() syscall
707+
and vice-versa 32-bit applications to call 64-bit mmap().
708+
Required for applications doing different bitness syscalls.
709+
703710
config HAVE_COPY_THREAD_TLS
704711
bool
705712
help

arch/x86/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ config X86
106106
select HAVE_ARCH_KMEMCHECK
107107
select HAVE_ARCH_MMAP_RND_BITS if MMU
108108
select HAVE_ARCH_MMAP_RND_COMPAT_BITS if MMU && COMPAT
109+
select HAVE_ARCH_COMPAT_MMAP_BASES if MMU && COMPAT
109110
select HAVE_ARCH_SECCOMP_FILTER
110111
select HAVE_ARCH_TRACEHOOK
111112
select HAVE_ARCH_TRANSPARENT_HUGEPAGE

arch/x86/include/asm/elf.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,9 @@ static inline int mmap_is_ia32(void)
303303
test_thread_flag(TIF_ADDR32));
304304
}
305305

306+
extern unsigned long tasksize_32bit(void);
307+
extern unsigned long tasksize_64bit(void);
308+
306309
#ifdef CONFIG_X86_32
307310

308311
#define __STACK_RND_MASK(is32bit) (0x7ff)

arch/x86/kernel/sys_x86_64.c

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
#include <linux/uaccess.h>
1818
#include <linux/elf.h>
1919

20+
#include <asm/elf.h>
21+
#include <asm/compat.h>
2022
#include <asm/ia32.h>
2123
#include <asm/syscalls.h>
2224

@@ -98,6 +100,18 @@ SYSCALL_DEFINE6(mmap, unsigned long, addr, unsigned long, len,
98100
return error;
99101
}
100102

103+
static unsigned long get_mmap_base(int is_legacy)
104+
{
105+
struct mm_struct *mm = current->mm;
106+
107+
#ifdef CONFIG_HAVE_ARCH_COMPAT_MMAP_BASES
108+
if (in_compat_syscall())
109+
return is_legacy ? mm->mmap_compat_legacy_base
110+
: mm->mmap_compat_base;
111+
#endif
112+
return is_legacy ? mm->mmap_legacy_base : mm->mmap_base;
113+
}
114+
101115
static void find_start_end(unsigned long flags, unsigned long *begin,
102116
unsigned long *end)
103117
{
@@ -114,10 +128,11 @@ static void find_start_end(unsigned long flags, unsigned long *begin,
114128
if (current->flags & PF_RANDOMIZE) {
115129
*begin = randomize_page(*begin, 0x02000000);
116130
}
117-
} else {
118-
*begin = current->mm->mmap_legacy_base;
119-
*end = TASK_SIZE;
131+
return;
120132
}
133+
134+
*begin = get_mmap_base(1);
135+
*end = in_compat_syscall() ? tasksize_32bit() : tasksize_64bit();
121136
}
122137

123138
unsigned long
@@ -191,7 +206,7 @@ arch_get_unmapped_area_topdown(struct file *filp, const unsigned long addr0,
191206
info.flags = VM_UNMAPPED_AREA_TOPDOWN;
192207
info.length = len;
193208
info.low_limit = PAGE_SIZE;
194-
info.high_limit = mm->mmap_base;
209+
info.high_limit = get_mmap_base(0);
195210
info.align_mask = 0;
196211
info.align_offset = pgoff << PAGE_SHIFT;
197212
if (filp) {

arch/x86/mm/mmap.c

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,16 @@ struct va_alignment __read_mostly va_align = {
3636
.flags = -1,
3737
};
3838

39-
static inline unsigned long tasksize_32bit(void)
39+
unsigned long tasksize_32bit(void)
4040
{
4141
return IA32_PAGE_OFFSET;
4242
}
4343

44+
unsigned long tasksize_64bit(void)
45+
{
46+
return TASK_SIZE_MAX;
47+
}
48+
4449
static unsigned long stack_maxrandom_size(unsigned long task_size)
4550
{
4651
unsigned long max = 0;
@@ -81,6 +86,8 @@ static unsigned long arch_rnd(unsigned int rndbits)
8186

8287
unsigned long arch_mmap_rnd(void)
8388
{
89+
if (!(current->flags & PF_RANDOMIZE))
90+
return 0;
8491
return arch_rnd(mmap_is_ia32() ? mmap32_rnd_bits : mmap64_rnd_bits);
8592
}
8693

@@ -114,22 +121,36 @@ static unsigned long mmap_legacy_base(unsigned long rnd,
114121
* This function, called very early during the creation of a new
115122
* process VM image, sets up which VM layout function to use:
116123
*/
117-
void arch_pick_mmap_layout(struct mm_struct *mm)
124+
static void arch_pick_mmap_base(unsigned long *base, unsigned long *legacy_base,
125+
unsigned long random_factor, unsigned long task_size)
118126
{
119-
unsigned long random_factor = 0UL;
120-
121-
if (current->flags & PF_RANDOMIZE)
122-
random_factor = arch_mmap_rnd();
123-
124-
mm->mmap_legacy_base = mmap_legacy_base(random_factor, TASK_SIZE);
127+
*legacy_base = mmap_legacy_base(random_factor, task_size);
128+
if (mmap_is_legacy())
129+
*base = *legacy_base;
130+
else
131+
*base = mmap_base(random_factor, task_size);
132+
}
125133

126-
if (mmap_is_legacy()) {
127-
mm->mmap_base = mm->mmap_legacy_base;
134+
void arch_pick_mmap_layout(struct mm_struct *mm)
135+
{
136+
if (mmap_is_legacy())
128137
mm->get_unmapped_area = arch_get_unmapped_area;
129-
} else {
130-
mm->mmap_base = mmap_base(random_factor, TASK_SIZE);
138+
else
131139
mm->get_unmapped_area = arch_get_unmapped_area_topdown;
132-
}
140+
141+
arch_pick_mmap_base(&mm->mmap_base, &mm->mmap_legacy_base,
142+
arch_rnd(mmap64_rnd_bits), tasksize_64bit());
143+
144+
#ifdef CONFIG_HAVE_ARCH_COMPAT_MMAP_BASES
145+
/*
146+
* The mmap syscall mapping base decision depends solely on the
147+
* syscall type (64-bit or compat). This applies for 64bit
148+
* applications and 32bit applications. The 64bit syscall uses
149+
* mmap_base, the compat syscall uses mmap_compat_base.
150+
*/
151+
arch_pick_mmap_base(&mm->mmap_compat_base, &mm->mmap_compat_legacy_base,
152+
arch_rnd(mmap32_rnd_bits), tasksize_32bit());
153+
#endif
133154
}
134155

135156
const char *arch_vma_name(struct vm_area_struct *vma)

include/linux/mm_types.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,11 @@ struct mm_struct {
367367
#endif
368368
unsigned long mmap_base; /* base of mmap area */
369369
unsigned long mmap_legacy_base; /* base of mmap area in bottom-up allocations */
370+
#ifdef CONFIG_HAVE_ARCH_COMPAT_MMAP_BASES
371+
/* Base adresses for compatible mmap() */
372+
unsigned long mmap_compat_base;
373+
unsigned long mmap_compat_legacy_base;
374+
#endif
370375
unsigned long task_size; /* size of task vm space */
371376
unsigned long highest_vm_end; /* highest vma end address */
372377
pgd_t * pgd;

0 commit comments

Comments
 (0)