strange bug, alloca suspected

Yann Droneaud (yann.droneaud@iupmime.univ-lemans.fr)
Tue, 13 Feb 2001 22:10:27 +0100


Hi,

I found a strange bug with modprobe/glibc

I supposed this is a bad interaction between gcc alloca(), glibc and modprobe.

Modprobe don't use alloca() correctly, then glibc failed. (stack corruption ?)

This need more investigation.

This mail is sent to glibc, gcc and modutils maintainers.

>Submitter-Id: net
>Originator: Yann Droneaud <yann.droneaud@iupmime.univ-lemans.fr>
>Organization: no others than mine
>Confidential: no
>Synopsis: undetermined
>Severity: non-critical
>Priority: low
>Category: libc
>Class: sw-bug
>Release: libc-2.1.3

>Environment:

Host type: i586-pc-linux-gnu
System: Linux Corwin.Ambre 2.4.0-prerelease #1 Mon Jan 1 23:11:11 CET 2001 i586
unknown
Architecture: i586

Addons: crypt linuxthreads
Build CFLAGS: -g -O2 -march=i586
Build CC: gcc
Compiler version: 2.95.2 19991024 (release)
Kernel headers: 2.4.1
Symbol versioning: yes
Build static: yes
Build shared: yes
Build pic-default: no
Build profile: yes
Build omitfp: no
Build bounded: no
Build static-nss: no
Stdio: libio
compiled against linux 2.3.99-pre5 with gcc-2.95.2

Others:
modutils-2.4.2
configured with './configure --prefix=/usr --exec-prefix= --disable-combined
--enable-combined-rmmod --enable-combined-lsmod --disable-strip'
gcc 2.95.2
bash 2.03.0(1)-release

>Description:
>How-To-Repeat:

How to reproduce bug
--------------------
on shell prompt, as root type:
# /sbin/modprobe --help
just display help, no problem.

try this now:
# modprobe --help
display help,
but finish by a Segmentation Fault

What a strange behaviour, isn't it ?

>Fix:

A little taste of debugging:
----------------------------
I decide to debug modprobe.
I added a signal handler for SIGSEGV.
The handler only wait for the debugger.

0x400951f1 in __libc_nanosleep () from /lib/libc.so.6
(gdb) bt
#0 0x400951f1 in __libc_nanosleep () from /lib/libc.so.6
#1 0x40095188 in __sleep (seconds=1) at ../sysdeps/unix/sysv/linux/sleep.c:82
#2 0x804ba3a in sighandler ()
#3 <signal handler called>
#4 tdestroy_recurse (root=0x0, freefct=0x400f1d20 <_IO_2_1_stderr_>) at
tsearch.c:637
#5 0x100 in ?? ()
(gdb) directory /tmp/glibc-2.1.3/misc
Source directories searched: /tmp/glibc-2.1.3/misc:$cdir:$cwd
(gdb) frame 4
#4 tdestroy_recurse (root=0x0, freefct=0x400f1d20 <_IO_2_1_stderr_>) at
tsearch.c:637
637 if (root->left != NULL)
(gdb) list
632 tree cannot be removed easily. We provide a function to do this. */
633 static void
634 internal_function
635 tdestroy_recurse (node root, __free_fn_t freefct)
636 {
637 if (root->left != NULL)
638 tdestroy_recurse (root->left, freefct);
639 if (root->right != NULL)
640 tdestroy_recurse (root->right, freefct);
641 (*freefct) ((void *) root->key);
(gdb) print root
$1 = 0x0

I wasn't able to find how/where this function could be call with a NULL
argument.

A little patch for glibc 2.1.3, misc/tsearch.c

=============================================================
--- tsearch.c Thu Nov 6 00:18:09 1997
+++ tsearch-meuh.c Sun Feb 11 23:54:37 2001
@@ -634,13 +634,22 @@
internal_function
tdestroy_recurse (node root, __free_fn_t freefct)
{
- if (root->left != NULL)
- tdestroy_recurse (root->left, freefct);
- if (root->right != NULL)
- tdestroy_recurse (root->right, freefct);
- (*freefct) ((void *) root->key);
- /* Free the node itself. */
- free (root);
+ if (root != NULL)
+ {
+ if (root->left != NULL)
+ tdestroy_recurse (root->left, freefct);
+ if (root->right != NULL)
+ tdestroy_recurse (root->right, freefct);
+ (*freefct) ((void *) root->key);
+ /* Free the node itself. */
+ free (root);
+ }
+#ifdef DEBUGGING
+ else
+ {
+ assert(root != NULL);
+ }
+#endif /* DEBUGGING */
}

void
===============================================================

For modprobe
I review the code before the getopt_long(), I found the alloca() call did not
reserve room for the last NUL char
The only thing important is the l++;

so the patch:
================================================================
--- modutils-2.4.2/insmod/modprobe.c Fri Jan 19 07:26:33 2001
+++ modutils-2.4.2-debug/insmod/modprobe.c Mon Feb 12 00:00:58 2001
@@ -33,6 +33,9 @@
#include <limits.h>
#include <sys/param.h>
#include <sys/stat.h>

+#include <signal.h>

#include "module.h"
#include "obj.h"
#include "modstat.h"
@@ -1476,6 +1479,25 @@
);
}

+#ifdef DEBUG
+void sighandler(int sig)
+{
+ int i;
+ static int wait_for_gdb;

+ printf("%d: signal SIGSEGV: %d, waiting for debugger\n", getpid(), sig);

+ while (wait_for_gdb == 0)
+ sleep(1);

+ /* wait about 10 seconds */
+ for(i = 0; i < 10 ; i++)
+ sleep(1);

+ exit(1);
+}
+#endif

int main(int argc, char *argv[])
{
int ret = 0;
@@ -1506,10 +1528,18 @@
int i, l = 0;
char *command;

+#ifdef DEBUG
+ signal(SIGSEGV, sighandler);
+#endif

for (i = 0; i < argc; ++i)
l += strlen(argv[i]) + 1;

+ l++; /* be sure to have room for last NUL char */

command = alloca(l);
*command = '\0';

for (i = 0; i < argc; ++i) {
strcat(command, argv[i]);
strcat(command, " ");
==================================================================

-- 
Yann Droneaud <yann.droneaud@iupmime.univ-lemans.fr>
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/