Possible race condition in gwlib/protected.c

Kieran <[email protected]>
Newsgroups gmane.comp.mobile.kannel.devel
Message-ID <[email protected]>
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);
     }
 }
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.