Changeset View
Changeset View
Standalone View
Standalone View
lib/libc/tests/resolv/resolv_test.c
Show All 28 Lines | |||||
* POSSIBILITY OF SUCH DAMAGE. | * POSSIBILITY OF SUCH DAMAGE. | ||||
*/ | */ | ||||
/* $FreeBSD$ */ | /* $FreeBSD$ */ | ||||
#include <sys/cdefs.h> | #include <sys/cdefs.h> | ||||
__RCSID("$NetBSD: resolv.c,v 1.6 2004/05/23 16:59:11 christos Exp $"); | __RCSID("$NetBSD: resolv.c,v 1.6 2004/05/23 16:59:11 christos Exp $"); | ||||
#include <sys/types.h> | #include <sys/types.h> | ||||
#include <sys/socket.h> | #include <sys/socket.h> | ||||
#include <assert.h> | |||||
#include <errno.h> | |||||
#include <pthread.h> | #include <pthread.h> | ||||
#include <stdio.h> | #include <stdio.h> | ||||
#include <netdb.h> | #include <netdb.h> | ||||
#include <stdlib.h> | #include <stdlib.h> | ||||
#include <unistd.h> | #include <unistd.h> | ||||
#include <string.h> | #include <string.h> | ||||
#include <stringlist.h> | #include <stringlist.h> | ||||
#include <atf-c.h> | #include <atf-c.h> | ||||
#define NTHREADS 10 | #define NTHREADS 10 | ||||
#define NHOSTS 100 | #define NHOSTS 100 | ||||
#define WS " \t\n\r" | #define WS " \t\n\r" | ||||
enum method { | enum method { | ||||
METHOD_GETADDRINFO, | METHOD_GETADDRINFO, | ||||
METHOD_GETHOSTBY, | METHOD_GETHOSTBY, | ||||
METHOD_GETIPNODEBY | METHOD_GETIPNODEBY | ||||
}; | }; | ||||
static StringList *hosts = NULL; | static StringList *hosts = NULL; | ||||
static enum method method = METHOD_GETADDRINFO; | |||||
static int *ask = NULL; | static int *ask = NULL; | ||||
static int *got = NULL; | static int *got = NULL; | ||||
static void load(const char *); | static void load(const char *); | ||||
static void resolvone(int); | static void resolvone(int, enum method); | ||||
static void *resolvloop(void *); | static void *resolvloop(void *); | ||||
static void run(int *); | static void run(int *, enum method); | ||||
static pthread_mutex_t stats = PTHREAD_MUTEX_INITIALIZER; | static pthread_mutex_t stats = PTHREAD_MUTEX_INITIALIZER; | ||||
static void | static void | ||||
load(const char *fname) | load(const char *fname) | ||||
{ | { | ||||
FILE *fp; | FILE *fp; | ||||
size_t len; | size_t len; | ||||
char *line; | char *line; | ||||
if ((fp = fopen(fname, "r")) == NULL) | if ((fp = fopen(fname, "r")) == NULL) | ||||
ATF_REQUIRE(fp != NULL); | ATF_REQUIRE(fp != NULL); | ||||
while ((line = fgetln(fp, &len)) != NULL) { | while ((line = fgetln(fp, &len)) != NULL) { | ||||
char c = line[len]; | char c = line[len]; | ||||
char *ptr; | char *ptr; | ||||
line[len] = '\0'; | line[len] = '\0'; | ||||
for (ptr = strtok(line, WS); ptr; ptr = strtok(NULL, WS)) { | for (ptr = strtok(line, WS); ptr; ptr = strtok(NULL, WS)) { | ||||
if (ptr == '\0' || ptr[0] == '#') | if (ptr[0] == '\0' || ptr[0] == '#') | ||||
pstef: I think this was checking for a null pointer, in which case you risk a null pointer dereference… | |||||
kevansAuthorUnsubmitted Done Inline Actionsptr is already checked for null as part of the loop invariant here. kevans: ptr is already checked for null as part of the loop invariant here. | |||||
pstefUnsubmitted Not Done Inline ActionsI admit I was wrong about the risk. But I'm not convinced it wasn't a bells and whistles... sorry, belts and suspenders kind of safety guarantee. I know I've written such reduntant checks. It's difficult for me to imagine strtok() returning an empty string, but I'm not sure. pstef: I admit I was wrong about the risk. But I'm not convinced it wasn't a bells and whistles... | |||||
continue; | continue; | ||||
sl_add(hosts, strdup(ptr)); | sl_add(hosts, strdup(ptr)); | ||||
} | } | ||||
line[len] = c; | line[len] = c; | ||||
} | } | ||||
(void)fclose(fp); | (void)fclose(fp); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 75 Lines • ▼ Show 20 Lines | if (hp2) | ||||
freehostent(hp2); | freehostent(hp2); | ||||
} | } | ||||
if (hp) | if (hp) | ||||
freehostent(hp); | freehostent(hp); | ||||
return hp ? 0 : -1; | return hp ? 0 : -1; | ||||
} | } | ||||
static void | static void | ||||
resolvone(int n) | resolvone(int n, enum method method) | ||||
{ | { | ||||
char buf[1024]; | char buf[1024]; | ||||
pthread_t self = pthread_self(); | pthread_t self = pthread_self(); | ||||
size_t i = (random() & 0x0fffffff) % hosts->sl_cur; | size_t i = (random() & 0x0fffffff) % hosts->sl_cur; | ||||
char *host = hosts->sl_str[i]; | char *host = hosts->sl_str[i]; | ||||
struct addrinfo hints, *res; | |||||
int error, len; | int error, len; | ||||
len = snprintf(buf, sizeof(buf), "%p: %d resolving %s %d\n", | len = snprintf(buf, sizeof(buf), "%p: %d resolving %s %d\n", | ||||
self, n, host, (int)i); | self, n, host, (int)i); | ||||
(void)write(STDOUT_FILENO, buf, len); | (void)write(STDOUT_FILENO, buf, len); | ||||
error = 0; | |||||
switch (method) { | switch (method) { | ||||
case METHOD_GETADDRINFO: | case METHOD_GETADDRINFO: | ||||
error = resolv_getaddrinfo(self, host, i); | error = resolv_getaddrinfo(self, host, i); | ||||
break; | break; | ||||
case METHOD_GETHOSTBY: | case METHOD_GETHOSTBY: | ||||
error = resolv_gethostby(self, host); | error = resolv_gethostby(self, host); | ||||
break; | break; | ||||
case METHOD_GETIPNODEBY: | case METHOD_GETIPNODEBY: | ||||
error = resolv_getipnodeby(self, host); | error = resolv_getipnodeby(self, host); | ||||
break; | break; | ||||
default: | default: | ||||
/* UNREACHABLE */ | |||||
/* XXX Needs an __assert_unreachable() for userland. */ | |||||
assert(0 && "Unreachable segment reached"); | |||||
abort(); | |||||
break; | break; | ||||
} | } | ||||
pthread_mutex_lock(&stats); | pthread_mutex_lock(&stats); | ||||
ask[i]++; | ask[i]++; | ||||
got[i] += error == 0; | got[i] += error == 0; | ||||
pthread_mutex_unlock(&stats); | pthread_mutex_unlock(&stats); | ||||
} | } | ||||
struct resolvloop_args { | |||||
int *nhosts; | |||||
enum method method; | |||||
}; | |||||
static void * | static void * | ||||
resolvloop(void *p) | resolvloop(void *p) | ||||
{ | { | ||||
int *nhosts = (int *)p; | struct resolvloop_args *args = p; | ||||
if (*nhosts == 0) | |||||
if (*args->nhosts == 0) { | |||||
free(args); | |||||
return NULL; | return NULL; | ||||
} | |||||
do | do | ||||
resolvone(*nhosts); | resolvone(*args->nhosts, args->method); | ||||
while (--(*nhosts)); | while (--(*args->nhosts)); | ||||
free(args); | |||||
return NULL; | return NULL; | ||||
} | } | ||||
static void | static void | ||||
run(int *nhosts) | run(int *nhosts, enum method method) | ||||
{ | { | ||||
pthread_t self; | pthread_t self; | ||||
int rc; | int rc; | ||||
struct resolvloop_args *args; | |||||
/* Created thread is responsible for free(). */ | |||||
args = malloc(sizeof(*args)); | |||||
ATF_REQUIRE(args != NULL); | |||||
args->nhosts = nhosts; | |||||
args->method = method; | |||||
self = pthread_self(); | self = pthread_self(); | ||||
rc = pthread_create(&self, NULL, resolvloop, nhosts); | rc = pthread_create(&self, NULL, resolvloop, args); | ||||
ATF_REQUIRE_MSG(rc == 0, "pthread_create failed: %s", strerror(rc)); | ATF_REQUIRE_MSG(rc == 0, "pthread_create failed: %s", strerror(rc)); | ||||
} | } | ||||
static int | static int | ||||
run_tests(const char *hostlist_file, enum method method) | run_tests(const char *hostlist_file, enum method method) | ||||
{ | { | ||||
int nthreads = NTHREADS; | size_t nthreads = NTHREADS; | ||||
int nhosts = NHOSTS; | size_t nhosts = NHOSTS; | ||||
int i, c, done, *nleft; | size_t i; | ||||
int c, done, *nleft; | |||||
hosts = sl_init(); | hosts = sl_init(); | ||||
srandom(1234); | srandom(1234); | ||||
load(hostlist_file); | load(hostlist_file); | ||||
ATF_REQUIRE_MSG(0 < hosts->sl_cur, "0 hosts in %s", hostlist_file); | ATF_REQUIRE_MSG(0 < hosts->sl_cur, "0 hosts in %s", hostlist_file); | ||||
nleft = malloc(nthreads * sizeof(int)); | nleft = malloc(nthreads * sizeof(int)); | ||||
ATF_REQUIRE(nleft != NULL); | ATF_REQUIRE(nleft != NULL); | ||||
ask = calloc(hosts->sl_cur, sizeof(int)); | ask = calloc(hosts->sl_cur, sizeof(int)); | ||||
ATF_REQUIRE(ask != NULL); | ATF_REQUIRE(ask != NULL); | ||||
got = calloc(hosts->sl_cur, sizeof(int)); | got = calloc(hosts->sl_cur, sizeof(int)); | ||||
ATF_REQUIRE(got != NULL); | ATF_REQUIRE(got != NULL); | ||||
for (i = 0; i < nthreads; i++) { | for (i = 0; i < nthreads; i++) { | ||||
nleft[i] = nhosts; | nleft[i] = nhosts; | ||||
run(&nleft[i]); | run(&nleft[i], method); | ||||
} | } | ||||
for (done = 0; !done;) { | for (done = 0; !done;) { | ||||
done = 1; | done = 1; | ||||
for (i = 0; i < nthreads; i++) { | for (i = 0; i < nthreads; i++) { | ||||
if (nleft[i] != 0) { | if (nleft[i] != 0) { | ||||
done = 0; | done = 0; | ||||
break; | break; | ||||
▲ Show 20 Lines • Show All 69 Lines • Show Last 20 Lines |
I think this was checking for a null pointer, in which case you risk a null pointer dereference after this change.