Content-Length bugs

Mark Doliner mark at kingant.net
Sun Jan 12 17:33:45 EST 2014


Hmm, I think the specific example of http_canread() might not be a
buffer overflow or null pointer dereference. I think the worst that
can happen is the while loop will run for a long time reading 1 byte
at a time. Eventually read() will probably return EAGAIN and we'll
break out of the while loop. But please let me know if anyone
disagrees.

I checked in the attached change to our private 2.x.y branch. My
inclination is to leave it like this until 3.0.0, when we hopefully
use the new unified http parsing code.
-------------- next part --------------
diff -r 82ec5fb22ce9 ChangeLog
--- a/ChangeLog	Sun Jan 12 13:08:28 2014 -0800
+++ b/ChangeLog	Sun Jan 12 14:27:03 2014 -0800
@@ -9,6 +9,8 @@ version 2.10.8:
 	* Fix buffer overflow when parsing a malformed HTTP response with
 	  chunked Transfer-Encoding. (Discovered by Matt Jones, Volvent)
 	  (CVE-2014-NNNN)
+	* Better handling of HTTP proxy responses with negative Content-Lengths.
+	  (Discovered by Matt Jones, Volvent)
 	* Fix handling of SSL certificates without subjects when using libnss.
 	* Fix handling of SSL certificates with timestamps in the distant future
 	  when using libnss. (#15586)
diff -r 82ec5fb22ce9 libpurple/proxy.c
--- a/libpurple/proxy.c	Sun Jan 12 13:08:28 2014 -0800
+++ b/libpurple/proxy.c	Sun Jan 12 14:27:03 2014 -0800
@@ -985,22 +985,34 @@ http_canread(gpointer data, gint source,
 	p = g_strrstr((const gchar *)connect_data->read_buffer, "Content-Length: ");
 	if (p != NULL) {
 		gchar *tmp;
-		int len = 0;
+		gsize content_len;
 		char tmpc;
+
 		p += strlen("Content-Length: ");
 		tmp = strchr(p, '\r');
 		if(tmp)
 			*tmp = '\0';
-		len = atoi(p);
+		if (sscanf(p, "%" G_GSIZE_FORMAT, &content_len) != 1) {
+			/* Couldn't read content length */
+			purple_debug_error("proxy", "Couldn't read content length value "
+					"from %s\n", p);
+			if(tmp)
+				*tmp = '\r';
+			purple_proxy_connect_data_disconnect_formatted(connect_data,
+					_("Unable to parse response from HTTP proxy: %s"),
+					connect_data->read_buffer);
+			return;
+		}
 		if(tmp)
 			*tmp = '\r';
 
 		/* Compensate for what has already been read */
-		len -= connect_data->read_len - headers_len;
+		content_len -= connect_data->read_len - headers_len;
 		/* I'm assuming that we're doing this to prevent the server from
 		   complaining / breaking since we don't read the whole page */
-		while (len--) {
+		while (content_len--) {
 			/* TODO: deal with EAGAIN (and other errors) better */
+			/* TODO: Reading 1 byte at a time is horrible and stupid. */
 			if (read(connect_data->fd, &tmpc, 1) < 0 && errno != EAGAIN)
 				break;
 		}


More information about the security mailing list