Subject: bug#15147: tr: crash upon failed write(2)

From: Bob Proulx <[email-masked]>
Date: Tue, 20 Aug 2013 15:23:40 -0600

Stephane Chazelas opened a bug in the Debian bug tracker concerning a
core dump crash from tr and I forward it here.  I reproduced this on
my Debian amd64 system with 8.21.

I have CC'd the bug on this message.  Because we have two BTS
instances I won't know the GNU bug number and can't set up the reply
headers ahead of time.  Please ensure when replying to CC the
interested parties.  It will probably take some fiddling.

  http://bugs.debian.org/720352

Bob

Stephane Chazelas wrote:

   Easiest way to reproduce:

   ~$ tr a b < /dev/zero > /dev/full
   zsh: segmentation fault (core dumped)  tr a b < /dev/zero > /dev/full

I first reproduced it on:

   ~$ tr a b < file 1<> file

where "file" was a sparse file and the filesystem was full. In
that other instance, I also observed "tr" outputting random data
to stdout actually corrupting the file.

/mnt# df -h .
Filesystem      Size  Used Avail Use% Mounted on
/dev/loop1      9.7M   92K  9.1M   1% /mnt
/mnt# truncate -s20M a
/mnt# tr a b < a 1<> a
zsh: segmentation fault  tr a b < a 1<> a
(139)/mnt# hd a
00000000  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
0098c400  01 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
0098c410  00 00 00 00 00 00 00 00  49 79 f2 5d ff 7f 00 00  |........Iy.]....|
0098c420  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
01400000

Valgrind shows:

==1521== Memcheck, a memory error detector
==1521== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==1521== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==1521== Command: tr a b
==1521==
==1521== Invalid read of size 8
==1521==    at 0x3FFC28789B: __GI_mempcpy (memcpy.S:272)
==1521==    by 0x3FFC278225: _IO_default_xsputn (genops.c:464)
==1521==    by 0x3FFC2768D2: _IO_file_xsputn@@GLIBC_2.2.5 (fileops.c:1356)
==1521==    by 0x3FFC270F24: fwrite_unlocked (iofwrite_u.c:46)
==1521==    by 0x40229B: ??? (in /usr/bin/tr)
==1521==    by 0x3FFC221994: (below main) (libc-start.c:260)
==1521==  Address 0x60d000 is not stack'd, malloc'd or (recently) free'd
==1521==
==1521==
==1521== Process terminating with default action of signal 11 (SIGSEGV): dumping core
==1521==  Access not within mapped region at address 0x60D000
==1521==    at 0x3FFC28789B: __GI_mempcpy (memcpy.S:272)
==1521==    by 0x3FFC278225: _IO_default_xsputn (genops.c:464)
==1521==    by 0x3FFC2768D2: _IO_file_xsputn@@GLIBC_2.2.5 (fileops.c:1356)
==1521==    by 0x3FFC270F24: fwrite_unlocked (iofwrite_u.c:46)
==1521==    by 0x40229B: ??? (in /usr/bin/tr)
==1521==    by 0x3FFC221994: (below main) (libc-start.c:260)

And gdb from a "tr" compiled with -g -O0:

#0  __mempcpy_sse2 () at ../sysdeps/x86_64/memcpy.S:272
#1  0x0000003ffc278226 in __GI__IO_default_xsputn (f=f@entry=0x3ffc5a7160 <_IO_2_1_stdout_>, data=data@entry=0x60e300 <in_squeeze_set>, n=n@entry=8193) at genops.c:464
#2  0x0000003ffc2768d3 in _IO_new_file_xsputn (n=8192, data=<optimized out>, f=0x3ffc5a7160 <_IO_2_1_stdout_>) at fileops.c:1356
#3  _IO_new_file_xsputn (f=0x3ffc5a7160 <_IO_2_1_stdout_>, data=<optimized out>, n=8192) at fileops.c:1278
#4  0x0000003ffc270f25 in __GI_fwrite_unlocked (buf=<optimized out>, size=1, count=8192, fp=<optimized out>) at iofwrite_u.c:46
#5  0x0000000000404a37 in main (argc=3, argv=0x7ffff50c6d08) at src/tr.c:1938

ltrace:

read(0, "y\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\n"...,
8192) = 8192
fwrite_unlocked("y\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\n"...,
1, 8192, 0x3ffc5a7160 <unfinished ...>
<SEGV>

It could very will be a bug in eglibc as I can't really see
anything wrong with the tr code.

It also occurs with LC_ALL=C

It also occurs on Ubuntu 13.10 amd64, not on 12.04 amd64,
possibly pointing to eglibc 2.17.

I couldn't reproduce it with any other utility only "tr", but
then again, none of the other utilities I tried to run under
ltrace showed any call to fwrite_unlocked with more than a few
bytes..

I can't reproduce it with stdbuf -o3605 tr a b > /dev/full < /dev/zero
or any value below 3605, but I do with any value above that.