Subject: bin/35848: make core dump on some Makefile
To: None <gnats-admin@netbsd.org, netbsd-bugs@netbsd.org>
From: None <eau@phear.org>
List: netbsd-bugs
Date: 02/26/2007 13:50:00
>Number:         35848
>Category:       bin
>Synopsis:       make core dump on some Makefile
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    bin-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Mon Feb 26 13:50:00 +0000 2007
>Originator:     Eric Auge
>Release:        4.0_BETA2
>Organization:
none
>Environment:
NetBSD kamehouse.no.phear.org 4.0_BETA2 NetBSD 4.0_BETA2 (KameHouse) #12: Mon Jan 22 11:03:40 CET 2007  root@kamehouse.no.phear.org:/usr/obj/sys/arch/i386/compile/KAMEHOUSE.MPACPI i386

>Description:
When try to to 'make' with this Makefile :
----- CUT ------
GLOP=*.ext

all:  toto

toto: 
.for I in ${GLOP:O:u}
TEST=${I}
TESTOR=${I:.ext=.newext}
        @echo "test:"
        @echo ${I}
        @echo $(I) ${I:.ext=.newext}
.endfor
------ PASTE ------

I get the following behavior :
rival@kamehouse:~/tmp $ make
make: "/home/rival/tmp/Makefile" line 9: Unassociated shell command "@echo "test:""
Segmentation fault (core dumped)

gdb'ing it lead to this :
Core was generated by `make'.
Program terminated with signal 11, Segmentation fault.
#0  0x08060b9f in Lst_First (l=0x0) at /usr/src/usr.bin/make/lst.lib/lstFirst.c:71
71          if (!LstValid (l) || LstIsEmpty (l)) {
gdb+mamod> bt
#0  0x08060b9f in Lst_First (l=0x0) at /usr/src/usr.bin/make/lst.lib/lstFirst.c:71
#1  0x08060bb4 in Lst_ForEach (l=0x0, proc=0x8056edc <ParseAddCmd>, d=0x8084f01) at /usr/src/usr.bin/make/lst.lib/lstForEach.c:74
#2  0x08058262 in Parse_File (name=0x806266f "Makefile", stream=0xbbbd06e0) at /usr/src/usr.bin/make/parse.c:2552
#3  0x08053ad2 in ReadMakefile (p=0x806266f, q=0x0) at /usr/src/usr.bin/make/main.c:1194
#4  0x08054562 in main (argc=0x1, argv=0xbfbfe588) at /usr/src/usr.bin/make/main.c:979

>How-To-Repeat:
use the pasted Makefile and make.
>Fix:
LstValid(l) is obviously not doing its job, may be some sanity checks
are missing up on the code path somewhere, here is some patch (may be not the best one though, since the issue can exist somewhere else in the code) :

root@kamehouse:/usr/src/usr.bin/make # diff -ru lst.lib/lstFirst.c lst.lib/lstFirst.c.new 
--- lst.lib/lstFirst.c  2007-02-26 14:39:52.000000000 +0100
+++ lst.lib/lstFirst.c.new      2007-02-26 14:39:15.000000000 +0100
@@ -68,6 +68,8 @@
 LstNode
 Lst_First(Lst l)
 {
+    if (!l)
+       return (NILLNODE);
     if (!LstValid (l) || LstIsEmpty (l)) {
        return (NILLNODE);
     } else {

And
root@kamehouse:/usr/src/usr.bin/make # diff -ru lst.lib/lstForEachFrom.c lst.lib/lstForEachFrom.c.new 
--- lst.lib/lstForEachFrom.c    2007-02-26 14:39:52.000000000 +0100
+++ lst.lib/lstForEachFrom.c.new        2007-02-26 14:39:30.000000000 +0100
@@ -79,6 +79,8 @@
     Boolean            done;
     int                result;
 
+    if (!l)
+       return 0;
     if (!LstValid (list) || LstIsEmpty (list)) {
        return 0;
     }

Other part of make's code might be affected, not sure though :

root@kamehouse:/usr/src/usr.bin/make # grep -r LstIsEmpty *
lst.lib/lstFirst.c.new:    if (!LstValid (l) || LstIsEmpty (l)) {
lst.lib/lstAppend.c:    if (LstValid (l) && (ln == NILLNODE && LstIsEmpty (l))) {
lst.lib/lstAppend.c:    if (!LstValid (l) || LstIsEmpty (l)  || ! LstNodeValid (ln, l)) {
lst.lib/lstFindFrom.c:    if (!LstValid (l) || LstIsEmpty (l) || !LstNodeValid (ln, l)) {
lst.lib/lstFirst.c:    if (!LstValid (l) || LstIsEmpty (l)) {
lst.lib/lstInsert.c:    if (LstValid (l) && (LstIsEmpty (l) && ln == NILLNODE))
lst.lib/lstInsert.c:    if (!LstValid (l) || LstIsEmpty (l) || !LstNodeValid (ln, l)) {
lst.lib/lstInt.h: * LstIsEmpty (l) --
lst.lib/lstInt.h:#define LstIsEmpty(l)  (((List)(l))->firstPtr == NilListNode)
lst.lib/lstIsEmpty.c: * LstIsEmpty.c --
lst.lib/lstIsEmpty.c:    return ( ! LstValid (l) || LstIsEmpty(l));
lst.lib/lstLast.c:    if (!LstValid(l) || LstIsEmpty (l)) {
lst.lib/lstOpen.c:      (l)->atEnd = LstIsEmpty (l) ? Head : Unknown;
lst.lib/lstForEachFrom.c:    if (!LstValid (list) || LstIsEmpty (list)) {
lst.lib/lstForEachFrom.c:    } while (!result && !LstIsEmpty(list) && !done);
lst.lib/lstForEachFrom.c.new:    if (!LstValid (list) || LstIsEmpty (list)) {
lst.lib/lstForEachFrom.c.new:    } while (!result && !LstIsEmpty(list) && !done);