
From: Anton Blanchard <anton@samba.org>, me.

Two uses of the FIXADDR_USER_START/END things are problematic:

a) ppc64 wants the FIXADDR area to be at a different location on 32bit and
   64bit tasks.  On 32bit we want it just below 4GB but that gets in the way
   on 64bit.  By putting both right at -(some small amount) we can also use
   some ppc tricks to get there real quickly (single instruction branches).

b) We assume that FIXADDR_USER_START and FIXADDR_USER_END are constants. 
   This breaks the UML build.

Fixes:

- Call it all gate. We currently have half the stuff called fixmap and
  the other gate, lets be consistent.

- Create in_gate_area(), get_gate_vma() and use it in both places

- Provide defaults for in_gate_area/get_gate_vma, allowing an arch to
  override it. (I used CONFIG_* but am open to better suggestions here)

- The /proc/pid/maps vma wasnt marked readable but the get_user
  vma was. That sounds suspicious to me, they are now both the same VMA
  and so have the same (read,exec) permissions



---

 fs/proc/task_mmu.c |   26 ++++++--------------------
 include/linux/mm.h |   28 ++++++++++++++++++++++++++++
 mm/memory.c        |   37 ++++++++++++++++++++-----------------
 3 files changed, 54 insertions(+), 37 deletions(-)

diff -puN fs/proc/task_mmu.c~proc-pid-maps-gate-fixes fs/proc/task_mmu.c
--- 25/fs/proc/task_mmu.c~proc-pid-maps-gate-fixes	2004-01-11 22:41:33.000000000 -0800
+++ 25-akpm/fs/proc/task_mmu.c	2004-01-11 22:41:33.000000000 -0800
@@ -76,22 +76,6 @@ int task_statm(struct mm_struct *mm, int
 	return size;
 }
 
-#ifdef AT_SYSINFO_EHDR
-
-static struct vm_area_struct gate_vmarea = {
-	/* Do _not_ mark this area as readable, cuz not the entire range may be readable
-	   (e.g., due to execute-only pages or holes) and the tools that read
-	   /proc/PID/maps should read the interesting bits from the gate-DSO file
-	   instead.  */
-	.vm_start = FIXADDR_USER_START,
-	.vm_end = FIXADDR_USER_END
-};
-
-# define gate_map()	&gate_vmarea
-#else
-# define gate_map()	NULL
-#endif
-
 static int show_map(struct seq_file *m, void *v)
 {
 	struct vm_area_struct *map = v;
@@ -146,15 +130,16 @@ static void *m_start(struct seq_file *m,
 		up_read(&mm->mmap_sem);
 		mmput(mm);
 		if (l == -1)
-			map = gate_map();
+			map = get_gate_vma(task);
 	}
 	return map;
 }
 
 static void m_stop(struct seq_file *m, void *v)
 {
+	struct task_struct *task = m->private;
 	struct vm_area_struct *map = v;
-	if (map && map != gate_map()) {
+	if (map && map != get_gate_vma(task)) {
 		struct mm_struct *mm = map->vm_mm;
 		up_read(&mm->mmap_sem);
 		mmput(mm);
@@ -163,13 +148,14 @@ static void m_stop(struct seq_file *m, v
 
 static void *m_next(struct seq_file *m, void *v, loff_t *pos)
 {
+	struct task_struct *task = m->private;
 	struct vm_area_struct *map = v;
 	(*pos)++;
 	if (map->vm_next)
 		return map->vm_next;
 	m_stop(m, v);
-	if (map != gate_map())
-		return gate_map();
+	if (map != get_gate_vma(task))
+		return get_gate_vma(task);
 	return NULL;
 }
 
diff -puN include/linux/mm.h~proc-pid-maps-gate-fixes include/linux/mm.h
--- 25/include/linux/mm.h~proc-pid-maps-gate-fixes	2004-01-11 22:41:33.000000000 -0800
+++ 25-akpm/include/linux/mm.h	2004-01-11 23:31:11.000000000 -0800
@@ -642,5 +642,33 @@ kernel_map_pages(struct page *page, int 
 }
 #endif
 
+#ifndef CONFIG_ARCH_GATE_AREA
+#ifdef AT_SYSINFO_EHDR
+static inline int in_gate_area(struct task_struct *task, unsigned long addr)
+{
+	if ((addr >= FIXADDR_USER_START) && (addr < FIXADDR_USER_END))
+		return 1;
+	else
+		return 0;
+}
+
+extern struct vm_area_struct gate_vma;
+static inline struct vm_area_struct *get_gate_vma(struct task_struct *tsk)
+{
+	return &gate_vma;
+}
+#else
+static inline int in_gate_area(struct task_struct *task, unsigned long addr)
+{
+	return 0;
+}
+
+static inline struct vm_area_struct *get_gate_vma(struct task_struct *tsk)
+{
+	return NULL;
+}
+#endif
+#endif
+
 #endif /* __KERNEL__ */
 #endif /* _LINUX_MM_H */
diff -puN mm/memory.c~proc-pid-maps-gate-fixes mm/memory.c
--- 25/mm/memory.c~proc-pid-maps-gate-fixes	2004-01-11 22:41:33.000000000 -0800
+++ 25-akpm/mm/memory.c	2004-01-12 02:46:43.000000000 -0800
@@ -45,6 +45,7 @@
 #include <linux/pagemap.h>
 #include <linux/rmap-locking.h>
 #include <linux/module.h>
+#include <linux/init.h>
 
 #include <asm/pgalloc.h>
 #include <asm/rmap.h>
@@ -705,25 +706,13 @@ int get_user_pages(struct task_struct *t
 		struct vm_area_struct *	vma;
 
 		vma = find_extend_vma(mm, start);
-
-#ifdef FIXADDR_USER_START
-		if (!vma &&
-		    start >= FIXADDR_USER_START && start < FIXADDR_USER_END) {
-			static struct vm_area_struct fixmap_vma = {
-				/* Catch users - if there are any valid
-				   ones, we can make this be "&init_mm" or
-				   something.  */
-				.vm_mm = NULL,
-				.vm_start = FIXADDR_USER_START,
-				.vm_end = FIXADDR_USER_END,
-				.vm_page_prot = PAGE_READONLY,
-				.vm_flags = VM_READ | VM_EXEC,
-			};
+		if (!vma && in_gate_area(tsk, start)) {
 			unsigned long pg = start & PAGE_MASK;
+			struct vm_area_struct *gate_vma = get_gate_vma(tsk);
 			pgd_t *pgd;
 			pmd_t *pmd;
 			pte_t *pte;
-			if (write) /* user fixmap pages are read-only */
+			if (write) /* user gate pages are read-only */
 				return i ? : -EFAULT;
 			pgd = pgd_offset_k(pg);
 			if (!pgd)
@@ -739,13 +728,12 @@ int get_user_pages(struct task_struct *t
 				get_page(pages[i]);
 			}
 			if (vmas)
-				vmas[i] = &fixmap_vma;
+				vmas[i] = gate_vma;
 			i++;
 			start += PAGE_SIZE;
 			len--;
 			continue;
 		}
-#endif
 
 		if (!vma)
 			return i ? : -EFAULT;
@@ -1713,3 +1701,18 @@ struct page * vmalloc_to_page(void * vma
 }
 
 EXPORT_SYMBOL(vmalloc_to_page);
+
+#if !defined(CONFIG_ARCH_GATE_AREA) && defined(AT_SYSINFO_EHDR)
+struct vm_area_struct gate_vma;
+
+static int __init gate_vma_init(void)
+{
+	gate_vma.vm_mm = NULL;
+	gate_vma.vm_start = FIXADDR_USER_START;
+	gate_vma.vm_end = FIXADDR_USER_END;
+	gate_vma.vm_page_prot = PAGE_READONLY;
+	gate_vma.vm_flags = 0;
+	return 0;
+}
+__initcall(gate_vma_init);
+#endif

_
