Subject: bug#900: temacs segmentation fault in unexec under Linux 2.6.26

From: Ulrich Mueller <[email-masked]>
Date: Tue, 9 Sep 2008 17:02:04 +0200

Tags: patch

I guess the issue boils down to the fact that testing for
(heap_bss_diff > MAX_HEAP_BSS_DIFF) is not a reliable method to
determine if heap randomisation is switched on. "heap_bss_diff" is
random in nature, and will therefore be smaller than MAX_HEAP_BSS_DIFF
in some cases. These lead to the observed segmentation faults.

Here is an attempt of a patch, asking the kernel (via /proc fs) for
the presence of the feature. I've also made the definition of
ADDR_NO_RANDOMIZE conditional, since it is already defined in newer
versions of personality.h.

Patch was tested with 22.3, but also applies cleanly to the CVS trunk
of today.

*** emacs-orig/src/emacs.c	2008-05-12 21:55:52.000000000 +0200
--- emacs/src/emacs.c	2008-09-09 16:26:52.000000000 +0200
***************
*** 73,78 ****
--- 73,81 ----
  
  #ifdef HAVE_PERSONALITY_LINUX32
  #include <sys/personality.h>
+ #ifndef ADDR_NO_RANDOMIZE
+ #define ADDR_NO_RANDOMIZE 0x0040000
+ #endif
  #endif
  
  #ifndef O_RDWR
***************
*** 789,794 ****
--- 792,817 ----
    return count >= 3 ? REPORT_EMACS_BUG_PRETEST_ADDRESS : REPORT_EMACS_BUG_ADDRESS;
  }
  
+ #ifdef HAVE_PERSONALITY_LINUX32
+ /* Get the `randomize_va_space' parameter. A value of 2 (introduced
+    in Linux 2.6.25) indicates that brk() randomization is switched on,
+    which will break unexec. See <http://lkml.org/lkml/2007/10/23/435>. */
+ static int
+ linux_randomize_va_space ()
+ {
+   FILE *fp;
+   int rand, count;
+ 
+   fp = fopen ("/proc/sys/kernel/randomize_va_space", "r");
+   if (!fp)
+     return -1;
+   count = fscanf (fp, "%d", &rand);
+   (void) fclose (fp);
+   if (count != 1)
+     return -1;
+   return rand;
+ }
+ #endif /* HAVE_PERSONALITY_LINUX32 */
  
  /* ARGSUSED */
  int
***************
*** 883,906 ****
    if (!initialized
        && (strcmp (argv[argc-1], "dump") == 0
            || strcmp (argv[argc-1], "bootstrap") == 0)
!       && heap_bss_diff > MAX_HEAP_BSS_DIFF)
      {
!       if (! getenv ("EMACS_HEAP_EXEC"))
!         {
!           /* Set this so we only do this once.  */
!           putenv("EMACS_HEAP_EXEC=true");
! 
! 	  /* A flag to turn off address randomization which is introduced
! 	   in linux kernel shipped with fedora core 4 */
! #define ADD_NO_RANDOMIZE 0x0040000
! 	  personality (PER_LINUX32 | ADD_NO_RANDOMIZE);
! #undef  ADD_NO_RANDOMIZE
! 
!           execvp (argv[0], argv);
! 
!           /* If the exec fails, try to dump anyway.  */
!           perror ("execvp");
!         }
      }
  #endif /* HAVE_PERSONALITY_LINUX32 */
  
--- 906,925 ----
    if (!initialized
        && (strcmp (argv[argc-1], "dump") == 0
            || strcmp (argv[argc-1], "bootstrap") == 0)
!       && !getenv ("EMACS_HEAP_EXEC")
!       && (heap_bss_diff > MAX_HEAP_BSS_DIFF
! 	  || linux_randomize_va_space() >= 2))
      {
!       /* Set this so we only do this once.  */
!       putenv("EMACS_HEAP_EXEC=true");
! 
!       /* Set personality and disable randomization of VA space. */
!       personality (PER_LINUX32 | ADDR_NO_RANDOMIZE);
! 
!       execvp (argv[0], argv);
! 
!       /* If the exec fails, try to dump anyway.  */
!       perror ("execvp");
      }
  #endif /* HAVE_PERSONALITY_LINUX32 */