1
Description: fix denial of service via decompression bomb
2
Origin: backported from 1.2.43
3
Bug-Ubuntu: https://bugs.launchpad.net/ubuntu/+source/libpng/+bug/533140
4
Bug-Debian: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=572308
6
Index: libpng-1.2.42/pngrutil.c
7
===================================================================
8
--- libpng-1.2.42.orig/pngrutil.c 2010-01-03 00:04:19.000000000 -0500
9
+++ libpng-1.2.42/pngrutil.c 2010-03-11 14:52:59.000000000 -0500
10
@@ -217,180 +217,193 @@
12
#if defined(PNG_READ_zTXt_SUPPORTED) || defined(PNG_READ_iTXt_SUPPORTED) || \
13
defined(PNG_READ_iCCP_SUPPORTED)
15
- * Decompress trailing data in a chunk. The assumption is that chunkdata
16
- * points at an allocated area holding the contents of a chunk with a
17
- * trailing compressed part. What we get back is an allocated area
18
- * holding the original prefix part and an uncompressed version of the
19
- * trailing part (the malloc area passed in is freed).
22
-png_decompress_chunk(png_structp png_ptr, int comp_type,
23
- png_size_t chunklength,
24
- png_size_t prefix_size, png_size_t *newlength)
26
+png_inflate(png_structp png_ptr, const png_byte *data, png_size_t size,
27
+ png_bytep output, png_size_t output_size)
29
- static PNG_CONST char msg[] = "Error decoding compressed chunk";
31
- png_size_t text_size;
32
+ png_size_t count = 0;
34
- if (comp_type == PNG_COMPRESSION_TYPE_BASE)
35
+ png_ptr->zstream.next_in = (png_bytep)data; /* const_cast: VALID */
36
+ png_ptr->zstream.avail_in = size;
41
- png_ptr->zstream.next_in = (png_bytep)(png_ptr->chunkdata + prefix_size);
42
- png_ptr->zstream.avail_in = (uInt)(chunklength - prefix_size);
45
+ /* Reset the output buffer each time round - we empty it
46
+ * after every inflate call.
48
png_ptr->zstream.next_out = png_ptr->zbuf;
49
- png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
50
+ png_ptr->zstream.avail_out = png_ptr->zbuf_size;
54
+ ret = inflate(&png_ptr->zstream, Z_NO_FLUSH);
55
+ avail = png_ptr->zbuf_size - png_ptr->zstream.avail_out;
57
- while (png_ptr->zstream.avail_in)
59
- ret = inflate(&png_ptr->zstream, Z_PARTIAL_FLUSH);
60
- if (ret != Z_OK && ret != Z_STREAM_END)
62
- if (png_ptr->zstream.msg != NULL)
63
- png_warning(png_ptr, png_ptr->zstream.msg);
65
- png_warning(png_ptr, msg);
66
- inflateReset(&png_ptr->zstream);
67
- png_ptr->zstream.avail_in = 0;
71
- text_size = prefix_size + png_sizeof(msg) + 1;
72
- text = (png_charp)png_malloc_warn(png_ptr, text_size);
75
- png_free(png_ptr, png_ptr->chunkdata);
76
- png_ptr->chunkdata = NULL;
77
- png_error(png_ptr, "Not enough memory to decompress chunk");
79
- png_memcpy(text, png_ptr->chunkdata, prefix_size);
82
- text[text_size - 1] = 0x00;
84
- /* Copy what we can of the error message into the text chunk */
85
- text_size = (png_size_t)(chunklength -
86
- (text - png_ptr->chunkdata) - 1);
87
- if (text_size > png_sizeof(msg))
88
- text_size = png_sizeof(msg);
89
- png_memcpy(text + prefix_size, msg, text_size);
92
- if (!png_ptr->zstream.avail_out || ret == Z_STREAM_END)
96
- text_size = prefix_size +
97
- png_ptr->zbuf_size - png_ptr->zstream.avail_out;
98
- text = (png_charp)png_malloc_warn(png_ptr, text_size + 1);
101
- png_free(png_ptr, png_ptr->chunkdata);
102
- png_ptr->chunkdata = NULL;
104
- "Not enough memory to decompress chunk.");
106
- png_memcpy(text + prefix_size, png_ptr->zbuf,
107
- text_size - prefix_size);
108
- png_memcpy(text, png_ptr->chunkdata, prefix_size);
109
- *(text + text_size) = 0x00;
116
- text = (png_charp)png_malloc_warn(png_ptr,
117
- (png_uint_32)(text_size +
118
- png_ptr->zbuf_size - png_ptr->zstream.avail_out + 1));
121
- png_free(png_ptr, tmp);
122
- png_free(png_ptr, png_ptr->chunkdata);
123
- png_ptr->chunkdata = NULL;
125
- "Not enough memory to decompress chunk..");
127
- png_memcpy(text, tmp, text_size);
128
- png_free(png_ptr, tmp);
129
- png_memcpy(text + text_size, png_ptr->zbuf,
130
- (png_ptr->zbuf_size - png_ptr->zstream.avail_out));
131
- text_size += png_ptr->zbuf_size - png_ptr->zstream.avail_out;
132
- *(text + text_size) = 0x00;
134
- if (ret == Z_STREAM_END)
138
- png_ptr->zstream.next_out = png_ptr->zbuf;
139
- png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
143
- if (ret != Z_STREAM_END)
145
-#if defined(PNG_STDIO_SUPPORTED) && !defined(_WIN32_WCE)
147
+ /* First copy/count any new output - but only if we didn't
148
+ * get an error code.
150
+ if ((ret == Z_OK || ret == Z_STREAM_END) && avail > 0)
152
+ if (output != 0 && output_size > count)
154
+ int copy = output_size - count;
155
+ if (avail < copy) copy = avail;
156
+ png_memcpy(output + count, png_ptr->zbuf, copy);
164
+ /* Termination conditions - always reset the zstream, it
165
+ * must be left in inflateInit state.
167
+ png_ptr->zstream.avail_in = 0;
168
+ inflateReset(&png_ptr->zstream);
170
- if (ret == Z_BUF_ERROR)
171
- png_snprintf(umsg, 52,
172
- "Buffer error in compressed datastream in %s chunk",
173
- png_ptr->chunk_name);
175
- else if (ret == Z_DATA_ERROR)
176
- png_snprintf(umsg, 52,
177
- "Data error in compressed datastream in %s chunk",
178
- png_ptr->chunk_name);
179
+ if (ret == Z_STREAM_END)
180
+ return count; /* NOTE: may be zero. */
183
- png_snprintf(umsg, 52,
184
- "Incomplete compressed datastream in %s chunk",
185
- png_ptr->chunk_name);
186
+ /* Now handle the error codes - the API always returns 0
187
+ * and the error message is dumped into the uncompressed
188
+ * buffer if available.
191
+ char *msg, umsg[52];
192
+ if (png_ptr->zstream.msg != 0)
193
+ msg = png_ptr->zstream.msg;
196
+#if defined(PNG_STDIO_SUPPORTED) && !defined(_WIN32_WCE)
200
+ msg = "Buffer error in compressed datastream in %s chunk";
203
+ msg = "Data error in compressed datastream in %s chunk";
206
+ msg = "Incomplete compressed datastream in %s chunk";
210
- png_warning(png_ptr, umsg);
211
+ png_snprintf(umsg, sizeof umsg, msg, png_ptr->chunk_name);
214
- png_warning(png_ptr,
215
- "Incomplete compressed datastream in chunk other than IDAT");
216
+ msg = "Damaged compressed datastream in chunk other than IDAT";
218
- text_size = prefix_size;
221
- text = (png_charp)png_malloc_warn(png_ptr, text_size+1);
224
- png_free(png_ptr, png_ptr->chunkdata);
225
- png_ptr->chunkdata = NULL;
226
- png_error(png_ptr, "Not enough memory for text.");
228
- png_memcpy(text, png_ptr->chunkdata, prefix_size);
230
- *(text + text_size) = 0x00;
233
+ png_warning(png_ptr, msg);
236
- inflateReset(&png_ptr->zstream);
237
- png_ptr->zstream.avail_in = 0;
238
+ /* 0 means an error - notice that this code simple ignores
239
+ * zero length compressed chunks as a result.
245
- png_free(png_ptr, png_ptr->chunkdata);
246
- png_ptr->chunkdata = text;
247
- *newlength=text_size;
249
+ * Decompress trailing data in a chunk. The assumption is that chunkdata
250
+ * points at an allocated area holding the contents of a chunk with a
251
+ * trailing compressed part. What we get back is an allocated area
252
+ * holding the original prefix part and an uncompressed version of the
253
+ * trailing part (the malloc area passed in is freed).
256
+png_decompress_chunk(png_structp png_ptr, int comp_type,
257
+ png_size_t chunklength,
258
+ png_size_t prefix_size, png_size_t *newlength)
260
+ /* The caller should guarantee this */
261
+ if (prefix_size > chunklength)
263
+ /* The recovery is to delete the chunk. */
264
+ png_warning(png_ptr, "invalid chunklength");
265
+ prefix_size = 0; /* To delete everything */
268
+ else if (comp_type == PNG_COMPRESSION_TYPE_BASE)
270
+ png_size_t expanded_size = png_inflate(png_ptr,
271
+ (png_bytep)(png_ptr->chunkdata + prefix_size),
272
+ chunklength - prefix_size,
273
+ 0/*output*/, 0/*output size*/);
275
+ /* If the size is zero either there was an error and a message
276
+ * has already been output (warning) or the size really is zero
277
+ * and we have nothing to do - the code will exit through the
278
+ * error case below.
280
+ if (expanded_size > 0)
282
+ /* Success (maybe) - really uncompress the chunk. */
283
+ png_size_t new_size = 0;
284
+ png_charp text = png_malloc_warn(png_ptr,
285
+ prefix_size + expanded_size + 1);
289
+ png_memcpy(text, png_ptr->chunkdata, prefix_size);
290
+ new_size = png_inflate(png_ptr,
291
+ (png_bytep)(png_ptr->chunkdata + prefix_size),
292
+ chunklength - prefix_size,
293
+ (png_bytep)(text + prefix_size), expanded_size);
294
+ text[prefix_size + expanded_size] = 0; /* just in case */
296
+ if (new_size == expanded_size)
298
+ png_free(png_ptr, png_ptr->chunkdata);
299
+ png_ptr->chunkdata = text;
300
+ *newlength = prefix_size + expanded_size;
301
+ return; /* The success return! */
304
+ png_warning(png_ptr, "png_inflate logic error");
305
+ png_free(png_ptr, text);
308
+ png_warning(png_ptr, "Not enough memory to decompress chunk.");
312
else /* if (comp_type != PNG_COMPRESSION_TYPE_BASE) */
314
-#if defined(PNG_STDIO_SUPPORTED) && !defined(_WIN32_WCE)
317
- png_snprintf(umsg, 50, "Unknown zTXt compression type %d", comp_type);
318
+#if defined(PNG_STDIO_SUPPORTED) && !defined(_WIN32_WCE)
319
+ png_snprintf(umsg, sizeof umsg, "Unknown zTXt compression type %d", comp_type);
320
png_warning(png_ptr, umsg);
322
png_warning(png_ptr, "Unknown zTXt compression type");
325
- *(png_ptr->chunkdata + prefix_size) = 0x00;
326
- *newlength = prefix_size;
327
+ /* The recovery is to simply drop the data. */
330
+ /* Generic error return - leave the prefix, delete the compressed
331
+ * data, reallocate the chunkdata to remove the potentially large
332
+ * amount of compressed data.
335
+ png_charp text = png_malloc_warn(png_ptr, prefix_size + 1);
338
+ if (prefix_size > 0)
339
+ png_memcpy(text, png_ptr->chunkdata, prefix_size);
340
+ png_free(png_ptr, png_ptr->chunkdata);
341
+ png_ptr->chunkdata = text;
343
+ /* This is an extra zero in the 'uncompressed' part. */
344
+ *(png_ptr->chunkdata + prefix_size) = 0x00;
346
+ /* Ignore a malloc error here - it is safe. */
349
+ *newlength = prefix_size;