On 11/30/2016 11:52 PM, Denys Vlasenko wrote:
On Wed, Nov 30, 2016 at 3:46 AM, Daniel Sabogal <dsaboga...@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
#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

Reply via email to