[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