Package: libxdg-basedir1
Version: 1.1.1-2
Severity: critical
File: libxdg-basedir
Tags: patch upstream

Dear Maintainer,

Any application using xdgDataHome, xdgConfigHome and possibly others will trigger invalid reads and writes in valgrind. For example the following code:
        const char *xdg_data_home = xdgDataHome(NULL);
        printf("%s\n", xdg_data_home);

It triggers:
==14808== Invalid write of size 1
==14808==    at 0x4C2D97A: memcpy@GLIBC_2.2.5 (mc_replace_strmem.c:881)
==14808==    by 0x4E352A0: xdgGetRelativeHome (basedir.c:577)
==14808==    by 0x4006A5: main (in /tmp/a.out)
==14808==  Address 0x53e305b is 0 bytes after a block of size 27 alloc'd
==14808==    at 0x4C294A0: malloc (vg_replace_malloc.c:291)
==14808==    by 0x4E35278: xdgGetRelativeHome (basedir.c:575)
==14808==    by 0x4006A5: main (in /tmp/a.out)
==14808==
==14808== Invalid read of size 1
==14808==    at 0x4C2C954: __GI_strlen (mc_replace_strmem.c:405)
==14808==    by 0x50A42DB: puts (ioputs.c:36)
==14808==    by 0x4006B5: main (in /tmp/a.out)
==14808==  Address 0x53e305b is 0 bytes after a block of size 27 alloc'd
==14808==    at 0x4C294A0: malloc (vg_replace_malloc.c:291)
==14808==    by 0x4E35278: xdgGetRelativeHome (basedir.c:575)
==14808==    by 0x4006A5: main (in /tmp/a.out)
==14808==

The solution is simple: add a +1 to the malloc in line 575 of basedir.c.
This as it uses the +1 on line 577 when limiting the amount of data that is to be copied.

Attached are:
 * a full test case
 * full result of valgrind before patching the library
 * the patch for the library
 * full result of valgrind after patching the library

I'm in doubt whether this is really this critical or not; I could imagine ways that, under certain circumstances, it is possible to crash users of this library. Especially when this allocation happens at the end of a page, and the next page isn't valid. Then writing the last byte/character could trigger a segmentation fault. Furthermore, the last byte could corrupt the memory management pointers at the begin of allocations causing malloc and/or free to crash, as well as malloc/free overwriting the last byte and the string then becoming longer due to the missing '\0' termination causing applications to crash when e.g. strdup allocates the memory based on strlen before allocation, then allocates and corrupts the string, and finally memcpy-ing but not '\0' terminating the duplicated string. These are all hypothetical though.

Regards,
Remko Bijker

-- System Information:
Debian Release: jessie/sid
  APT prefers unstable
  APT policy: (500, 'unstable'), (1, 'experimental')
Architecture: amd64 (x86_64)

Kernel: Linux 3.11-2-amd64 (SMP w/4 CPU cores)
Locale: LANG=en_GB.UTF-8, LC_CTYPE=en_GB.UTF-8 (charmap=UTF-8)
Shell: /bin/sh linked to /bin/dash

Versions of packages libxdg-basedir1 depends on:
ii  libc6  2.17-96

libxdg-basedir1 recommends no packages.

libxdg-basedir1 suggests no packages.

-- no debconf information
==14838== Memcheck, a memory error detector
==14838== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==14838== Using Valgrind-3.9.0 and LibVEX; rerun with -h for copyright info
==14838== Command: ./a.out
==14838== 
/home/rubidium/.local/share
==14838== 
==14838== HEAP SUMMARY:
==14838==     in use at exit: 0 bytes in 0 blocks
==14838==   total heap usage: 1 allocs, 1 frees, 28 bytes allocated
==14838== 
==14838== All heap blocks were freed -- no leaks are possible
==14838== 
==14838== For counts of detected and suppressed errors, rerun with: -v
==14838== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)
==14808== Memcheck, a memory error detector
==14808== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==14808== Using Valgrind-3.9.0 and LibVEX; rerun with -h for copyright info
==14808== Command: ./a.out
==14808== 
==14808== Invalid write of size 1
==14808==    at 0x4C2D97A: memcpy@GLIBC_2.2.5 (mc_replace_strmem.c:881)
==14808==    by 0x4E352A0: xdgGetRelativeHome (basedir.c:577)
==14808==    by 0x4006A5: main (in /tmp/a.out)
==14808==  Address 0x53e305b is 0 bytes after a block of size 27 alloc'd
==14808==    at 0x4C294A0: malloc (vg_replace_malloc.c:291)
==14808==    by 0x4E35278: xdgGetRelativeHome (basedir.c:575)
==14808==    by 0x4006A5: main (in /tmp/a.out)
==14808== 
==14808== Invalid read of size 1
==14808==    at 0x4C2C954: __GI_strlen (mc_replace_strmem.c:405)
==14808==    by 0x50A42DB: puts (ioputs.c:36)
==14808==    by 0x4006B5: main (in /tmp/a.out)
==14808==  Address 0x53e305b is 0 bytes after a block of size 27 alloc'd
==14808==    at 0x4C294A0: malloc (vg_replace_malloc.c:291)
==14808==    by 0x4E35278: xdgGetRelativeHome (basedir.c:575)
==14808==    by 0x4006A5: main (in /tmp/a.out)
==14808== 
/home/rubidium/.local/share
==14808== 
==14808== HEAP SUMMARY:
==14808==     in use at exit: 0 bytes in 0 blocks
==14808==   total heap usage: 1 allocs, 1 frees, 27 bytes allocated
==14808== 
==14808== All heap blocks were freed -- no leaks are possible
==14808== 
==14808== For counts of detected and suppressed errors, rerun with: -v
==14808== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 2 from 2)
diff -Nupr libxdg-basedir-1.1.1.orig/src/basedir.c libxdg-basedir-1.1.1/src/basedir.c
--- libxdg-basedir-1.1.1.orig/src/basedir.c	2013-11-25 16:07:09.864380760 +0100
+++ libxdg-basedir-1.1.1/src/basedir.c	2013-11-25 16:09:07.452378162 +0100
@@ -572,7 +572,7 @@ static char * xdgGetRelativeHome(const c
 		unsigned int homelen;
 		if (!(home = xdgGetEnv("HOME")))
 			return NULL;
-		if (!(relhome = (char*)malloc((homelen = strlen(home))+fallbacklength))) return NULL;
+		if (!(relhome = (char*)malloc((homelen = strlen(home))+fallbacklength+1))) return NULL;
 		memcpy(relhome, home, homelen);
 		memcpy(relhome+homelen, relativefallback, fallbacklength+1);
 	}
#include <stdlib.h>
#include <stdio.h>
#include <basedir.h>

int main(int argc, char *argv[]) 
{
	const char *xdg_data_home = xdgDataHome(NULL);
	printf("%s\n", xdg_data_home);
	free((void*)xdg_data_home);

	return 0;
}

Reply via email to