[PATCH] ia32 support for NX when sigaction->sa_restorer == NULL

From: Arun Sharma <arun.sharma_at_intel.com>
Date: 2004-09-11 02:17:54
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.

Signed-off-by: Kevin Tian <kevin.tian@intel.com>
Signed-off-by: Arun Sharma <arun.sharma@intel.com>

 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(-)

diff -purN -X dontdiff linux-2.6.8.ori/arch/ia64/ia32/binfmt_elf32.c linux-2.6.8/arch/ia64/ia32/binfmt_elf32.c
--- linux-2.6.8.ori/arch/ia64/ia32/binfmt_elf32.c	2004-08-16 15:36:30.000000000 +0800
+++ linux-2.6.8/arch/ia64/ia32/binfmt_elf32.c	2004-09-09 18:31:47.000000000 +0800
@@ -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_area
 	return 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(&current->mm->mmap_sem);
+		{
+			insert_vm_struct(current->mm, vma);
+		}
+		up_write(&current->mm->mmap_sem);
+	}
+
+	/*
 	 * Install LDT as anonymous memory.  This gives us all-zero segment descriptors
 	 * until a task modifies them via modify_ldt().
 	 */
diff -purN -X dontdiff linux-2.6.8.ori/arch/ia64/ia32/ia32priv.h linux-2.6.8/arch/ia64/ia32/ia32priv.h
--- linux-2.6.8.ori/arch/ia64/ia32/ia32priv.h	2004-08-16 15:36:30.000000000 +0800
+++ linux-2.6.8/arch/ia64/ia32/ia32priv.h	2004-09-09 19:03:34.000000000 +0800
@@ -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
 
diff -purN -X dontdiff linux-2.6.8.ori/arch/ia64/ia32/ia32_signal.c linux-2.6.8/arch/ia64/ia32/ia32_signal.c
--- linux-2.6.8.ori/arch/ia64/ia32/ia32_signal.c	2004-06-17 15:55:32.000000000 +0800
+++ linux-2.6.8/arch/ia64/ia32/ia32_signal.c	2004-09-09 18:31:47.000000000 +0800
@@ -853,14 +853,19 @@ setup_frame_ia32 (int sig, struct k_siga
 		unsigned 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_s
 		unsigned 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;
 
diff -purN -X dontdiff linux-2.6.8.ori/arch/ia64/ia32/ia32_support.c linux-2.6.8/arch/ia64/ia32/ia32_support.c
--- linux-2.6.8.ori/arch/ia64/ia32/ia32_support.c	2004-08-16 15:36:30.000000000 +0800
+++ linux-2.6.8/arch/ia64/ia32/ia32_support.c	2004-09-09 18:31:47.000000000 +0800
@@ -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
  */
diff -purN -X dontdiff linux-2.6.8.ori/arch/ia64/mm/init.c linux-2.6.8/arch/ia64/mm/init.c
--- linux-2.6.8.ori/arch/ia64/mm/init.c	2004-08-16 15:36:30.000000000 +0800
+++ linux-2.6.8/arch/ia64/mm/init.c	2004-09-09 18:31:47.000000000 +0800
@@ -585,6 +585,6 @@ mem_init (void)
 	setup_gate();
 
 #ifdef CONFIG_IA32_SUPPORT
-	ia32_boot_gdt_init();
+	ia32_mem_init();
 #endif
 }
diff -purN -X dontdiff linux-2.6.8.ori/include/asm-ia64/ia32.h linux-2.6.8/include/asm-ia64/ia32.h
--- linux-2.6.8.ori/include/asm-ia64/ia32.h	2004-08-16 15:36:43.000000000 +0800
+++ linux-2.6.8/include/asm-ia64/ia32.h	2004-09-09 18:31:47.000000000 +0800
@@ -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);
-
To unsubscribe from this list: send the line "unsubscribe linux-ia64" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Received on Fri Sep 10 12:28:37 2004

This archive was generated by hypermail 2.1.8 : 2005-08-02 09:20:30 EST