Subject: bin/29806: grep segfaults on empty files
To: None <gnats-admin@netbsd.org, netbsd-bugs@netbsd.org>
From: None <mlelstv@serpens.de>
List: netbsd-bugs
Date: 03/27/2005 22:35:00
>Number:         29806
>Category:       bin
>Synopsis:       grep segfaults on empty files
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    bin-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Sun Mar 27 22:35:00 +0000 2005
>Originator:     Michael van Elst
>Release:        NetBSD 3.99.1
>Organization:
-- 
                                Michael van Elst
Internet: mlelstv@serpens.de
                                "A potential Snark may lurk in every tree."
>Environment:
	
	
System: NetBSD pussyfoot 3.99.1 NetBSD 3.99.1 (PUSSYFOOT) #0: Sun Mar 27 19:09:41 MEST 2005 src@pussyfoot:/sys/arch/i386/compile/PUSSYFOOT i386
Architecture: i386
Machine: i386
>Description:
grep segfaults on empty files.

grep uses mmap() to read files that fit completely into memory.
The mmbin_file() function then scans the mapped file for nontext
characters within the first MIN(128,length_of_file-1) bytes.

The calculation of length_of_file-1 is done with unsigned arithmetic,
causing an underflow for zero length files. In this case
128 bytes are scanned.

Under 2.0 a mmap() of length 0 returns a pointer to a read-only page
(that seems to be the beginning of the executable starting with the
ELF header). That page is scanned for nontext characters, but otherwise
the failure seems to be harmless.

Under -current a mmap() of length 0 returns a pointer to a page with
no access permissions. Scanning that page results in a segmentation
fault.

It is unclear why the file should only be scanned up to length-1.
A simple fix is to scan to the end of the file, avoiding the
subtraction and thus the underflow.

>How-To-Repeat:
% cp /dev/zero foo
% grep 1 foo
Segmentation fault (core dumped)

>Fix:

Index: binary.c
===================================================================
RCS file: /cvsroot/src/usr.bin/grep/binary.c,v
retrieving revision 1.2
diff -u -r1.2 binary.c
--- binary.c	30 Oct 2004 17:37:09 -0000	1.2
+++ binary.c	27 Mar 2005 21:33:07 -0000
@@ -87,7 +87,7 @@
 {
 	int i;
 	/* XXX knows too much about mmf internals */
-	for (i = 0; i < BUFFER_SIZE && i < f->len - 1; i++)
+	for (i = 0; i < BUFFER_SIZE && i < f->len; i++)
 		if (!isprint((unsigned char)f->base[i]) &&
 		    !isspace((unsigned char)f->base[i]) &&
 		    f->base[i] != line_endchar)