Bug 1506344
| Summary: | Possible out of bounds write in xec.c:iousepipe | ||||||||
|---|---|---|---|---|---|---|---|---|---|
| Product: | Red Hat Enterprise Linux 6 | Reporter: | Paulo Andrade <pandrade> | ||||||
| Component: | ksh | Assignee: | Siteshwar Vashisht <svashisht> | ||||||
| Status: | CLOSED CURRENTRELEASE | QA Contact: | BaseOS QE - Apps <qe-baseos-apps> | ||||||
| Severity: | medium | Docs Contact: | |||||||
| Priority: | urgent | ||||||||
| Version: | 6.7 | CC: | bbreard, fkrska, jkurik, kdudka, mkyral, rzaleski, svashisht, toneata | ||||||
| Target Milestone: | rc | Keywords: | Patch, Reopened, ZStream | ||||||
| Target Release: | --- | ||||||||
| Hardware: | All | ||||||||
| OS: | Linux | ||||||||
| Whiteboard: | |||||||||
| Fixed In Version: | Doc Type: | If docs needed, set a value | |||||||
| Doc Text: | Story Points: | --- | |||||||
| Clone Of: | |||||||||
| : | 1506347 1536977 1537053 (view as bug list) | Environment: | |||||||
| Last Closed: | 2018-06-21 08:47:59 UTC | Type: | Bug | ||||||
| Regression: | --- | Mount Type: | --- | ||||||
| Documentation: | --- | CRM: | |||||||
| Verified Versions: | Category: | --- | |||||||
| oVirt Team: | --- | RHEL 7.3 requirements from Atomic Host: | |||||||
| Cloudforms Team: | --- | Target Upstream Version: | |||||||
| Embargoed: | |||||||||
| Bug Depends On: | |||||||||
| Bug Blocks: | 1494481, 1506347, 1536977, 1537053 | ||||||||
| Attachments: |
|
||||||||
The pattern being found is 0xa 'IOSEEK|IOWRITE'.
In a previous crash, this was found when it crashed in the free call in
sh_iovalidfd:
"""
Sfio_t **sftable = shp->sftable;
...
max = shp->gd->lim.open_max;
shp->sftable = (Sfio_t**)calloc((n+1)*(sizeof(int*)+sizeof(Sfio_t*)+1),1);
if(max)
memcpy(shp->sftable,sftable,max*sizeof(Sfio_t*));
...
if(sftable)
free((void*)sftable);
and the crash is in the free call, but the addresses do not look like
related in a way that could corrupt glibc malloc metadata.
The out of bounds write was at in iousepipe():
shp->fdstatus[subpipe[2]] = shp->fdstatus[1];
(gdb) p sh.gd.lim
$1 = {arg_max = 2621440, open_max = 32, clk_tck = 100, child_max = 16384, ngroups_max = 65536, posix_version = 188 '\274',
posix_jobcontrol = 1 '\001', fs3d = 0 '\000'}
Using the previous value of sh.sftable (the sftable variable being released), and
knowing how the data is packed:
(gdb) p ((char*)(sftable+64))[1]
$2 = 10 '\n'
and this memory would be 28 bytes after the end of the buffer:
(gdb) p ((char*)(sftable+64))[60]
$3 = 10 '\n'
I said the addresses do not look related because:
(gdb) p sftable
$4 = (Sfio_t **) 0x96ec30
(gdb) p sh.sftable
$5 = (Sfio_t **) 0x9b66e0
(gdb) p 0x9b66e0-0x96ec30
$6 = 293552
(gdb) p 293552/17
$7 = 17267
17 is the size of the packed data, and in that space it would fit 17267
entries, so the pointers look quite distant in memory.
"""
On a new crash, 0xa appears again:
Program terminated with signal 11, Segmentation fault.
#0 sfraise (f=0x76ada0, type=14, data=0x0)
at /usr/src/debug/ksh-20120801/src/lib/libast/sfio/sfraise.c:84
84 { next = disc->disc;
(gdb) bt
#0 sfraise (f=0x76ada0, type=14, data=0x0)
at /usr/src/debug/ksh-20120801/src/lib/libast/sfio/sfraise.c:84
#1 0x00000000004c4304 in _sfcleanup ()
at /usr/src/debug/ksh-20120801/src/lib/libast/sfio/sfmode.c:91
#2 0x000000334fe35992 in __run_exit_handlers (status=0) at exit.c:78
#3 exit (status=0) at exit.c:100
#4 0x000000000041aa35 in sh_done (ptr=0x76d160, sig=0)
at /usr/src/debug/ksh-20120801/src/cmd/ksh93/sh/fault.c:671
#5 0x0000000000407dac in sh_main (ac=<value optimized out>,
av=0x7fffffffcb38, userinit=<value optimized out>)
at /usr/src/debug/ksh-20120801/src/cmd/ksh93/sh/main.c:354
#6 0x000000334fe1ed1d in __libc_start_main (main=0x406ae0 <main>, argc=4,
ubp_av=0x7fffffffcb38, init=<value optimized out>,
fini=<value optimized out>, rtld_fini=<value optimized out>,
stack_end=0x7fffffffcb28) at libc-start.c:226
#7 0x0000000000406a19 in _start ()
(gdb) p disc
$1 = (Sfdisc_t *) 0xa000000000000
But the last coredump has plently of other paths to look. This is again a
build using glibc malloc, but also MALLOC_PERTURB_=90 to help in detecting
use after free, and it indeed happens:
(gdb) p *f
$2 = {
next = 0x96efc0 "\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245"...,
endw = 0x96efc0 "\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245\245"...,
---Type <return> to continue, or q <return> to quit---q
endr = 0x96efc0 "\245\245\245\245\245\245\245\245\245\2Quit
(gdb) p/o ~90&0xff
$3 = 0245
So, it is using released stream buffers. Might be some bad realloc logic
somewhere (like a save/restore state not accounting a buffer realloc):
\245"..., size = 65536, val = 65536, extent = -1, here = 0,
unused_1 = 0 '\000', tiny = "", bits = 0, mode = 33, disc = 0x96ee70,
pool = 0x76afe8, rsrv = 0x0, proc = 0x0, mutex = 0x0, stdio = 0x0, lpos = 0,
iosz = 0, blksz = 1024, getr = 0}
(gdb) p *f.disc
$4 = {readf = 0x423990 <piperead>, writef = 0, seekf = 0,
exceptf = 0x423ed0 <slowexcept>, disc = 0xa000000000000}
Since the crash is in an atexit handler, the \245\245 buffers should
be during data release, and/or a side effect of data corruption.
On another crash there is this pattern:
#0 0x00000000000a3ed0 in ?? ()
(gdb) bt
#0 0x00000000000a3ed0 in ?? ()
#1 0x00000000004ca14f in sfsync (f=0x76ada0)
...
(gdb) frame 1
#1 0x00000000004ca14f in sfsync (f=0x76ada0)
at /usr/src/debug/ksh-20120801/src/lib/libast/sfio/sfsync.c:117
117 (void)(*f->disc->exceptf)(f,SF_SYNC,(Void_t*)((int)1),f->disc);
(gdb) p f.disc
$1 = (struct _sfdisc_s *) 0x96ee70
(gdb) p* f.disc
$2 = {readf = 0x423990 <piperead>, writef = 0, seekf = 0, exceptf = 0xa3ed0,
disc = 0x0}
(gdb) x/i 0x423ed0
0x423ed0 <slowexcept>: mov %rbx,-0x20(%rsp)
Note again the 0xa, that replaced by 0x42 (following the pattern of the readf
callback) turns out to be the pointer it should have had:
0xa3ed0
0x423ed0
Created attachment 1345451 [details] ksh-20120801-out-of-bounds.patch This patch corrects this specific problem. Note also that it is kind of a minimal patch for the issue in bug #1259898, that, by treating F_dupfd_cloexec specially, also calls sh_iovalidfd(shp,newfd); if newfd returned by fcntl(F_DUPFD) would have an offset out of bounds in the shell data structures. Comment on attachment 1345451 [details] ksh-20120801-out-of-bounds.patch Looks good to me. Btw. bug #1463312 (out of bound access on the same array at index -1) seems to be also related. LGTM too. Created attachment 1370722 [details]
Validate duplicated fd in iousepipe()
Comment on attachment 1370722 [details] Validate duplicated fd in iousepipe() The condition in attachment #1345451 [details] was in fact unnecessary but harmless. This patch should be functionally equivalent as I understand it. |
107: int iousepipe(Shell_t *shp) 108: { 109: int fd=sffileno(sfstdout),i,err=errno; 110: if(usepipe) 111: { 112: usepipe++; 113: iounpipe(shp); 114: } 115: if(sh_rpipe(subpipe) < 0) 116: return(0); 117: usepipe++; 118: if(shp->comsub!=1) 119: { 120: subpipe[2] = sh_fcntl(subpipe[1],F_DUPFD,10); 121: sh_close(subpipe[1]); 122: return(1); 123: } 124: subpipe[2] = sh_fcntl(fd,F_dupfd_cloexec,10); 125: shp->fdstatus[subpipe[2]] = shp->fdstatus[1]; in line 125 it might write out of bounds if subpipe[2] is set to a value >= sh.gd.lim.open_max This is one possible reason for a crash I am looking at: (gdb) frame 6 #6 0x0000000000424dd2 in sh_close (fd=60) at /usr/src/debug/ksh-20120801/src/cmd/ksh93/sh/io.c:691 691 sh_iovalidfd(shp,fd); (gdb) p shp.gd.lim.open_max $1 = 32 (gdb) frame 7 #7 0x0000000000456cfe in iounpipe (shp=0x76d160) at /usr/src/debug/ksh-20120801/src/cmd/ksh93/sh/xec.c:170 170 sh_close(subpipe[2]); and it for sure did write to random memory when previously called iousepipe. The crash is in a special build using glibc malloc: #0 0x000000334fe32495 in raise (sig=6) at ../nptl/sysdeps/unix/sysv/linux/raise.c:64 #1 0x000000334fe33c75 in abort () at abort.c:92 #2 0x000000334fe703a7 in __libc_message (do_abort=2, fmt=0x334ff58b80 "*** glibc detected *** %s: %s: 0x%s ***\n") at ../sysdeps/unix/sysv/linux/libc_fatal.c:198 #3 0x000000334fe75dee in malloc_printerr (action=3, str=0x334ff58e80 "free(): invalid next size (normal)", ptr=<value optimized out>, ar_ptr=<value optimized out>) at malloc.c:6360 #4 0x000000334fe78c80 in _int_free (av=0x335018e120, p=0x96ec20, have_lock=0) at malloc.c:4846 #5 0x00000000004241ee in sh_iovalidfd (shp=0x76d160, fd=<value optimized out>) at /usr/src/debug/ksh-20120801/src/cmd/ksh93/sh/io.c:435 #6 0x0000000000424dd2 in sh_close (fd=60) at /usr/src/debug/ksh-20120801/src/cmd/ksh93/sh/io.c:691 #7 0x0000000000456cfe in iounpipe (shp=0x76d160) at /usr/src/debug/ksh-20120801/src/cmd/ksh93/sh/xec.c:170 #8 0x00000000004570f6 in _sh_fork (shp=0x76d160, parent=16219, flags=<value optimized out>, jobid=0x7fffffffba28) ...