[prev in list] [next in list] [prev in thread] [next in thread] 

List:       busybox
Subject:    Re: segfault bb_make_directory + dirname with musl
From:       Tito <farmatito () tiscali ! it>
Date:       2016-12-06 21:32:04
Message-ID: 79551539-e83e-aead-1caf-951ac10968cb () tiscali ! it
[Download RAW message or body]

On 11/30/2016 11:52 PM, Denys Vlasenko wrote:
> On Wed, Nov 30, 2016 at 3:46 AM, Daniel Sabogal <dsabogalcc@gmail.com> wrote:
>> The following commands cause busybox to segfault on musl-based systems.
>>
>> $ install -D a /
>> $ install -D a /b
>> $ install -D a /b/
>>
>> This happens because the code in
>>
>> https://git.busybox.net/busybox/tree/coreutils/install.c?h=1_25_1#n196
>>
>> passes the result of dirname() to bb_make_directory() which modifies its
>> contents. For paths of the above forms, musl's dirname returns a string
>> literal "/" which shouldn't be modified.
>>
>> See http://git.musl-libc.org/cgit/musl/tree/src/misc/dirname.c
>>
>> There are a few other occurrences of the code shown above, but I've not
>> checked to see if they could be made to segfault.
>
> Does this fix the problem?
>
>                         /* Bypass leading non-'/'s and then subsequent '/'s */
>                         while (*s) {
>                                 if (*s == '/') {
>                                         do {
>                                                 ++s;
>                                         } while (*s == '/');
>                                         c = *s; /* Save the current char */
> ====added line==>                       if (c)
>                                                 *s = '\0'; /* and
> replace it with nul */
>                                         break;
> _______________________________________________
Hi,
don't know if this could be useful, but reading this thread inspired
me to write dirname and basename replacement functions for busybox
that return a malloced string that could be modified by the caller.
The functions seem to work nice as standalone program and both
pass the tests as in the man 3 basename examples and a few more
added by me.

/*  Man 3 BASENAME examples (taken from SUSv2)
     path       dirname   basename
        /usr/lib   /usr      lib
        /usr/      /         usr
        usr        .         usr
        /          /         /
        .          .         .
        ..         .         ..
      Added examples:
        usr/lib   usr       lib
        usr/lib/  usr       lib
        usr/       .        usr
        /usr       .        usr
        /a/b/c     /a/b      c
        /a/b/c/    /a/b      c
        a/b/c      a/b       c
        a/b/c/     a/b       c
        //         /         /
        ///        /         /
        ////       /         /
        '/a/b/ '   /a/b     ' '
*/

The functions look like this:

char* bb_basename_malloc(const char *name)
{
	const char *p1 = NULL;
	const char *p2 = NULL;
	const char *s = name;
	char *last = last_char_is(s, '/');

	while (*s) {
		if (*s == '/') {
			p2 = p1;
			p1 = s;
		}
		s++;
	}
	if (last) {
		if(p2)
			name = p2 + 1;
	} else if (p1) {
		name = p1 + 1;
	}
	return xstrndup(name, strlen(name) - (last && last != name));
}

char* bb_dirname_malloc(const char *name)
{
	int len = 0;
	const char *p1 = NULL;
	const char *p2 = NULL;
	const char *s = name;
	
	while (*s) {
		if (*s == '/' && ((s[1] && s[1] != '/') || s == name)) {
			p2 = p1;
			p1 = s;
		}
		s++;
	}
	if (!p2 && !p1)
		return xstrdup(".");
	if (p1 == name && !p2)
		return xstrdup("/");
	if (p1)
		len  =  strlen(p1);
	return xstrndup(name, strlen(name) - len);
}

Attached you will find the standalone version to test the functions.
Hints, critics, improvements are welcome.

Ciao,
Tito

["bb_dirbasename_malloc.c" (text/x-csrc)]

#include <string.h>
#include <stdlib.h>
#include <stdio.h>

char* xstrndup(const char *s, int n)
{
	int m;
	char *t;

//	if (ENABLE_DEBUG && s == NULL)
//		bb_error_msg_and_die("xstrndup bug");

	/* We can just xmalloc(n+1) and strncpy into it, */
	/* but think about xstrndup("abc", 10000) wastage! */
	m = n;
	t = (char*) s;
	while (m) {
		if (!*t) break;
		m--;
		t++;
	}
	n -= m;
	t = malloc(n + 1);
	t[n] = '\0';

	return memcpy(t, s, n);
}

char* xstrdup(const char *s)
{
	char *t;

	if (s == NULL)
		return NULL;

	t = strdup(s);

	if (t == NULL)
		exit(1);

	return t;
}

char* last_char_is(const char *s, int c)
{
	if (s && *s) {
		size_t sz = strlen(s) - 1;
		s += sz;
		if ( (unsigned char)*s == c)
			return (char*)s;
	}
	return NULL;
}

char* bb_basename_malloc(const char *name)
{
	const char *p1 = NULL;
	const char *p2 = NULL;
	const char *s = name; 
	char *last = last_char_is(s, '/');

	while (*s) {
		if (*s == '/') {
			p2 = p1;
			p1 = s;
		}
		s++;
	}
	if (last) {
		if(p2)
			name = p2 + 1;
	} else if (p1) {
		name = p1 + 1;
	}
	return xstrndup(name, strlen(name) - (last && last != name));
}

char* bb_dirname_malloc(const char *name)
{
	int len = 0;
	const char *p1 = NULL;
	const char *p2 = NULL;
	const char *s = name;
	
	while (*s) {
		if (*s == '/' && ((s[1] && s[1] != '/') || s == name)) {
			p2 = p1;
			p1 = s;
		}
		s++;
	}
	if (!p2 && !p1)
		return xstrdup(".");
	if (p1 == name && !p2)
		return xstrdup("/");
	if (p1)
		len  =  strlen(p1);
	return xstrndup(name, strlen(name) - len);
}

/*  Man 3 BASENAME examples (taken from SUSv2)
    path       dirname   basename
       /usr/lib   /usr      lib
       /usr/      /         usr
       usr        .         usr
       /          /         /
       .          .         .
       ..         .         ..
     Added examples:
       usr/lib   usr       lib
       usr/lib/  usr       lib
       usr/       .        usr
       /usr       .        usr
       /a/b/c     /a/b      c
       /a/b/c/    /a/b      c
       a/b/c      a/b       c
       a/b/c/     a/b       c
       //         /         /
       ///        /         /
       ////       /         /
       '/a/b/ '   /a/b     ' '
*/

int main(int argc, char ** argv)
{
	char *dname = bb_dirname_malloc( "/usr/lib");
	char *bname = bb_basename_malloc("/usr/lib");
	printf("test 1 dirname  %s\texpected %s\tresult %s = %s\n",   "/usr/lib" , "/usr", \
dname, (strcmp(dname, "/usr") == 0) ? "PASSED" : "FAILED");  printf("test 1 basename \
%s\texpected %s\tresult %s = %s\n\n", "/usr/lib" , "lib" , bname, (strcmp(bname, \
"lib")  == 0) ? "PASSED" : "FAILED");  free(dname);
	free(bname);

	dname = bb_dirname_malloc("/usr/");
	bname = bb_basename_malloc("/usr/");
	printf("test 2 dirname  %s\t\texpected %s\tresult %s = %s\n",   "/usr/" , "/"      , \
dname, (strcmp(dname, "/")   == 0) ? "PASSED" : "FAILED");  printf("test 2 basename \
%s\t\texpected %s\tresult %s = %s\n\n", "/usr/" , "usr"    , bname, (strcmp(bname, \
"usr") == 0) ? "PASSED" : "FAILED");  free(dname);
	free(bname);
	dname = bb_dirname_malloc("usr");
	bname = bb_basename_malloc("usr");
	printf("test 3 dirname  %s\t\texpected %s\tresult %s = %s\n",   "usr", "."         , \
dname, (strcmp(dname, ".")   == 0) ? "PASSED" : "FAILED");  printf("test 3 basename \
%s\t\texpected %s\tresult %s = %s\n\n", "usr", "usr"       , bname, (strcmp(bname, \
"usr") == 0) ? "PASSED" : "FAILED");  free(dname);
	free(bname);
	dname = bb_dirname_malloc("/");
	bname = bb_basename_malloc("/");
	printf("test 4 dirname  %s\t\texpected %s\tresult %s = %s\n",   "/", "/", dname, \
(strcmp(dname, "/") == 0) ? "PASSED" : "FAILED");  printf("test 4 basename \
%s\t\texpected %s\tresult %s = %s\n\n", "/", "/" ,bname, (strcmp(bname, "/") == 0) ? \
"PASSED" : "FAILED");  free(dname);
	free(bname);
	dname = bb_dirname_malloc(".");
	bname = bb_basename_malloc(".");
	printf("test 5 dirname  %s\t\texpected %s\tresult %s = %s\n",   ".", ".", dname, \
(strcmp(dname, ".") == 0) ? "PASSED" : "FAILED");  printf("test 5 basename \
%s\t\texpected %s\tresult %s = %s\n\n", ".", ".", bname, (strcmp(bname, ".") == 0) ? \
"PASSED" : "FAILED");  free(dname);
	free(bname);
	dname = bb_dirname_malloc("..");
	bname = bb_basename_malloc("..");
	printf("test 6 dirname  %s\t\texpected %s\tresult %s = %s\n",   "..", ".",  dname, \
(strcmp(dname, ".")  == 0) ? "PASSED" : "FAILED");  printf("test 6 basename \
%s\t\texpected %s\tresult %s = %s\n\n", "..", "..", bname, (strcmp(bname, "..") == 0) \
? "PASSED" : "FAILED");  free(dname);
	free(bname);
	dname = bb_dirname_malloc("usr/lib");
	bname = bb_basename_malloc("usr/lib");
	printf("test 7 dirname  %s\t\texpected %s\tresult %s = %s\n",   "usr/lib", "usr", \
dname, (strcmp(dname, "usr")  == 0) ? "PASSED" : "FAILED");  printf("test 7 basename \
%s\t\texpected %s\tresult %s = %s\n\n", "usr/lib", "lib", bname, (strcmp(bname, \
"lib")  == 0) ? "PASSED" : "FAILED");  free(dname);
	free(bname);
	dname = bb_dirname_malloc("usr/lib/");
	bname = bb_basename_malloc("usr/lib/");
	printf("test 8 dirname  %s\texpected %s\tresult %s = %s\n",   "usr/lib/", "usr", \
dname, (strcmp(dname, "usr")  == 0) ? "PASSED" : "FAILED");  printf("test 8 basename \
%s\texpected %s\tresult %s = %s\n\n", "usr/lib/", "lib", bname, (strcmp(bname, "lib") \
== 0) ? "PASSED" : "FAILED");  free(dname);
	free(bname);
	dname = bb_dirname_malloc("");
	bname = bb_basename_malloc("");
	printf("test 9 dirname  %s\t\texpected %s\tresult %s = %s\n",   "", ".", dname, \
(strcmp(dname, ".")  == 0) ? "PASSED" : "FAILED");  printf("test 9 basename \
%s\t\texpected %s\tresult %s = %s\n\n", "", "", bname, (strcmp(bname, "")  == 0) ? \
"PASSED" : "FAILED");  free(dname);
	free(bname);
	dname = bb_dirname_malloc("usr/");
	bname = bb_basename_malloc("usr/");
	printf("test10 dirname  %s\t\texpected %s\tresult %s = %s\n",   "usr/", ".", dname, \
(strcmp(dname, ".")  == 0) ? "PASSED" : "FAILED");  printf("test10 basename \
%s\t\texpected %s\tresult %s = %s\n\n", "usr/", "usr", bname, (strcmp(bname, "usr")  \
== 0) ? "PASSED" : "FAILED");  free(dname);
	free(bname);
	dname = bb_dirname_malloc("/usr");
	bname = bb_basename_malloc("/usr");
	printf("test11 dirname  %s\t\texpected %s\tresult %s = %s\n",   "/usr", "/", dname, \
(strcmp(dname, "/")  == 0) ? "PASSED" : "FAILED");  printf("test11 basename \
%s\t\texpected %s\tresult %s = %s\n\n", "/usr", "usr", bname, (strcmp(bname, "usr")  \
== 0) ? "PASSED" : "FAILED");  free(dname);
	free(bname);
	dname = bb_dirname_malloc("/a/b/c");
	bname = bb_basename_malloc("/a/b/c");
	printf("test12 dirname  %s\texpected %s result %s = %s\n",   "/a/b/c", "/a/b", \
dname, (strcmp(dname, "/a/b")  == 0) ? "PASSED" : "FAILED");  printf("test12 basename \
%s\texpected %s result %s = %s\n\n", "/a/b/b", "c", bname, (strcmp(bname, "c")  == 0) \
? "PASSED" : "FAILED");  free(dname);
	free(bname);
	dname = bb_dirname_malloc("/usr/local/bin");
	bname = bb_basename_malloc("/usr/local/bin");
	printf("test13 dirname  %s\texpected %s result %s = %s\n",   "/usr/local/bin", \
"/usr/local", dname, (strcmp(dname, "/usr/local")  == 0) ? "PASSED" : "FAILED");  \
printf("test13 basename %s\texpected %s result %s = %s\n\n", "/usr/local/bin", "bin", \
bname, (strcmp(bname, "bin")  == 0) ? "PASSED" : "FAILED");  free(dname);
	free(bname);
	dname = bb_dirname_malloc("/usr/local/bin/");
	bname = bb_basename_malloc("/usr/local/bin/");
	printf("test14 dirname  %s\texpected %s result %s = %s\n",   "/usr/local/bin/", \
"/usr/local", dname, (strcmp(dname, "/usr/local")  == 0) ? "PASSED" : "FAILED");  \
printf("test14 basename %s\texpected %s result %s = %s\n\n", "/usr/local/bin/", \
"bin", bname, (strcmp(bname, "bin")  == 0) ? "PASSED" : "FAILED");  free(dname);
	free(bname);
	dname = bb_dirname_malloc("usr/local/bin");
	bname = bb_basename_malloc("usr/local/bin");
	printf("test15 dirname  %s\texpected %s result %s = %s\n",   "usr/local/bin", \
"usr/local", dname, (strcmp(dname, "usr/local")  == 0) ? "PASSED" : "FAILED");  \
printf("test15 basename %s\texpected %s result %s = %s\n\n", "usr/local/bin", "bin", \
bname, (strcmp(bname, "bin")  == 0) ? "PASSED" : "FAILED");  free(dname);
	free(bname);
	dname = bb_dirname_malloc("usr/local/bin/");
	bname = bb_basename_malloc("usr/local/bin/");
	printf("test16 dirname  %s\texpected %s result %s = %s\n",   "usr/local/bin", \
"usr/local", dname, (strcmp(dname, "usr/local")  == 0) ? "PASSED" : "FAILED");  \
printf("test16 basename %s\texpected %s result %s = %s\n\n", "usr/local/bin", "bin", \
bname, (strcmp(bname, "bin")  == 0) ? "PASSED" : "FAILED");  free(dname);
	free(bname);
	dname = bb_dirname_malloc("//");
	bname = bb_basename_malloc("//");
	printf("test17 dirname  %s\texpected %s result %s = %s\n",   "//", "/", dname, \
(strcmp(dname, "/")  == 0) ? "PASSED" : "FAILED");  printf("test17 basename \
%s\texpected %s result %s = %s\n\n", "//", "/", bname, (strcmp(bname, "/")  == 0) ? \
"PASSED" : "FAILED");  free(dname);
	free(bname);
	dname = bb_dirname_malloc("///");
	bname = bb_basename_malloc("///");
	printf("test18 dirname  %s\texpected %s result %s = %s\n",   "///", "/", dname, \
(strcmp(dname, "/")  == 0) ? "PASSED" : "FAILED");  printf("test18 basename \
%s\texpected %s result %s = %s\n\n", "///", "/", bname, (strcmp(bname, "/")  == 0) ? \
"PASSED" : "FAILED");  free(dname);
	free(bname);
	dname = bb_dirname_malloc("////");
	bname = bb_basename_malloc("////");
	printf("test19 dirname  %s\texpected %s result %s = %s\n",   "////", "/", dname, \
(strcmp(dname, "/")  == 0) ? "PASSED" : "FAILED");  printf("test19 basename \
%s\texpected %s result %s = %s\n\n", "////", "/", bname, (strcmp(bname, "/")  == 0) ? \
"PASSED" : "FAILED");  free(dname);
	free(bname);
	dname = bb_dirname_malloc("/a/b/ ");
	bname = bb_basename_malloc("/a/b/ ");
	printf("test20 dirname  %s\texpected %s result %s = %s\n",   "/a/b/ ", "/a/b", \
dname, (strcmp(dname, "/a/b")  == 0) ? "PASSED" : "FAILED");  printf("test20 basename \
%s\texpected %s result %s = %s\n\n", "/a/b/ ", " ", bname, (strcmp(bname, " ")  == 0) \
? "PASSED" : "FAILED");  free(dname);
	free(bname);
	return 0;
}



_______________________________________________
busybox mailing list
busybox@busybox.net
http://lists.busybox.net/mailman/listinfo/busybox

[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic