diff mbox

[v2] gcc: fix segfault from calling free on non-malloc'd area

Message ID 1403640354-9651-1-git-send-email-paul.gortmaker@windriver.com
State New
Headers show

Commit Message

Paul Gortmaker June 24, 2014, 8:05 p.m. UTC
We see the following on a 32bit gcc installed on 64 bit host:

  Reading symbols from ./i586-pokymllib32-linux-gcc...done.
  (gdb) run
  Starting program: x86-pokymllib32-linux/lib32-gcc/4.9.0-r0/image/usr/bin/i586-pokymllib32-linux-gcc

  Program received signal SIGSEGV, Segmentation fault.
  0xf7e957e0 in free () from /lib/i386-linux-gnu/libc.so.6
  (gdb) bt
  #0  0xf7e957e0 in free () from /lib/i386-linux-gnu/libc.so.6
  #1  0x0804b73c in set_multilib_dir () at gcc-4.9.0/gcc/gcc.c:7827
  #2  main (argc=1, argv=0xffffd504) at gcc-4.9.0/gcc/gcc.c:6688
  (gdb)

The problem arises because we conditionally assign the pointer we
eventually free, and the conditional may assign the pointer to the
non-malloc'd internal string "." which fails when we free it here:

   if (multilib_dir == NULL && multilib_os_dir != NULL
       && strcmp (multilib_os_dir, ".") == 0)
     {
       free (CONST_CAST (char *, multilib_os_dir));
       ...

As suggested by Jakub, ensure the "." case is also malloc'd via
xstrdup() and hence the pointer for the "." case can be freed.

Cc: Jakub Jelinek <jakub@redhat.com>
Cc: Jeff Law <law@redhat.com>
Cc: Matthias Klose <doko@ubuntu.com>
CC: Tobias Burnus <burnus@net-b.de>
Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com>
---

[v2: don't change the causality of the free() ; instead just make
 the "." pointer be malloc'd as well.  Note that I was unable to
 reproduce the broken-ness of my original (broken) patch with a
 direct build of trunk, with "./configure --prefix=/usr/local"
 but I also did re-test this new patch still fixed the error that
 we saw in yocto with gcc-4.9.0 with the invalid free segfault.]

 gcc/gcc.c | 13 +++++++++----
 1 file changed, 9 insertions(+), 4 deletions(-)

Comments

Matthias Klose June 24, 2014, 8:09 p.m. UTC | #1
Am 24.06.2014 22:05, schrieb Paul Gortmaker:
> We see the following on a 32bit gcc installed on 64 bit host:
> 
>   Reading symbols from ./i586-pokymllib32-linux-gcc...done.
>   (gdb) run
>   Starting program: x86-pokymllib32-linux/lib32-gcc/4.9.0-r0/image/usr/bin/i586-pokymllib32-linux-gcc
> 
>   Program received signal SIGSEGV, Segmentation fault.
>   0xf7e957e0 in free () from /lib/i386-linux-gnu/libc.so.6
>   (gdb) bt
>   #0  0xf7e957e0 in free () from /lib/i386-linux-gnu/libc.so.6
>   #1  0x0804b73c in set_multilib_dir () at gcc-4.9.0/gcc/gcc.c:7827
>   #2  main (argc=1, argv=0xffffd504) at gcc-4.9.0/gcc/gcc.c:6688
>   (gdb)
> 
> The problem arises because we conditionally assign the pointer we
> eventually free, and the conditional may assign the pointer to the
> non-malloc'd internal string "." which fails when we free it here:
> 
>    if (multilib_dir == NULL && multilib_os_dir != NULL
>        && strcmp (multilib_os_dir, ".") == 0)
>      {
>        free (CONST_CAST (char *, multilib_os_dir));
>        ...
> 
> As suggested by Jakub, ensure the "." case is also malloc'd via
> xstrdup() and hence the pointer for the "." case can be freed.

I tested the very same test and didn't find any issues. This should go to the
trunk and the active branches (but I cannot approve it).

  Matthias
Jeff Law June 25, 2014, 9:21 p.m. UTC | #2
On 06/24/14 14:05, Paul Gortmaker wrote:
> We see the following on a 32bit gcc installed on 64 bit host:
>
>    Reading symbols from ./i586-pokymllib32-linux-gcc...done.
>    (gdb) run
>    Starting program: x86-pokymllib32-linux/lib32-gcc/4.9.0-r0/image/usr/bin/i586-pokymllib32-linux-gcc
>
>    Program received signal SIGSEGV, Segmentation fault.
>    0xf7e957e0 in free () from /lib/i386-linux-gnu/libc.so.6
>    (gdb) bt
>    #0  0xf7e957e0 in free () from /lib/i386-linux-gnu/libc.so.6
>    #1  0x0804b73c in set_multilib_dir () at gcc-4.9.0/gcc/gcc.c:7827
>    #2  main (argc=1, argv=0xffffd504) at gcc-4.9.0/gcc/gcc.c:6688
>    (gdb)
>
> The problem arises because we conditionally assign the pointer we
> eventually free, and the conditional may assign the pointer to the
> non-malloc'd internal string "." which fails when we free it here:
>
>     if (multilib_dir == NULL && multilib_os_dir != NULL
>         && strcmp (multilib_os_dir, ".") == 0)
>       {
>         free (CONST_CAST (char *, multilib_os_dir));
>         ...
>
> As suggested by Jakub, ensure the "." case is also malloc'd via
> xstrdup() and hence the pointer for the "." case can be freed.
>
> Cc: Jakub Jelinek <jakub@redhat.com>
> Cc: Jeff Law <law@redhat.com>
> Cc: Matthias Klose <doko@ubuntu.com>
> CC: Tobias Burnus <burnus@net-b.de>
> Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com>
> ---
>
> [v2: don't change the causality of the free() ; instead just make
>   the "." pointer be malloc'd as well.  Note that I was unable to
>   reproduce the broken-ness of my original (broken) patch with a
>   direct build of trunk, with "./configure --prefix=/usr/local"
>   but I also did re-test this new patch still fixed the error that
>   we saw in yocto with gcc-4.9.0 with the invalid free segfault.]
>
>   gcc/gcc.c | 13 +++++++++----
>   1 file changed, 9 insertions(+), 4 deletions(-)
THanks.  Installed on the trunk.
diff mbox

Patch

diff --git a/gcc/gcc.c b/gcc/gcc.c
index 9ac18e60d801..168acf7eb0c9 100644
--- a/gcc/gcc.c
+++ b/gcc/gcc.c
@@ -7790,10 +7790,15 @@  set_multilib_dir (void)
 		q2++;
 	      if (*q2 == ':')
 		ml_end = q2;
-	      new_multilib_os_dir = XNEWVEC (char, ml_end - q);
-	      memcpy (new_multilib_os_dir, q + 1, ml_end - q - 1);
-	      new_multilib_os_dir[ml_end - q - 1] = '\0';
-	      multilib_os_dir = *new_multilib_os_dir ? new_multilib_os_dir : ".";
+	      if (ml_end - q == 1)
+		multilib_os_dir = xstrdup (".");
+	      else
+		{
+		  new_multilib_os_dir = XNEWVEC (char, ml_end - q);
+		  memcpy (new_multilib_os_dir, q + 1, ml_end - q - 1);
+		  new_multilib_os_dir[ml_end - q - 1] = '\0';
+		  multilib_os_dir = new_multilib_os_dir;
+		}
 
 	      if (q2 < end && *q2 == ':')
 		{