[patch] Disable child VMA randomizations

Jan Kratochvil
2008-06-07T19:54:33Z

[-- Attachment #1: Type: text/plain, Size: 288 bytes --]

Hi,

the processes map their addresses randomly by default. It can make the
debugging inconvenient as varous addresses are different on each run.

This feature was suggested by Jakub Jelinek. One can also already wrap whole
GDB by a script calling: setarch `uname -m` -R

Regards,
Jan

[-- Attachment #2: gdb-derandomize.patch --]
[-- Type: text/plain, Size: 12957 bytes --]

2008-06-07 Jan Kratochvil <jan.kratochvil@redhat.com>

* configure.ac: Add check for HAVE_PERSONALITY.
* configure, config.in: Regenerate.
* fork-child.c: New include <errno.h>.
[HAVE_PERSONALITY]: New include <sys/personality.h>.
[HAVE_PERSONALITY] (set_disable_randomization): New function.
(disable_randomization, show_disable_randomization): New.
(fork_inferior) [HAVE_PERSONALITY]: Disable randomization upon the
variable DISABLE_RANDOMIZATION.
(_initialize_fork_child): Call ADD_SETSHOW_BOOLEAN_CMD for the variable
DISABLE_RANDOMIZATION.

2008-06-07 Jan Kratochvil <jan.kratochvil@redhat.com>

* gdb.texinfo (Starting): Document "set disable-randomization".

2008-06-07 Jan Kratochvil <jan.kratochvil@redhat.com>

* gdb.base/randomize.exp, gdb.base/randomize.c: New files.

--- gdb/config.in 5 Jun 2008 22:36:56 -0000 1.100
+++ gdb/config.in 7 Jun 2008 18:37:52 -0000
@@ -237,6 +237,9 @@
/* Define to 1 if you have the <nlist.h> header file. */
#undef HAVE_NLIST_H

+/* Define if you support the personality syscall. */
+#undef HAVE_PERSONALITY
+
/* Define to 1 if you have the `poll' function. */
#undef HAVE_POLL

--- gdb/configure 5 Jun 2008 22:36:56 -0000 1.249
+++ gdb/configure 7 Jun 2008 18:38:03 -0000
@@ -26702,6 +26702,108 @@ _ACEOF
fi

+if test "$cross_compiling" = yes; then
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <sys/personality.h>
+int
+main ()
+{
+#define PERSONALITY_ADDR_NO_RANDOMIZE 0x0040000
+ /* Test the flag could be set and stays set. */
+ personality (personality (0xffffffff) | PERSONALITY_ADDR_NO_RANDOMIZE);
+ if (!(personality (personality (0xffffffff))
+ & PERSONALITY_ADDR_NO_RANDOMIZE))
+ return 1
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_PERSONALITY 1
+_ACEOF
+
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <sys/personality.h>
+int
+main ()
+{
+#define PERSONALITY_ADDR_NO_RANDOMIZE 0x0040000
+ /* Test the flag could be set and stays set. */
+ personality (personality (0xffffffff) | PERSONALITY_ADDR_NO_RANDOMIZE);
+ if (!(personality (personality (0xffffffff))
+ & PERSONALITY_ADDR_NO_RANDOMIZE))
+ return 1
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_PERSONALITY 1
+_ACEOF
+
+else
+ echo "$as_me: program exited with status $ac_status" >&5
+echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+
+

# Check whether --with-sysroot or --without-sysroot was given.
if test "${with_sysroot+set}" = set; then
--- gdb/configure.ac 5 Jun 2008 22:36:56 -0000 1.69
+++ gdb/configure.ac 7 Jun 2008 18:38:04 -0000
@@ -1266,6 +1266,23 @@ if test "x$gdb_cv_sys_syscall_h_has_tkil
AC_DEFINE(HAVE_TKILL_SYSCALL, 1, [Define if you support the tkill syscall.])
fi

+dnl Check if we can disable the virtual address space randomization.
+dnl The functionality of setarch -R.
+define([PERSONALITY_TEST], [AC_LANG_PROGRAM([#include <sys/personality.h>],
+ [#define PERSONALITY_ADDR_NO_RANDOMIZE 0x0040000
+ /* Test the flag could be set and stays set. */
+ personality (personality (0xffffffff) | PERSONALITY_ADDR_NO_RANDOMIZE);
+ if (!(personality (personality (0xffffffff))
+ & PERSONALITY_ADDR_NO_RANDOMIZE))
+ return 1])])
+AC_RUN_IFELSE(PERSONALITY_TEST,
+ AC_DEFINE([HAVE_PERSONALITY], 1,
+ [Define if you support the personality syscall.]),
+ ,
+ AC_LINK_IFELSE(PERSONALITY_TEST,
+ AC_DEFINE([HAVE_PERSONALITY], 1,
+ [Define if you support the personality syscall.])))
+
dnl Handle optional features that can be enabled.

AC_ARG_WITH(sysroot,
--- gdb/fork-child.c 15 Mar 2008 14:55:21 -0000 1.41
+++ gdb/fork-child.c 7 Jun 2008 18:38:11 -0000
@@ -35,6 +35,10 @@
#include "solib.h"

#include <signal.h>
+#include <errno.h>
+#ifdef HAVE_PERSONALITY
+# include <sys/personality.h>
+#endif

/* This just gets used as a default if we can't find SHELL. */
#define SHELL_FILE "/bin/sh"
@@ -42,6 +46,34 @@
extern char **environ;

static char *exec_wrapper;
+static int disable_randomization =
+#ifdef HAVE_PERSONALITY
+ 1;
+#else
+ 0;
+#endif
+
+#ifndef HAVE_PERSONALITY
+static void
+set_disable_randomization (char *args, int from_tty, struct cmd_list_element *c)
+{
+ if (disable_randomization)
+ {
+ disable_randomization = 0;
+ error (_("Unsupported on this platform."));
+ }
+}
+#endif
+
+static void
+show_disable_randomization (struct ui_file *file, int from_tty,
+ struct cmd_list_element *c, const char *value)
+{
+ fprintf_filtered (file, _("\
+Whether we disable the randomization of the virtual address space of\n\
+a spawned child is %s.\n"),
+ value);
+}

/* Break up SCRATCH into an argument vector suitable for passing to
execvp and store it in ARGV. E.g., on "run a b c d" this routine
@@ -303,6 +335,23 @@ fork_inferior (char *exec_file_arg, char
if (debug_fork)
sleep (debug_fork);

+#ifdef HAVE_PERSONALITY
+ if (disable_randomization)
+ {
+# define PERSONALITY_ADDR_NO_RANDOMIZE 0x0040000
+ int val;
+
+ errno = 0;
+ val = personality (0xffffffff);
+ if (errno == 0)
+ personality (val | PERSONALITY_ADDR_NO_RANDOMIZE);
+ if (errno != 0
+ || !(personality (0xffffffff) & PERSONALITY_ADDR_NO_RANDOMIZE))
+ warning (_("Currently enabled disable-randomization is unsupported "
+ "on this platform."));
+ }
+#endif /* HAVE_PERSONALITY */
+
/* Create a new session for the inferior process, if necessary.
It will also place the inferior in a separate process group. */
if (create_tty_session () <= 0)
@@ -490,4 +539,19 @@ Show the wrapper for running programs.")
add_cmd ("exec-wrapper", class_run, unset_exec_wrapper_command,
_("Disable use of an execution wrapper."),
&unsetlist);
+
+ add_setshow_boolean_cmd ("disable-randomization", class_support,
+ &disable_randomization, _("\
+Set mode for inserting breakpoints."), _("\
+Show mode for inserting breakpoints."), _("\
+When this mode is on (which is the default), the randomization of\n\
+the virtual address space is disabled (turns on ADDR_NO_RANDOMIZE).\n\
+Standalone programs run with the randomization enabled by default."),
+#ifdef HAVE_PERSONALITY
+ NULL,
+#else
+ set_disable_randomization,
+#endif
+ &show_disable_randomization,
+ &setlist, &showlist);
}
--- gdb/doc/gdb.texinfo 6 Jun 2008 20:58:08 -0000 1.503
+++ gdb/doc/gdb.texinfo 7 Jun 2008 18:38:48 -0000
@@ -1999,6 +1999,38 @@ environment:
This command is available when debugging locally on most targets, excluding
@sc{djgpp}, Cygwin, MS Windows, and QNX Neutrino.

+@kindex set disable-randomization
+@item set disable-randomization
+@itemx set disable-randomization on
+This option (enabled by default in @value{GDBN}) will turn off the native
+randomization of the virtual address space of the started program. This option
+is useful for multiple debugging sessions to make the execution better
+reproducible and memory addresses reusable across debugging sessions.
+
+This feature is implemented only on @sc{gnu}/Linux. You can get the same
+behavior using
+
+@smallexample
+(@value{GDBP}) set exec-wrapper setarch `uname -m` -R
+@end smallexample
+
+@item set disable-randomization off
+Leave the behavior of the started executable unchanged. While the addresses
+get assigned differently on each run some subtle bugs may be reproducible only
+with specially assigned addresses possibly not reachable with the default
+setting of @kbd{set disable-randomization on}.
+
+PIE executables (type @code{ET_DYN}, compiled by @code{gcc -fPIE -pie}) have
+randomized everything - the executable base address, shared libraries base
+address (their prelinking is ignored), mmap areas, stack and heap. Regular
+executables (type @code{ET_EXEC}) do not have randomized their base address,
+shared libraries base address is ranomized only for non-prelinked libraries,
+mmap, stack and heap are still randomized.
+
+@item show disable-randomization
+Show the current setting of the explicit disable of the native randomization of
+the virtual address space of the started program.
+
@end table

@node Arguments
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ gdb/testsuite/gdb.base/randomize.c 7 Jun 2008 18:38:48 -0000
@@ -0,0 +1,32 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2008 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+ Please email any bugs, comments, and/or additions to this file to:
+ bug-gdb@prep.ai.mit.edu */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+int main()
+{
+ void *p;
+
+ p = malloc (1);
+ printf ("address = %p\n", p);
+
+ return 0;
+}
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ gdb/testsuite/gdb.base/randomize.exp 7 Jun 2008 18:38:48 -0000
@@ -0,0 +1,63 @@
+# Copyright 2008 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+set testfile randomize
+set srcfile ${testfile}.c
+set binfile ${objdir}/${subdir}/${testfile}
+if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } {
+ untested "Couldn't compile test program"
+ return -1
+}
+
+# Get things started.
+
+gdb_exit
+gdb_start
+gdb_reinitialize_dir $srcdir/$subdir
+gdb_load ${binfile}
+
+proc address_get { testname } {
+ global gdb_prompt
+
+ if {![runto_main]} {
+ return -1
+ }
+ gdb_test_multiple "continue" $testname {
+ -re "address = (0x\[0-9a-f\]*).*Program exited normally..*$gdb_prompt $" {
+ pass $testname
+ return $expect_out(1,string)
+ }
+ }
+}
+
+gdb_test "set disable-randomization off"
+set addr1 [address_get "randomized first address"]
+set addr2 [address_get "randomized second address"]
+set test "randomized addresses should not match"
+if {$addr1 eq $addr2} {
+ fail $test
+} else {
+ pass $test
+}
+
+gdb_test "set disable-randomization on"
+set addr1 [address_get "fixed first address"]
+set addr2 [address_get "fixed second address"]
+set test "fixed addresses should match"
+if {$addr1 eq $addr2} {
+ pass $test
+} else {
+ fail $test
+}