Hi,
I'm feeling my way into the kannel source, and I think I've identified
a race condition in the gw_gethostbyname() function in gwlib/protected.c
In particular, the gethostbyname function call returns some static data.
I've rolled patch to fix this, noting that:
1. The only callers in the of gw_gethostbyname in tree currently are in
gwlib/socket.c
2. Those callers only use the hostent.h_name and hostent.h_addr_list[0]
fields in the struct hostent.
3. I've been careful to free the memory allocated as close to the call
to gw_gethostbyname() as possible.
The patch thus copies mallocs space for these fields, and copies the
relevant data, and then frees it.
I know that the BSDs don't support gethostbyname_r() function call (and
that it's a pig to make portable, and isn't part of the standards). Is
there a reason Kannel uses gethostbyname in preference to getaddrinfo()?
Regards
Kieran
diff -ur -x '*.o' cvs/gateway/gwlib/protected.c work/gateway/gwlib/protected.c
--- cvs/gateway/gwlib/protected.c 2000-10-23 18:56:05.000000000 +0100
+++ work/gateway/gwlib/protected.c 2002-08-21 04:00:55.000000000 +0100
@@ -92,19 +92,69 @@
return ret;
}
+/* This function:
+ 1. Mallocs enough memory to copy relevent fields of src
+ 2. Copies those fields into dest
+ This is done because the struct that gethostbyname returns contains pointers
+ to static data.
+ Note: since kannel only uses the hostent.h_name and hostent.h_addr_list[0]
+ elements of the returned hostent currently, that's all that is copied.
+*/
+void malloc_and_copy_hostent (struct hostent * dest, const struct hostent * src) {
+ int count;
+ char *cp;
+ /* Mallocs and copies hostent.h_name element */
+ count = strlen (src->h_name) + 1;
+ dest->h_name = (char *)gw_native_malloc (count);
+ memcpy (dest->h_name, src->h_name, count);
+
+ /* Mallocs an array of size 2 for hostent.h_addr_list */
+ count = sizeof (src->h_addr_list);
+ dest->h_addr_list = (char **)gw_native_malloc (2 * sizeof(char *));
+
+ /* Mallocs and copies first element of src->h_addr_list[] */
+ count = strlen (src->h_addr_list[0]) + 1;
+ cp = (char *)gw_native_malloc (count);
+ memcpy(cp, src->h_addr_list[0], count);
+
+ /* Sets h_addr_list[0] to copy, and [1] to NULL */
+ dest->h_addr_list[0] = cp;
+ dest->h_addr_list[1] = NULL;
+
+ return;
+}
+
+/* This function frees allocated hostent structures, as malloced by
+ malloc_and_copy_hostent() defined above. This function needs to
+ be called every time gw_gethostbyname() is called to avoid memory
+ leaks
+*/
+void free_hostent(struct hostent * in) {
+ int i;
+ gw_free (in->h_name);
+ for (i=0; in->h_addr_list[i] != NULL; i++) {
+ gw_free (in->h_addr_list[i]);
+ }
+ gw_free (in->h_addr_list);
+}
+
+/* Pre: The pointer "ent" must point to a valid struct hostent
+ Post: if the return value == 0, fields in ent point to malloced
+ memory which should be freed using free_hostent()
+*/
int gw_gethostbyname(struct hostent *ent, const char *name)
{
int ret;
struct hostent *p;
lock(GETHOSTBYNAME);
- p = gethostbyname(name);
+ p = gethostbyname(name);
if (p == NULL)
ret = -1;
else {
ret = 0;
- *ent = *p;
+ malloc_and_copy_hostent(ent, p);
}
unlock(GETHOSTBYNAME);
return ret;
diff -ur -x '*.o' cvs/gateway/gwlib/protected.h work/gateway/gwlib/protected.h
--- cvs/gateway/gwlib/protected.h 2001-01-29 13:01:43.000000000 +0000
+++ work/gateway/gwlib/protected.h 2002-08-21 03:36:35.000000000 +0100
@@ -21,6 +21,7 @@
struct tm gw_gmtime(time_t t);
int gw_rand(void);
int gw_gethostbyname(struct hostent *ret, const char *name);
+void free_hostent(struct hostent *);
/*
* Make it harder to use these by mistake.
diff -ur -x '*.o' cvs/gateway/gwlib/socket.c work/gateway/gwlib/socket.c
--- cvs/gateway/gwlib/socket.c 2002-05-02 15:44:54.000000000 +0100
+++ work/gateway/gwlib/socket.c 2002-08-21 04:02:29.000000000 +0100
@@ -58,6 +58,7 @@
goto error;
}
addr.sin_addr = *(struct in_addr *) hostinfo.h_addr;
+ free_hostent(&hostinfo);
}
reuse = 1;
@@ -116,7 +117,8 @@
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr = *(struct in_addr *) hostinfo.h_addr;
-
+ free_hostent(&hostinfo);
+
if (our_port > 0 || (interface_name != NULL && strcmp(interface_name, "*") != 0))
{
int reuse;
@@ -131,6 +133,7 @@
goto error;
}
o_addr.sin_addr = *(struct in_addr *) o_hostinfo.h_addr;
+ free_hostent(&o_hostinfo);
}
reuse = 1;
@@ -305,6 +308,7 @@
return -1;
}
sa.sin_addr = *(struct in_addr *) hostinfo.h_addr;
+ free_hostent(&hostinfo);
}
if (bind(s, (struct sockaddr *) &sa, (int) sizeof(sa)) == -1) {
@@ -335,6 +339,7 @@
return NULL;
}
sa.sin_addr = *(struct in_addr *) h.h_addr_list[0];
+ free_hostent(&h);
}
return octstr_create_from_data((char *) &sa, sizeof(sa));
@@ -440,6 +445,7 @@
} else {
official_name = octstr_create(h.h_name);
official_ip = gw_netaddr_to_octstr(AF_INET, h.h_addr);
+ free_hostent(&h);
}
}