ia32 support for NX when sigaction->sa_restorer == NULL
PATCH 1/1 ia32 support for NX when sigaction->sa_restorer == NULL
When userland doesn't specify sigaction->sa_restorer, we try to put the restorer code on the stack. But this breaks ia32 binaries with non-executable stacks. We now put the restorer code on a gate page.
We still leave the sigreturn code on the stack due to backward compatibility concerns with ia32 gdb.
This issue was primarily seen with debian glibc.
-Arun
When userland doesn't specify sigaction->sa_restorer, we try to put the restorer code on the stack. But this breaks ia32 binaries with non-executable stacks. We now put the restorer code on a gate page.
arch/ia64/ia32/binfmt_elf32.c | 39 +++++++++++++++++++++++++++++++++++++++ arch/ia64/ia32/ia32_signal.c | 33 ++++++++++++++++++++++----------- arch/ia64/ia32/ia32_support.c | 28 +++++++++++++++++++++++++--- arch/ia64/ia32/ia32priv.h | 11 ++++++++--- arch/ia64/mm/init.c | 2 +- include/asm-ia64/ia32.h | 2 +- 6 files changed, 96 insertions(+), 19 deletions(-)
arch/ia64/ia32/binfmt_elf32.c +39 -0
@@ -48,6 +48,7 @@ static void elf32_set_personality (void)extern struct page *ia32_shared_page[];extern unsigned long *ia32_gdt; +extern struct page *ia32_gate_page;struct page *ia32_install_shared_page (struct vm_area_struct *vma,unsigned long address,int *type)
@@ -59,10 +60,25 @@ ia32_install_shared_page (struct vm_areareturn pg; } +struct page * +ia32_install_gate_page (struct vm_area_struct *vma,unsigned long address,int *type) +{ +struct page *pg = ia32_gate_page; +get_page (pg); +if (type) + *type =VM_FAULT_MINOR ; +return pg; +} + +static struct vm_operations_struct ia32_shared_page_vm_ops = { .nopage = ia32_install_shared_page }; +static struct vm_operations_struct ia32_gate_page_vm_ops = { + .nopage = ia32_install_gate_page +}; +void ia64_elf32_init (struct pt_regs *regs) {
@@ -90,6 +106,29 @@ ia64_elf32_init (struct pt_regs *regs) }/* +* When user stack is not executable, push sigreturn code to stack makes +* segmentation fault raised when returning to kernel. So now sigreturn +* code is locked in specific gate page, which is pointed by pretcode +* when setup_frame_ia32 +*/ + vma =kmem_cache_alloc (vm_area_cachep,SLAB_KERNEL ); +if (vma) { +memset (vma,0 ,sizeof (*vma)); + vma->vm_mm = current->mm; + vma->vm_start =IA32_GATE_OFFSET ; + vma->vm_end = vma->vm_start +PAGE_SIZE ; + vma->vm_page_prot =PAGE_COPY_EXEC ; + vma->vm_flags =VM_READ |VM_MAYREAD |VM_EXEC + |VM_MAYEXEC |VM_RESERVED ; + vma->vm_ops = &ia32_gate_page_vm_ops; +down_write (¤t->mm->mmap_sem); + { +insert_vm_struct (current->mm, vma); + } +up_write (¤t->mm->mmap_sem); + } + +/* * Install LDT as anonymous memory. This gives us all-zero segment descriptors * until a task modifies them via modify_ldt(). */
arch/ia64/ia32/ia32priv.h +8 -3
@@ -168,6 +168,9 @@ struct ia32_user_fxsr_struct {#define IA32_SA_HANDLER (ka) ((unsigned long) (ka)->sa.sa_handler & 0xffffffff)#define IA32_SA_RESTORER (ka) ((unsigned long) (ka)->sa.sa_handler >> 32) +#define __IA32_NR_sigreturn 119 +#define __IA32_NR_rt_sigreturn 173 +struct sigaction32 {unsigned int sa_handler;/* Really a pointer, but need to deal with 32 bits */ unsigned int sa_flags;
@@ -324,14 +327,16 @@ struct old_linux32_dirent {#define IA32_PAGE_OFFSET 0xc0000000#define IA32_STACK_TOP IA32_PAGE_OFFSET +#define IA32_GATE_OFFSET IA32_PAGE_OFFSET +#define IA32_GATE_END IA32_PAGE_OFFSET + PAGE_SIZE/* * The system segments (GDT, TSS, LDT) have to be mapped below 4GB so the IA-32 engine can * access them. */ -#define IA32_GDT_OFFSET (IA32_PAGE_OFFSET) -#define IA32_TSS_OFFSET (IA32_PAGE_OFFSET + PAGE_SIZE) -#define IA32_LDT_OFFSET (IA32_PAGE_OFFSET + 2*PAGE_SIZE) +#define IA32_GDT_OFFSET (IA32_PAGE_OFFSET + PAGE_SIZE) +#define IA32_TSS_OFFSET (IA32_PAGE_OFFSET + 2*PAGE_SIZE) +#define IA32_LDT_OFFSET (IA32_PAGE_OFFSET + 3*PAGE_SIZE)#define ELF_EXEC_PAGESIZE IA32_PAGE_SIZE
arch/ia64/ia32/ia32_signal.c +22 -11
@@ -853,14 +853,19 @@ setup_frame_ia32 (int sig, struct k_sigaunsigned int restorer =IA32_SA_RESTORER (ka); err |=__put_user (restorer, &frame->pretcode); }else { - err |=__put_user ((long )frame->retcode, &frame->pretcode); -/* This is popl %eax ; movl $,%eax ; int $0x80 */ - err |=__put_user (0xb858 , (short *)(frame->retcode+0 )); - err |=__put_user (__IA32_NR_sigreturn &0xffff , (short *)(frame->retcode+2 )); - err |=__put_user (__IA32_NR_sigreturn >>16 , (short *)(frame->retcode+4 )); - err |=__put_user (0x80cd , (short *)(frame->retcode+6 )); +/* Pointing to restorer in ia32 gate page */ + err |=__put_user (IA32_GATE_OFFSET , &frame->pretcode); } +/* This is popl %eax ; movl $,%eax ; int $0x80 +* and there for historical reasons only. +* See arch/i386/kernel/signal.c +*/ + + err |=__put_user (0xb858 , (short *)(frame->retcode+0 )); + err |=__put_user (__IA32_NR_sigreturn, (int *)(frame->retcode+2 )); + err |=__put_user (0x80cd , (short *)(frame->retcode+6 )); +if (err) goto give_sigsegv;
@@ -924,13 +929,19 @@ setup_rt_frame_ia32 (int sig, struct k_sunsigned int restorer =IA32_SA_RESTORER (ka); err |=__put_user (restorer, &frame->pretcode); }else { - err |=__put_user ((long )frame->retcode, &frame->pretcode); -/* This is movl $,%eax ; int $0x80 */ - err |=__put_user (0xb8 , (char *)(frame->retcode+0 )); - err |=__put_user (__IA32_NR_rt_sigreturn, (int *)(frame->retcode+1 )); - err |=__put_user (0x80cd , (short *)(frame->retcode+5 )); +/* Pointing to rt_restorer in ia32 gate page */ + err |=__put_user (IA32_GATE_OFFSET +8 , &frame->pretcode); } +/* This is movl $,%eax ; int $0x80 +* and there for historical reasons only. +* See arch/i386/kernel/signal.c +*/ + + err |=__put_user (0xb8 , (char *)(frame->retcode+0 )); + err |=__put_user (__IA32_NR_rt_sigreturn, (int *)(frame->retcode+1 )); + err |=__put_user (0x80cd , (short *)(frame->retcode+5 )); +if (err) goto give_sigsegv;
arch/ia64/ia32/ia32_support.c +25 -3
@@ -33,6 +33,7 @@ struct exec_domain ia32_exec_domain;struct page *ia32_shared_page[NR_CPUS ];unsigned long *ia32_boot_gdt;unsigned long *cpu_gdt_table[NR_CPUS ]; +struct page *ia32_gate_page;static unsigned long load_desc (u16 selector)
@@ -155,7 +156,7 @@ ia32_gdt_init (void)/* * Setup IA32 GDT and TSS */ -void +static void ia32_boot_gdt_init (void ) {unsigned long ldt_size;
@@ -166,12 +167,12 @@ ia32_boot_gdt_init (void)/* CS descriptor in IA-32 (scrambled) format */ ia32_boot_gdt[__USER_CS >>3 ] - =IA32_SEG_DESCRIPTOR (0 , (IA32_PAGE_OFFSET -1 ) >>IA32_PAGE_SHIFT , + =IA32_SEG_DESCRIPTOR (0 , (IA32_GATE_END -1 ) >>IA32_PAGE_SHIFT ,0xb ,1 ,3 ,1 ,1 ,1 ,1 );/* DS descriptor in IA-32 (scrambled) format */ ia32_boot_gdt[__USER_DS >>3 ] - =IA32_SEG_DESCRIPTOR (0 , (IA32_PAGE_OFFSET -1 ) >>IA32_PAGE_SHIFT , + =IA32_SEG_DESCRIPTOR (0 , (IA32_GATE_END -1 ) >>IA32_PAGE_SHIFT ,0x3 ,1 ,3 ,1 ,1 ,1 ,1 ); ldt_size =PAGE_ALIGN (IA32_LDT_ENTRIES *IA32_LDT_ENTRY_SIZE );
@@ -181,6 +182,27 @@ ia32_boot_gdt_init (void) 0x2, 0, 3, 1, 1, 1, 0); } +static void +ia32_gate_page_init (void ) +{ +unsigned long *sr; + + ia32_gate_page =alloc_page (GFP_KERNEL ); + sr =page_address (ia32_gate_page); +/* This is popl %eax ; movl $,%eax ; int $0x80 */ + *sr++ =0xb858 | (__IA32_NR_sigreturn <<16 ) | (0x80cdUL <<48 ); + +/* This is movl $,%eax ; int $0x80 */ + *sr =0xb8 | (__IA32_NR_rt_sigreturn <<8 ) | (0x80cdUL <<40 ); +} + +void +ia32_mem_init (void ) +{ +ia32_boot_gdt_init (); +ia32_gate_page_init (); +} +/* * Handle bad IA32 interrupt via syscall */
arch/ia64/mm/init.c +1 -1
@@ -585,6 +585,6 @@ mem_init (void)setup_gate ();#ifdef CONFIG_IA32_SUPPORT -ia32_boot_gdt_init (); +ia32_mem_init ();#endif }
include/asm-ia64/ia32.h +1 -1
@@ -14,7 +14,7 @@# ifdef CONFIG_IA32_SUPPORT extern void ia32_cpu_init (void ); -extern void ia32_boot_gdt_init (void ); +extern void ia32_mem_init (void );extern void ia32_gdt_init (void );extern int ia32_exception (struct pt_regs *regs,unsigned long isr);extern int ia32_intercept (struct pt_regs *regs,unsigned long isr);
Quoted 3 lines · Arun Sharma
When userland doesn't specify
sigaction->sa_restorer, we try to put the restorer code on the stack. But this breaks ia32 binaries with non-executable stacks. We now put the restorer code on a gate page.
Does this work with nested signal handlers?
Andreas.
Signature · 4 lines
Andreas Schwab, SuSE Labs, [email protected] SuSE Linux AG, Maxfeldstraße 5, 90409 Nürnberg, Germany Key fingerprint = 58CA 54C7 6D53 942B 1756 01D3 44D5 214B 8276 4ED5 "And now for something completely different."
Quoted 7 lines · Andreas Schwab
Quoted 3 lines · Arun Sharma
When userland doesn't specify
sigaction->sa_restorer, we try to put the restorer code on the stack. But this breaks ia32 binaries with non-executable stacks. We now put the restorer code on a gate page.Does this work with nested signal handlers?
I don't see why not. The gate page is read-only.
-Arun
Quoted 1 line · Arun Sharma
I don't see why not. The gate page is read-only.
Thanks, I understand it now.
Andreas.
Signature · 4 lines
Andreas Schwab, SuSE Labs, [email protected] SuSE Linux AG, Maxfeldstraße 5, 90409 Nürnberg, Germany Key fingerprint = 58CA 54C7 6D53 942B 1756 01D3 44D5 214B 8276 4ED5 "And now for something completely different."