changeset 27328:e28b627be482

A few changes to our code to handle chunked transfer-encodings (technically required for http 1.1 clients, but I guess it hasn't been a problem until now?) * Most importantly, check that the size of this chunk is less than the size of the data remaining in the buffer. Otherwise malicious servers could cause us to crash * Handle reading chunks that have a semi-colon after them. It seems like maybe this is allowed but almost never used? * Assume len is non-NULL (it always is in our case) * Add some comments
author Mark Doliner <mark@kingant.net>
date Thu, 02 Jul 2009 07:33:17 +0000
parents a051f77d86a8
children b0f0579f5f22
files libpurple/util.c
diffstat 1 files changed, 32 insertions(+), 8 deletions(-) [+]
line wrap: on
line diff
--- a/libpurple/util.c	Thu Jul 02 07:09:24 2009 +0000
+++ b/libpurple/util.c	Thu Jul 02 07:33:17 2009 +0000
@@ -3788,32 +3788,56 @@
 process_chunked_data(char *data, gsize *len)
 {
 	gsize sz;
-	gsize nlen = 0;
+	gsize newlen = 0;
 	char *p = data;
 	char *s = data;
 
 	while (*s) {
-		if (sscanf(s, "%" G_GSIZE_MODIFIER "x\r\n", &sz) != 1) {
-			purple_debug_error("util", "Error processing chunked data. Expected data length, found: %s\n", s);
+		/* Read the size of this chunk */
+		if (sscanf(s, "%" G_GSIZE_MODIFIER "x\r\n", &sz) != 1 &&
+			sscanf(s, "%" G_GSIZE_MODIFIER "x;", &sz) != 1)
+		{
+			purple_debug_error("util", "Error processing chunked data: "
+					"Expected data length, found: %s\n", s);
 			break;
 		}
-		if (sz == 0)
+		if (sz == 0) {
+			/* We've reached the last chunk */
+			/*
+			 * TODO: The spec allows "footers" to follow the last chunk.
+			 *       If there is more data after this line then we should
+			 *       treat it like a header.
+			 */
 			break;
+		}
+
+		/* Advance to the start of the data */
 		s = strstr(s, "\r\n") + 2;
+
+		if (s + sz > data + *len) {
+			purple_debug_error("util", "Error processing chunked data: "
+					"Chunk size %" G_GSIZE_FORMAT " bytes was longer "
+					"than the data remaining in the buffer (%"
+					G_GSIZE_FORMAT " bytes)\n", sz, data + *len - s);
+		}
+
+		/* Move all data overtop of the chunk length that we read in earlier */
 		g_memmove(p, s, sz);
 		p += sz;
 		s += sz;
-		nlen += sz;
+		newlen += sz;
 		if (*s != '\r' && *(s + 1) != '\n') {
-			purple_debug_error("util", "Error processing chunked data. Expected \\r\\n, found: %s\n", s);
+			purple_debug_error("util", "Error processing chunked data: "
+					"Expected \\r\\n, found: %s\n", s);
 			break;
 		}
 		s += 2;
 	}
+
+	/* NULL terminate the data */
 	*p = 0;
 
-	if (len)
-		*len = nlen;
+	*len = newlen;
 }
 
 static void