This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Give a better error for PCH with exec-shield-randomize


In the current sources, using PCH on a system using Linux style
exec-shield-randomize will generally crash the compiler in some
horrible manner.

It turns out that at least with Linux kernel 2.4.22 in Fedora Core 1,
the mincore function will not detect when a memory page is being used
by an anonymous mapping.  It turns out that the kernel will permit an
mmap with MAP_FIXED to overlap such a mapping.  It happens that the
garbage collectors use anonymous mappings.  The effect is that the
code in gt_pch_restore() will not notice the overlap with the garbage
collector and will succeed in forcing the mapping.  This will of
course lead to a horrible crash as the same memory page is being used
in two very different ways.

This patch fixes that problem, by adding a function ggc_pch_overlap to
the garbage collectors.  That function returns whether the given
address overlaps.  The function is only called when mmap without
MAP_FIXED fails to return the expected address, so the overhead should
not matter.

This patch also explicitly checks for the Linux kernel procfs file
/proc/sys/kernel/exec-shield-randomize.  If that file exists and holds
a non-zero number, then the kernel has exec-shield-randomize enabled.
In that case, we give a mildly informative error message.

I also added an abort() after the call to sorry().  There is no chance
of success at this point, and a good chance of a horrible crash.  I
don't see any reason for the compiler to continue.

This patch also includes two minor corrections.  Don't munmap the
address until we are sure that we are going to mmap a new address.
When reading from a file, read it to the correct memory address.

OK for mainline?

Ian


2004-03-03  Ian Lance Taylor  <ian@wasabisystems.com>

	* ggc-common.c (gt_pch_restore): If we didn't get the address we
	wanted, call ggc_pch_overlap to check whether the garbage
	collector is using the pages we need.  Don't munmap the address
	unless we are going to try to mmap again.  When reading from the
	file, read into the memory we allocated.  When handling a
	relocation failure, check for Linux kernel exec-shield-randomize,
	and give a better error message if it is set.
	* ggc.h (ggc_pch_overlap): Declare.
	* ggc-page.c (ggc_pch_overlap): New function.
	* ggc-zone.c (ggc_pch_overlap): New function.


Index: ggc-common.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/ggc-common.c,v
retrieving revision 1.83
diff -p -u -r1.83 ggc-common.c
--- ggc-common.c	3 Mar 2004 11:25:47 -0000	1.83
+++ ggc-common.c	4 Mar 2004 04:29:22 -0000
@@ -611,34 +611,43 @@ gt_pch_restore (FILE *f)
 #if HAVE_MINCORE
       if (addr != mmi.preferred_base)
 	{
-	  size_t page_size = getpagesize();
-	  char one_byte;
-	  
-	  if (addr != (void *) MAP_FAILED)
-	    munmap (addr, mmi.size);
-	  
-	  /* We really want to be mapped at mmi.preferred_base
-	     so we're going to resort to MAP_FIXED.  But before,
-	     make sure that we can do so without destroying a
-	     previously mapped area, by looping over all pages
-	     that would be affected by the fixed mapping.  */
-	  errno = 0;
+	  /* We didn't get the address we wanted.  See if it might be
+	     possible to force it by using MAP_FIXED.  Check whether
+	     the garbage collector has allocated any pages in the area
+	     we need.  If not, use mincore to check whether any of the
+	     pages have been allocated for other purposes.  If not,
+	     try using MAP_FIXED.  (We have to check the garbage
+	     collector pages separately because the Linux kernel, at
+	     least around 2.4.20, returns ENOMEM for an anonymous
+	     mmap, such as the ones used by the garbage
+	     collector.)  */
+	  if (! ggc_pch_overlap (mmi.preferred_base, mmi.size))
+	    {
+	      size_t page_size = getpagesize ();
+	      char one_byte;
+
+	      errno = 0;
 	  
-	  for (i = 0; i < mmi.size; i+= page_size)
-	    if (mincore ((char *)mmi.preferred_base + i, page_size, 
-			 (void *)&one_byte) == -1
-		&& errno == ENOMEM)
-	      continue; /* The page is not mapped.  */
-	    else
-	      break;
+	      for (i = 0; i < mmi.size; i += page_size)
+		if (mincore ((char *) mmi.preferred_base + i, page_size, 
+			     (void *) &one_byte) == -1
+		    && errno == ENOMEM)
+		  continue; /* The page is not mapped.  */
+		else
+		  break;
 	  
-	  if (i >= mmi.size)
-	    addr = mmap (mmi.preferred_base, mmi.size, 
-			 PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_FIXED,
-			 fileno (f), mmi.offset);
+	      if (i >= mmi.size)
+		{
+		  if (addr != (void *) MAP_FAILED)
+		    munmap (addr, mmi.size);
+		  addr = mmap (mmi.preferred_base, mmi.size, 
+			       PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_FIXED,
+			       fileno (f), mmi.offset);
+		}
+	    }
 	}
 #endif /* HAVE_MINCORE */
-      
+
       needs_read = addr == (void *) MAP_FAILED;
 
 #else /* HAVE_MMAP_FILE */
@@ -651,7 +660,7 @@ gt_pch_restore (FILE *f)
   if (needs_read)
     {
       if (fseek (f, mmi.offset, SEEK_SET) != 0
-	  || fread (&mmi, mmi.size, 1, f) != 1)
+	  || fread (addr, mmi.size, 1, f) != 1)
 	fatal_error ("can't read PCH file: %m");
     }
   else if (fseek (f, mmi.offset + mmi.size, SEEK_SET) != 0)
@@ -679,7 +688,31 @@ gt_pch_restore (FILE *f)
 		*ptr += (size_t)addr - (size_t)mmi.preferred_base;
 	    }
 
+      /* A Linux kernel with exec-shield-randomize set to a non-zero
+	 value won't work.  Give a nice error message for this common
+	 case.  */
+      {
+	FILE *pf;
+
+	pf = fopen ("/proc/sys/kernel/exec-shield-randomize", "r");
+	if (pf != NULL)
+	  {
+	    char buf[100];
+	    size_t c;
+
+	    c = fread (buf, 1, sizeof buf - 1, pf);
+	    if (c > 0)
+	      {
+		buf[c] = '\0';
+		if (atoi (buf) > 0)
+		  inform ("PCH is not compatible with exec-shield-randomize");
+	      }
+	    fclose (pf);
+	  }
+      }
+
       sorry ("had to relocate PCH");
+      abort ();
     }
 
   gt_pch_restore_stringpool ();
Index: ggc.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/ggc.h,v
retrieving revision 1.66
diff -p -u -r1.66 ggc.h
--- ggc.h	3 Mar 2004 11:25:48 -0000	1.66
+++ ggc.h	4 Mar 2004 04:29:22 -0000
@@ -200,6 +200,10 @@ extern void ggc_pch_finish (struct ggc_p
    parameter.  Set up the GC implementation for the new objects.  */
 extern void ggc_pch_read (FILE *, void *);
 
+/* Return whether any of the pages we have allocated overlap with the
+   memory range BASE to SIZE.  */
+extern int ggc_pch_overlap (char *, size_t);
+
 
 /* Allocation.  */
 
Index: ggc-page.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/ggc-page.c,v
retrieving revision 1.90
diff -p -u -r1.90 ggc-page.c
--- ggc-page.c	3 Mar 2004 11:25:47 -0000	1.90
+++ ggc-page.c	4 Mar 2004 04:29:23 -0000
@@ -2404,3 +2404,27 @@ ggc_pch_read (FILE *f, void *addr)
   /* Update the statistics.  */
   G.allocated = G.allocated_last_gc = offs - (char *)addr;
 }
+
+/* Return whether any of the pages we have allocated overlap with the
+   memory range BASE to SIZE.  */
+
+int
+ggc_pch_overlap (char *base, size_t size)
+{
+#ifdef USING_MMAP
+  unsigned int order;
+  page_entry *p;
+
+  for (order = 2; order < NUM_ORDERS; order++)
+    {
+      for (p = G.pages[order]; p != NULL; p = p->next)
+	if (p->page + p->bytes > base && p->page < base + size)
+	  return 1;
+    }
+  for (p = G.free_pages; p != NULL; p = p->next)
+    if (p->page + p->bytes > base && p->page < base + size)
+      return 1;
+#endif
+
+  return 0;
+}
Index: ggc-zone.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/ggc-zone.c,v
retrieving revision 2.14
diff -p -u -r2.14 ggc-zone.c
--- ggc-zone.c	4 Mar 2004 04:25:12 -0000	2.14
+++ ggc-zone.c	4 Mar 2004 04:29:25 -0000
@@ -1412,3 +1412,29 @@ ggc_pch_read (FILE *f, void *addr)
   entry->next = entry->zone->pages;
   entry->zone->pages = entry;
 }
+
+int
+ggc_pch_overlap (char *base, size_t size)
+{
+#ifdef USING_MMAP
+  struct alloc_zone *zone;
+
+  for (zone = G.zones; zone; zone = zone->next_zone)
+    {
+      page_entry *p;
+
+      for (p = zone->pages; p; p = p->next)
+	{
+	  if (p->page + G.pagesize > base && p->page < base + size)
+	    return 1;
+	}
+      for (p = zone->free_pages; p; p = p->next)
+	{
+	  if (p->page + G.pagesize > base && p->page < base + size)
+	    return 1;
+	}
+    }
+#endif
+
+  return 0;
+}


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]