Skip to content

Commit d55c571

Browse files
oleg-nesterovPeter Zijlstra
authored andcommitted
x86/uprobes: Fix XOL allocation failure for 32-bit tasks
This script #!/usr/bin/bash echo 0 > /proc/sys/kernel/randomize_va_space echo 'void main(void) {}' > TEST.c # -fcf-protection to ensure that the 1st endbr32 insn can't be emulated gcc -m32 -fcf-protection=branch TEST.c -o test bpftrace -e 'uprobe:./test:main {}' -c ./test "hangs", the probed ./test task enters an endless loop. The problem is that with randomize_va_space == 0 get_unmapped_area(TASK_SIZE - PAGE_SIZE) called by xol_add_vma() can not just return the "addr == TASK_SIZE - PAGE_SIZE" hint, this addr is used by the stack vma. arch_get_unmapped_area_topdown() doesn't take TIF_ADDR32 into account and in_32bit_syscall() is false, this leads to info.high_limit > TASK_SIZE. vm_unmapped_area() happily returns the high address > TASK_SIZE and then get_unmapped_area() returns -ENOMEM after the "if (addr > TASK_SIZE - len)" check. handle_swbp() doesn't report this failure (probably it should) and silently restarts the probed insn. Endless loop. I think that the right fix should change the x86 get_unmapped_area() paths to rely on TIF_ADDR32 rather than in_32bit_syscall(). Note also that if CONFIG_X86_X32_ABI=y, in_x32_syscall() falsely returns true in this case because ->orig_ax = -1. But we need a simple fix for -stable, so this patch just sets TS_COMPAT if the probed task is 32-bit to make in_ia32_syscall() true. Fixes: 1b028f7 ("x86/mm: Introduce mmap_compat_base() for 32-bit mmap()") Reported-by: Paulo Andrade <pandrade@redhat.com> Signed-off-by: Oleg Nesterov <oleg@redhat.com> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Link: https://lore.kernel.org/all/aV5uldEvV7pb4RA8@redhat.com/ Cc: stable@vger.kernel.org Link: https://patch.msgid.link/aWO7Fdxn39piQnxu@redhat.com
1 parent 10d6d24 commit d55c571

3 files changed

Lines changed: 32 additions & 3 deletions

File tree

arch/x86/kernel/uprobes.c

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1823,3 +1823,27 @@ bool is_uprobe_at_func_entry(struct pt_regs *regs)
18231823

18241824
return false;
18251825
}
1826+
1827+
#ifdef CONFIG_IA32_EMULATION
1828+
unsigned long arch_uprobe_get_xol_area(void)
1829+
{
1830+
struct thread_info *ti = current_thread_info();
1831+
unsigned long vaddr;
1832+
1833+
/*
1834+
* HACK: we are not in a syscall, but x86 get_unmapped_area() paths
1835+
* ignore TIF_ADDR32 and rely on in_32bit_syscall() to calculate
1836+
* vm_unmapped_area_info.high_limit.
1837+
*
1838+
* The #ifdef above doesn't cover the CONFIG_X86_X32_ABI=y case,
1839+
* but in this case in_32bit_syscall() -> in_x32_syscall() always
1840+
* (falsely) returns true because ->orig_ax == -1.
1841+
*/
1842+
if (test_thread_flag(TIF_ADDR32))
1843+
ti->status |= TS_COMPAT;
1844+
vaddr = get_unmapped_area(NULL, TASK_SIZE - PAGE_SIZE, PAGE_SIZE, 0, 0);
1845+
ti->status &= ~TS_COMPAT;
1846+
1847+
return vaddr;
1848+
}
1849+
#endif

include/linux/uprobes.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,7 @@ extern void arch_uprobe_clear_state(struct mm_struct *mm);
242242
extern void arch_uprobe_init_state(struct mm_struct *mm);
243243
extern void handle_syscall_uprobe(struct pt_regs *regs, unsigned long bp_vaddr);
244244
extern void arch_uprobe_optimize(struct arch_uprobe *auprobe, unsigned long vaddr);
245+
extern unsigned long arch_uprobe_get_xol_area(void);
245246
#else /* !CONFIG_UPROBES */
246247
struct uprobes_state {
247248
};

kernel/events/uprobes.c

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1694,6 +1694,12 @@ static const struct vm_special_mapping xol_mapping = {
16941694
.mremap = xol_mremap,
16951695
};
16961696

1697+
unsigned long __weak arch_uprobe_get_xol_area(void)
1698+
{
1699+
/* Try to map as high as possible, this is only a hint. */
1700+
return get_unmapped_area(NULL, TASK_SIZE - PAGE_SIZE, PAGE_SIZE, 0, 0);
1701+
}
1702+
16971703
/* Slot allocation for XOL */
16981704
static int xol_add_vma(struct mm_struct *mm, struct xol_area *area)
16991705
{
@@ -1709,9 +1715,7 @@ static int xol_add_vma(struct mm_struct *mm, struct xol_area *area)
17091715
}
17101716

17111717
if (!area->vaddr) {
1712-
/* Try to map as high as possible, this is only a hint. */
1713-
area->vaddr = get_unmapped_area(NULL, TASK_SIZE - PAGE_SIZE,
1714-
PAGE_SIZE, 0, 0);
1718+
area->vaddr = arch_uprobe_get_xol_area();
17151719
if (IS_ERR_VALUE(area->vaddr)) {
17161720
ret = area->vaddr;
17171721
goto fail;

0 commit comments

Comments
 (0)