Hi,
while inspecting the last changes in busybox code I hit some issues in
libbb/strrstr.c. The strrtstr function fails on some corner cases of a little
test program I wrote (and bombs out on NULL pointers):

char* strrstr(const char *haystack, const char *needle)
{
        char *tmp = strrchr(haystack, *needle);
        if (tmp == NULL || strcmp(tmp, needle) != 0)
                return NULL;
        return tmp;
}


./test
'baaabaaab'  vs. 'aaa'       : FAILED
'baaabaaaab' vs. 'aaa'       : FAILED
'baaabaab'   vs. 'aaa'       : FAILED
'aaa'        vs. 'aaa'       : FAILED
'aaa'        vs. 'a'         : PASSED
'aaa'        vs. 'bbb'       : PASSED
'a'          vs. 'aaa'       : PASSED
'aaa'        vs. ''          : FAILED
''           vs. 'aaa'       : PASSED
''           vs. ''          : FAILED
Segmentation fault

I've tried to write a more robust variant:

/*
 * The strrstr() function finds the last occurrence of the nul terminated
 * substring needle in the nul terminated string haystack. The terminating
 * nul characters  or NULL pointers are  not compared.
 */

char* strrstr(const char *haystack, const char *needle)
{
        char *p = (char *)haystack;
        char *s1;
        char *s2;

        if (!haystack || !needle)
                return NULL;

        while (*p++)
                /* VOID */;

        p--;
        
        while (p-- != haystack) {
                s1 = p;
                s2 = (char *)needle;
                while (*s1 && *s1++ == *s2++) {
                        if (!*s2)
                                return p;
                }
        }
        return NULL;
}

This function passes all of the test cases I could think about:

./test
'baaabaaab'  vs. 'aaa'       : PASSED
'baaabaaaab' vs. 'aaa'       : PASSED
'baaabaab'   vs. 'aaa'       : PASSED
'aaa'        vs. 'aaa'       : PASSED
'aaa'        vs. 'a'         : PASSED
'aaa'        vs. 'bbb'       : PASSED
'a'          vs. 'aaa'       : PASSED
'aaa'        vs. ''          : PASSED
''           vs. 'aaa'       : PASSED
''           vs. ''          : PASSED
'NULL'       vs. 'NULL'      : PASSED
''           vs. 'NULL'      : PASSED
'NULL'       vs. ''          : PASSED
'aaa'        vs. 'NULL'      : PASSED
'NULL'       vs. 'aaa'       : PASSED

For the moment there is no diff but just a drop in replacement
for strrstr.c for testing and improvement by the list members.
There is a little size increase:
scripts/bloat-o-meter busybox_old busybox_unstripped
function                                             old     new   delta
strrstr                                               53      73     +20
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 1/0 up/down: 20/0)               Total: 20 bytes

The strrstr function at the moment is used only in depmod 
so the depmod author should check if this new implementation
is suitable to be used.

Hints, critics and improvements are ,as always, welcome.


Ciao,
Tito

/* vi: set sw=4 ts=4: */
/*
 * Utility routines.
 *
 * Copyright (C) 2008 Bernhard Fischer
 * Copyright (C) 2008 Tito Ragusa
 *
 * Licensed under GPLv2 or later, see file License in this tarball for details.
 */

#include "libbb.h"

/*
 * The strrstr() function finds the last occurrence of the nul terminated
 * substring needle in the nul terminated string haystack. The terminating
 * nul characters  or NULL pointers are  not compared.
 */

char* strrstr(const char *haystack, const char *needle)
{
	char *p = (char *)haystack;
	char *s1;
	char *s2;

	if (!haystack || !needle)
		return NULL;

	while (*p++)
		/* VOID */;

	p--;
	
	while (p-- != haystack) {
		s1 = p;
		s2 = (char *)needle;
		while (*s1 && *s1++ == *s2++) {
			if (!*s2)
				return p;
		}
	}
	return NULL;
}

#ifdef STRRSTR_TEST
int main(int argc, char **argv)
{
	int ret = 0;
	int n;
	char *tmp;

	
	ret |= !(n = ((tmp = strrstr("baaabaaab", "aaa")) != NULL && strcmp(tmp, "aaab") == 0));
	printf("'baaabaaab'  vs. 'aaa'       : %s\n", n ? "PASSED" : "FAILED");
	ret |= !(n = ((tmp = strrstr("baaabaaaab", "aaa")) != NULL && strcmp(tmp, "aaab") == 0));
	printf("'baaabaaaab' vs. 'aaa'       : %s\n", n ? "PASSED" : "FAILED");
	ret |= !(n = ((tmp = strrstr("baaabaab", "aaa")) != NULL && strcmp(tmp, "aaabaab") == 0));
	printf("'baaabaab'   vs. 'aaa'       : %s\n", n ? "PASSED" : "FAILED");
	ret |= !(n = (strrstr("aaa", "aaa") != NULL));
	printf("'aaa'        vs. 'aaa'       : %s\n", n ? "PASSED" : "FAILED");
	ret |= !(n = (strrstr("aaa", "a") != NULL));
	printf("'aaa'        vs. 'a'         : %s\n", n ? "PASSED" : "FAILED");
	ret |= !(n = (strrstr("aaa", "bbb") == NULL));
	printf("'aaa'        vs. 'bbb'       : %s\n", n ? "PASSED" : "FAILED");
	ret |= !(n = (strrstr("a", "aaa") == NULL));
	printf("'a'          vs. 'aaa'       : %s\n", n ? "PASSED" : "FAILED");
	ret |= !(n = (strrstr("aaa", "") == NULL));
	printf("'aaa'        vs. ''          : %s\n", n ? "PASSED" : "FAILED");
	ret |= !(n = (strrstr("", "aaa") == NULL));
	printf("''           vs. 'aaa'       : %s\n", n ? "PASSED" : "FAILED");
	ret |= !(n = (strrstr("", "") == NULL));
	printf("''           vs. ''          : %s\n", n ? "PASSED" : "FAILED");
	ret |= !(n = (strrstr(NULL, NULL) == NULL));
	printf("'NULL'       vs. 'NULL'      : %s\n", n ? "PASSED" : "FAILED");
	ret |= !(n = (strrstr("", NULL) == NULL));
	printf("''           vs. 'NULL'      : %s\n", n ? "PASSED" : "FAILED");
	ret |= !(n = (strrstr(NULL, "") == NULL));
	printf("'NULL'       vs. ''          : %s\n", n ? "PASSED" : "FAILED");
	ret |= !(n = (strrstr("aaa", NULL) == NULL));
	printf("'aaa'        vs. 'NULL'      : %s\n", n ? "PASSED" : "FAILED");
	ret |= !(n = (strrstr(NULL, "aaa") == NULL));
	printf("'NULL'       vs. 'aaa'       : %s\n", n ? "PASSED" : "FAILED");

	return ret;
}
#endif
_______________________________________________
busybox mailing list
busybox@busybox.net
http://busybox.net/cgi-bin/mailman/listinfo/busybox

Reply via email to