Bug 680281

Summary: off-by-one in virFileAbsPath can lead to memory corruption
Product: Red Hat Enterprise Linux 6 Reporter: Eric Blake <eblake>
Component: libvirtAssignee: Eric Blake <eblake>
Status: CLOSED ERRATA QA Contact: Virtualization Bugs <virt-bugs>
Severity: unspecified Docs Contact:
Priority: unspecified    
Version: 6.1CC: dallan, eblake, gsun, hjiang, mjenner, xen-maint
Target Milestone: rc   
Target Release: ---   
Hardware: Unspecified   
OS: Unspecified   
Whiteboard:
Fixed In Version: libvirt-0.8.7-8.el6 Doc Type: Bug Fix
Doc Text:
Story Points: ---
Clone Of:
: 680289 (view as bug list) Environment:
Last Closed: 2011-05-19 13:28:14 UTC Type: ---
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: 680289    

Description Eric Blake 2011-02-24 22:04:33 UTC
Description of problem:
virFileAbsPath writes past the bounds of a malloc'd array; this can probably be exploited to cause DoS attacks if that overwrite happens to cross a word boundary and corrupt malloc() data structures, although I have not yet tried to find a reproducer test case.

Version-Release number of selected component (if applicable):
libvirt-0.8.7-7.el6
also applies back to upstream 0.7.2, which means this should be cloned to RHEL 5.7 (and if I can come up with a crasher testcase, also to 5.6.z and 6.0.z).

How reproducible:
not sure yet; found by code inspection

Steps to Reproduce:
1.
2.
3.
  
Actual results:


Expected results:


Additional info:
Fixed by upstream patch:

commit 9f5bbe3b926b52c6490346fd3c026546caadaefd
Author: Daniel P. Berrange <berrange>
Date:   Tue Feb 22 17:08:12 2011 +0000

    Fix off-by-1 in virFileAbsPath.
    
    The virFileAbsPath was not taking into account the '/' directory
    separator when allocating memory for combining cwd + path. Convert
    to use virAsprintf to avoid this type of bug completely.
    
    * src/util/util.c: Convert virFileAbsPath to use virAsprintf

Comment 2 Eric Blake 2011-02-24 22:11:06 UTC
related to bug 679164, but the memory bugs in that bug don't extend back to RHEL 5; created this as a separate bug for easier cloning

Comment 6 Eric Blake 2011-02-24 23:28:27 UTC
It turns out that virLogParseOutputs in logging.c is the only client of the faulty virFileAbsPath; this in turn is only called when parsing the environment variable LIBVIRT_LOG_OUTPUTS or the log_outputs line in libvirtd.conf.  The problem only occurs when accessing a problematic log directive of the form <n>:file:relative-path).  A workaround is to always use <n>:file:/absolute-path if it is desired to perform logging via that environment variable or configuration line.  Therefore, I'm debating whether this is severe enough to warrant the hassle of a z-stream fix, or whether it is easy enough to avoid.

Here's a reproducer under valgrind:

$ LIBVIRT_LOG_OUTPUTS='4:file:log' valgrind virsh version
...
==17924== Invalid write of size 1
==17924==    at 0x4A0675F: strcpy (mc_replace_strmem.c:311)
==17924==    by 0x3D6543DE3D: virFileAbsPath (string3.h:107)
==17924==    by 0x3D65431563: virLogParseOutputs (logging.c:768)
==17924==    by 0x3D65472D5A: virInitialize (libvirt.c:337)
==17924==    by 0x3D6547389C: virConnectOpenAuth (libvirt.c:1495)
==17924==    by 0x41B62F: main (virsh.c:10559)
==17924==  Address 0x4c6356f is 0 bytes after a block of size 31 alloc'd

I was not able to quickly find a particular file length of $CWD plus the relative name where the out-of-bounds write would scribble over malloc data (rather than in the allocation padding) to the point of actually causing a crash, but that doesn't rule out the possibility of such a length.

Comment 7 Wayne Sun 2011-02-25 05:26:42 UTC
Verified

build:
RHEL6.1-20110202.n.0

Packages:
libvirt-0.8.7-8.el6.x86_64
valgrind-3.6.0-3.el6.x86_64
qemu-kvm-0.12.1.2-2.143.el6.x86_64
kernel-2.6.32-118.el6.x86_64
libnl-1.1-13.el6.x86_64

Steps:
# LIBVIRT_LOG_OUTPUTS='4:file:log' valgrind virsh version

Output:

LIBVIRT_LOG_OUTPUTS='4:file:log' valgrind virsh version
==10746== Memcheck, a memory error detector
==10746== Copyright (C) 2002-2010, and GNU GPL'd, by Julian Seward et al.
==10746== Using Valgrind-3.6.0 and LibVEX; rerun with -h for copyright info
==10746== Command: virsh version
==10746== 
Compiled against library: libvir 0.8.7
Using library: libvir 0.8.7
Using API: QEMU 0.8.7
Running hypervisor: QEMU 0.12.1

==10746== 
==10746== HEAP SUMMARY:
==10746==     in use at exit: 2,962 bytes in 45 blocks
==10746==   total heap usage: 194 allocs, 149 frees, 1,970,457 bytes allocated
==10746== 
==10746== LEAK SUMMARY:
==10746==    definitely lost: 0 bytes in 0 blocks
==10746==    indirectly lost: 0 bytes in 0 blocks
==10746==      possibly lost: 0 bytes in 0 blocks
==10746==    still reachable: 2,962 bytes in 45 blocks
==10746==         suppressed: 0 bytes in 0 blocks

Comment 10 errata-xmlrpc 2011-05-19 13:28:14 UTC
An advisory has been issued which should help the problem
described in this bug report. This report is therefore being
closed with a resolution of ERRATA. For more information
on therefore solution and/or where to find the updated files,
please follow the link below. You may reopen this bug report
if the solution does not work for you.

http://rhn.redhat.com/errata/RHBA-2011-0596.html

Comment 11 Huming Jiang 2011-06-02 06:49:54 UTC
Could reproduce this bug on the following components of rh5.6:

Packages:
libvirt-0.8.2-15.el5
valgrind-3.5.0-1.el5
kernel-2.6.18-238.el5
libnl-1.0-0.10.pre5.5

Steps:
# LIBVIRT_LOG_OUTPUTS='4:file:log' valgrind virsh version

Outputs:

# LIBVIRT_LOG_OUTPUTS='4:file:log' valgrind virsh version
==6569== Memcheck, a memory error detector
==6569== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al.
==6569== Using Valgrind-3.5.0 and LibVEX; rerun with -h for copyright info
==6569== Command: virsh version
==6569== 
==6569== Invalid write of size 1
==6569==    at 0x3B02CE72BF: __vsnprintf_chk (in /lib64/libc-2.5.so)
==6569==    by 0x3B02CE722A: __snprintf_chk (in /lib64/libc-2.5.so)
==6569==    by 0x3B16C02EC4: numa_init (in /usr/lib64/libnuma.so.1)
==6569==    by 0x3B0280D1BA: call_init (in /lib64/ld-2.5.so)
==6569==    by 0x3B0280D2C4: _dl_init (in /lib64/ld-2.5.so)
==6569==    by 0x3B02800AA9: ??? (in /lib64/ld-2.5.so)
==6569==    by 0x1: ???
==6569==    by 0x7FF000946: ???
==6569==    by 0x7FF00094C: ???
==6569==  Address 0x4c501a0 is 0 bytes after a block of size 0 alloc'd
==6569==    at 0x4A05E1C: malloc (vg_replace_malloc.c:195)
==6569==    by 0x3B16C02D9B: numa_init (in /usr/lib64/libnuma.so.1)
==6569==    by 0x3B0280D1BA: call_init (in /lib64/ld-2.5.so)
==6569==    by 0x3B0280D2C4: _dl_init (in /lib64/ld-2.5.so)
==6569==    by 0x3B02800AA9: ??? (in /lib64/ld-2.5.so)
==6569==    by 0x1: ???
==6569==    by 0x7FF000946: ???
==6569==    by 0x7FF00094C: ???
==6569== 
==6569== Invalid write of size 1
==6569==    at 0x3B02C6E3B1: _IO_default_xsputn (in /lib64/libc-2.5.so)
==6569==    by 0x3B02C43D38: vfprintf (in /lib64/libc-2.5.so)
==6569==    by 0x3B02CE72E7: __vsnprintf_chk (in /lib64/libc-2.5.so)
==6569==    by 0x3B02CE722A: __snprintf_chk (in /lib64/libc-2.5.so)
==6569==    by 0x3B16C02EC4: numa_init (in /usr/lib64/libnuma.so.1)
==6569==    by 0x3B0280D1BA: call_init (in /lib64/ld-2.5.so)
==6569==    by 0x3B0280D2C4: _dl_init (in /lib64/ld-2.5.so)
==6569==    by 0x3B02800AA9: ??? (in /lib64/ld-2.5.so)
==6569==    by 0x1: ???
==6569==    by 0x7FF000946: ???
==6569==    by 0x7FF00094C: ???
==6569==  Address 0x4c501a0 is 0 bytes after a block of size 0 alloc'd
==6569==    at 0x4A05E1C: malloc (vg_replace_malloc.c:195)
==6569==    by 0x3B16C02D9B: numa_init (in /usr/lib64/libnuma.so.1)
==6569==    by 0x3B0280D1BA: call_init (in /lib64/ld-2.5.so)
==6569==    by 0x3B0280D2C4: _dl_init (in /lib64/ld-2.5.so)
==6569==    by 0x3B02800AA9: ??? (in /lib64/ld-2.5.so)
==6569==    by 0x1: ???
==6569==    by 0x7FF000946: ???
==6569==    by 0x7FF00094C: ???
==6569== 
==6569== Invalid write of size 1
==6569==    at 0x3B02CE72FD: __vsnprintf_chk (in /lib64/libc-2.5.so)
==6569==    by 0x3B02CE722A: __snprintf_chk (in /lib64/libc-2.5.so)
==6569==    by 0x3B16C02EC4: numa_init (in /usr/lib64/libnuma.so.1)
==6569==    by 0x3B0280D1BA: call_init (in /lib64/ld-2.5.so)
==6569==    by 0x3B0280D2C4: _dl_init (in /lib64/ld-2.5.so)
==6569==    by 0x3B02800AA9: ??? (in /lib64/ld-2.5.so)
==6569==    by 0x1: ???
==6569==    by 0x7FF000946: ???
==6569==    by 0x7FF00094C: ???
==6569==  Address 0x4c501a1 is 1 bytes after a block of size 0 alloc'd
==6569==    at 0x4A05E1C: malloc (vg_replace_malloc.c:195)
==6569==    by 0x3B16C02D9B: numa_init (in /usr/lib64/libnuma.so.1)
==6569==    by 0x3B0280D1BA: call_init (in /lib64/ld-2.5.so)
==6569==    by 0x3B0280D2C4: _dl_init (in /lib64/ld-2.5.so)
==6569==    by 0x3B02800AA9: ??? (in /lib64/ld-2.5.so)
==6569==    by 0x1: ???
==6569==    by 0x7FF000946: ???
==6569==    by 0x7FF00094C: ???
==6569== 
==6569== Invalid write of size 1
==6569==    at 0x3B02CE72BF: __vsnprintf_chk (in /lib64/libc-2.5.so)
==6569==    by 0x3B02CE722A: __snprintf_chk (in /lib64/libc-2.5.so)
==6569==    by 0x3B0280D1BA: call_init (in /lib64/ld-2.5.so)
==6569==    by 0x3B0280D2C4: _dl_init (in /lib64/ld-2.5.so)
==6569==    by 0x3B02800AA9: ??? (in /lib64/ld-2.5.so)
==6569==    by 0x1: ???
==6569==    by 0x7FF000946: ???
==6569==    by 0x7FF00094C: ???
==6569==  Address 0x4c501a1 is 1 bytes after a block of size 0 alloc'd
==6569==    at 0x4A05E1C: malloc (vg_replace_malloc.c:195)
==6569==    by 0x3B16C02D9B: numa_init (in /usr/lib64/libnuma.so.1)
==6569==    by 0x3B0280D1BA: call_init (in /lib64/ld-2.5.so)
==6569==    by 0x3B0280D2C4: _dl_init (in /lib64/ld-2.5.so)
==6569==    by 0x3B02800AA9: ??? (in /lib64/ld-2.5.so)
==6569==    by 0x1: ???
==6569==    by 0x7FF000946: ???
==6569==    by 0x7FF00094C: ???
==6569== 
==6569== Invalid write of size 1
==6569==    at 0x3B02C43762: vfprintf (in /lib64/libc-2.5.so)
==6569==    by 0x3B02CE72E7: __vsnprintf_chk (in /lib64/libc-2.5.so)
==6569==    by 0x3B02CE722A: __snprintf_chk (in /lib64/libc-2.5.so)
==6569==    by 0x3B0280D1BA: call_init (in /lib64/ld-2.5.so)
==6569==    by 0x3B0280D2C4: _dl_init (in /lib64/ld-2.5.so)
==6569==    by 0x3B02800AA9: ??? (in /lib64/ld-2.5.so)
==6569==    by 0x1: ???
==6569==    by 0x7FF000946: ???
==6569==    by 0x7FF00094C: ???
==6569==  Address 0x4c501a1 is 1 bytes after a block of size 0 alloc'd
==6569==    at 0x4A05E1C: malloc (vg_replace_malloc.c:195)
==6569==    by 0x3B16C02D9B: numa_init (in /usr/lib64/libnuma.so.1)
==6569==    by 0x3B0280D1BA: call_init (in /lib64/ld-2.5.so)
==6569==    by 0x3B0280D2C4: _dl_init (in /lib64/ld-2.5.so)
==6569==    by 0x3B02800AA9: ??? (in /lib64/ld-2.5.so)
==6569==    by 0x1: ???
==6569==    by 0x7FF000946: ???
==6569==    by 0x7FF00094C: ???
==6569== 
==6569== Invalid write of size 1
==6569==    at 0x3B02CE72FD: __vsnprintf_chk (in /lib64/libc-2.5.so)
==6569==    by 0x3B02CE722A: __snprintf_chk (in /lib64/libc-2.5.so)
==6569==    by 0x3B0280D1BA: call_init (in /lib64/ld-2.5.so)
==6569==    by 0x3B0280D2C4: _dl_init (in /lib64/ld-2.5.so)
==6569==    by 0x3B02800AA9: ??? (in /lib64/ld-2.5.so)
==6569==    by 0x1: ???
==6569==    by 0x7FF000946: ???
==6569==    by 0x7FF00094C: ???
==6569==  Address 0x4c501a2 is 2 bytes after a block of size 0 alloc'd
==6569==    at 0x4A05E1C: malloc (vg_replace_malloc.c:195)
==6569==    by 0x3B16C02D9B: numa_init (in /usr/lib64/libnuma.so.1)
==6569==    by 0x3B0280D1BA: call_init (in /lib64/ld-2.5.so)
==6569==    by 0x3B0280D2C4: _dl_init (in /lib64/ld-2.5.so)
==6569==    by 0x3B02800AA9: ??? (in /lib64/ld-2.5.so)
==6569==    by 0x1: ???
==6569==    by 0x7FF000946: ???
==6569==    by 0x7FF00094C: ???
==6569== 
==6569== Invalid write of size 1
==6569==    at 0x4A082E7: strcpy (mc_replace_strmem.c:303)
==6569==    by 0x3B17037DA9: virFileAbsPath (in /usr/lib64/libvirt.so.0.8.2)
==6569==    by 0x3B1702DF27: virLogParseOutputs (in /usr/lib64/libvirt.so.0.8.2)
==6569==    by 0x3B17076D02: virInitialize (in /usr/lib64/libvirt.so.0.8.2)
==6569==    by 0x3B17076E34: virConnectOpenAuth (in /usr/lib64/libvirt.so.0.8.2)
==6569==    by 0x41876A: ??? (in /usr/bin/virsh)
==6569==    by 0x3B02C1D993: (below main) (in /lib64/libc-2.5.so)
==6569==  Address 0x4c53e08 is 0 bytes after a block of size 8 alloc'd
==6569==    at 0x4A05F1D: realloc (vg_replace_malloc.c:476)
==6569==    by 0x3B1702E37E: virReallocN (in /usr/lib64/libvirt.so.0.8.2)
==6569==    by 0x3B17037D87: virFileAbsPath (in /usr/lib64/libvirt.so.0.8.2)
==6569==    by 0x3B1702DF27: virLogParseOutputs (in /usr/lib64/libvirt.so.0.8.2)
==6569==    by 0x3B17076D02: virInitialize (in /usr/lib64/libvirt.so.0.8.2)
==6569==    by 0x3B17076E34: virConnectOpenAuth (in /usr/lib64/libvirt.so.0.8.2)
==6569==    by 0x41876A: ??? (in /usr/bin/virsh)
==6569==    by 0x3B02C1D993: (below main) (in /lib64/libc-2.5.so)
==6569== 
==6569== Syscall param open(filename) points to unaddressable byte(s)
==6569==    at 0x3B0380E210: __open_nocancel (in /lib64/libpthread-2.5.so)
==6569==    by 0x3B1702DF48: virLogParseOutputs (in /usr/lib64/libvirt.so.0.8.2)
==6569==    by 0x3B17076D02: virInitialize (in /usr/lib64/libvirt.so.0.8.2)
==6569==    by 0x3B17076E34: virConnectOpenAuth (in /usr/lib64/libvirt.so.0.8.2)
==6569==    by 0x41876A: ??? (in /usr/bin/virsh)
==6569==    by 0x3B02C1D993: (below main) (in /lib64/libc-2.5.so)
==6569==  Address 0x4c53e08 is 0 bytes after a block of size 8 alloc'd
==6569==    at 0x4A05F1D: realloc (vg_replace_malloc.c:476)
==6569==    by 0x3B1702E37E: virReallocN (in /usr/lib64/libvirt.so.0.8.2)
==6569==    by 0x3B17037D87: virFileAbsPath (in /usr/lib64/libvirt.so.0.8.2)
==6569==    by 0x3B1702DF27: virLogParseOutputs (in /usr/lib64/libvirt.so.0.8.2)
==6569==    by 0x3B17076D02: virInitialize (in /usr/lib64/libvirt.so.0.8.2)
==6569==    by 0x3B17076E34: virConnectOpenAuth (in /usr/lib64/libvirt.so.0.8.2)
==6569==    by 0x41876A: ??? (in /usr/bin/virsh)
==6569==    by 0x3B02C1D993: (below main) (in /lib64/libc-2.5.so)
==6569== 
==6569== Invalid read of size 1
==6569==    at 0x4A06D94: strlen (mc_replace_strmem.c:275)
==6569==    by 0x3B02C798B5: strdup (in /lib64/libc-2.5.so)
==6569==    by 0x3B1702D3B6: virLogDefineOutput (in /usr/lib64/libvirt.so.0.8.2)
==6569==    by 0x3B1702DF76: virLogParseOutputs (in /usr/lib64/libvirt.so.0.8.2)
==6569==    by 0x3B17076D02: virInitialize (in /usr/lib64/libvirt.so.0.8.2)
==6569==    by 0x3B17076E34: virConnectOpenAuth (in /usr/lib64/libvirt.so.0.8.2)
==6569==    by 0x41876A: ??? (in /usr/bin/virsh)
==6569==    by 0x3B02C1D993: (below main) (in /lib64/libc-2.5.so)
==6569==  Address 0x4c53e08 is 0 bytes after a block of size 8 alloc'd
==6569==    at 0x4A05F1D: realloc (vg_replace_malloc.c:476)
==6569==    by 0x3B1702E37E: virReallocN (in /usr/lib64/libvirt.so.0.8.2)
==6569==    by 0x3B17037D87: virFileAbsPath (in /usr/lib64/libvirt.so.0.8.2)
==6569==    by 0x3B1702DF27: virLogParseOutputs (in /usr/lib64/libvirt.so.0.8.2)
==6569==    by 0x3B17076D02: virInitialize (in /usr/lib64/libvirt.so.0.8.2)
==6569==    by 0x3B17076E34: virConnectOpenAuth (in /usr/lib64/libvirt.so.0.8.2)
==6569==    by 0x41876A: ??? (in /usr/bin/virsh)
==6569==    by 0x3B02C1D993: (below main) (in /lib64/libc-2.5.so)
==6569== 
==6569== Invalid read of size 1
==6569==    at 0x4A080C0: memcpy (mc_replace_strmem.c:482)
==6569==    by 0x3B1702D3B6: virLogDefineOutput (in /usr/lib64/libvirt.so.0.8.2)
==6569==    by 0x3B1702DF76: virLogParseOutputs (in /usr/lib64/libvirt.so.0.8.2)
==6569==    by 0x3B17076D02: virInitialize (in /usr/lib64/libvirt.so.0.8.2)
==6569==    by 0x3B17076E34: virConnectOpenAuth (in /usr/lib64/libvirt.so.0.8.2)
==6569==    by 0x41876A: ??? (in /usr/bin/virsh)
==6569==    by 0x3B02C1D993: (below main) (in /lib64/libc-2.5.so)
==6569==  Address 0x4c53e08 is 0 bytes after a block of size 8 alloc'd
==6569==    at 0x4A05F1D: realloc (vg_replace_malloc.c:476)
==6569==    by 0x3B1702E37E: virReallocN (in /usr/lib64/libvirt.so.0.8.2)
==6569==    by 0x3B17037D87: virFileAbsPath (in /usr/lib64/libvirt.so.0.8.2)
==6569==    by 0x3B1702DF27: virLogParseOutputs (in /usr/lib64/libvirt.so.0.8.2)
==6569==    by 0x3B17076D02: virInitialize (in /usr/lib64/libvirt.so.0.8.2)
==6569==    by 0x3B17076E34: virConnectOpenAuth (in /usr/lib64/libvirt.so.0.8.2)
==6569==    by 0x41876A: ??? (in /usr/bin/virsh)
==6569==    by 0x3B02C1D993: (below main) (in /lib64/libc-2.5.so)
==6569== 
error: unable to connect to '/var/run/libvirt/libvirt-sock', libvirtd may need to be started: Connection refused
error: failed to connect to the hypervisor
==6569== 
==6569== HEAP SUMMARY:
==6569==     in use at exit: 21,628 bytes in 386 blocks
==6569==   total heap usage: 651 allocs, 265 frees, 407,408 bytes allocated
==6569== 
==6569== LEAK SUMMARY:
==6569==    definitely lost: 11 bytes in 1 blocks
==6569==    indirectly lost: 0 bytes in 0 blocks
==6569==      possibly lost: 0 bytes in 0 blocks
==6569==    still reachable: 21,617 bytes in 385 blocks
==6569==         suppressed: 0 bytes in 0 blocks
==6569== Rerun with --leak-check=full to see details of leaked memory
==6569== 
==6569== For counts of detected and suppressed errors, rerun with: -v
==6569== ERROR SUMMARY: 10 errors from 10 contexts (suppressed: 4 from 4)