|
1 For CVE-2016-9189 |
|
2 https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2016-9189 |
|
3 |
|
4 Python Imaging allows context-dependent attackers to obtain sensitive |
|
5 information by using the "crafted image file" approach, related to an |
|
6 "Integer Overflow" issue affecting the Image.core.map_buffer in map.c |
|
7 component. |
|
8 |
|
9 Code changes based on those found upstream for Pillow at: |
|
10 |
|
11 https://github.com/python-pillow/Pillow/pull/2146/commits/c50ebe6459a131a1ea8ca531f10da616d3ceaa0f |
|
12 for: |
|
13 map.c |
|
14 |
|
15 https://github.com/python-pillow/Pillow/pull/2146/commits/445451c0b9347b50e0f603db33f196e207de470d |
|
16 for: |
|
17 PIL/Image.py |
|
18 |
|
19 https://github.com/python-pillow/Pillow/pull/2146/commits/1a43da7a8bda884a597f3a1623364f4719d21c14 |
|
20 for: |
|
21 PIL/EpsImagePlugin.py |
|
22 PIL/IptcImagePlugin.py |
|
23 PIL/JPegImagePlugin.py |
|
24 PIL/PpmImagePlugin.py |
|
25 _imaging.c |
|
26 libImaging/File.c |
|
27 |
|
28 --- Imaging-1.1.7/map.c.orig 2016-11-21 07:50:42.925380355 +0000 |
|
29 +++ Imaging-1.1.7/map.c 2016-11-21 07:53:34.182039527 +0000 |
|
30 @@ -339,8 +339,18 @@ |
|
31 stride = xsize * 4; |
|
32 } |
|
33 |
|
34 + if (ysize > INT_MAX / stride) { |
|
35 + PyErr_SetString(PyExc_MemoryError, "Integer overflow in ysize"); |
|
36 + return NULL; |
|
37 + } |
|
38 + |
|
39 size = ysize * stride; |
|
40 |
|
41 + if (offset > SIZE_MAX - size) { |
|
42 + PyErr_SetString(PyExc_MemoryError, "Integer overflow in offset"); |
|
43 + return NULL; |
|
44 + } |
|
45 + |
|
46 /* check buffer size */ |
|
47 bytes = PyImaging_ReadBuffer(target, (const void**) &ptr); |
|
48 if (bytes < 0) { |
|
49 --- Imaging-1.1.7/PIL/Image.py.orig 2016-11-21 07:58:09.978008218 +0000 |
|
50 +++ Imaging-1.1.7/PIL/Image.py 2016-11-21 08:02:12.063288055 +0000 |
|
51 @@ -1740,6 +1740,25 @@ |
|
52 |
|
53 return Image()._new(core.wedge("L")) |
|
54 |
|
55 + |
|
56 +def _check_size(size): |
|
57 + """ |
|
58 + Common check to enforce type and sanity check on size tuples |
|
59 + |
|
60 + :param size: Should be a 2 tuple of (width, height) |
|
61 + :returns: True, or raises a ValueError |
|
62 + """ |
|
63 + |
|
64 + if not isinstance(size, tuple): |
|
65 + raise ValueError("Size must be a tuple") |
|
66 + if len(size) != 2: |
|
67 + raise ValueError("Size must be a tuple of length 2") |
|
68 + if size[0] <= 0 or size[1] <= 0: |
|
69 + raise ValueError("Width and Height must be > 0") |
|
70 + |
|
71 + return True |
|
72 + |
|
73 + |
|
74 ## |
|
75 # Creates a new image with the given mode and size. |
|
76 # |
|
77 @@ -1756,6 +1775,8 @@ |
|
78 def new(mode, size, color=0): |
|
79 "Create a new image" |
|
80 |
|
81 + _check_size(size) |
|
82 + |
|
83 if color is None: |
|
84 # don't initialize |
|
85 return Image()._new(core.new(mode, size)) |
|
86 @@ -1792,6 +1813,8 @@ |
|
87 def fromstring(mode, size, data, decoder_name="raw", *args): |
|
88 "Load image from string" |
|
89 |
|
90 + _check_size(size) |
|
91 + |
|
92 # may pass tuple instead of argument list |
|
93 if len(args) == 1 and isTupleType(args[0]): |
|
94 args = args[0] |
|
95 @@ -1839,6 +1862,8 @@ |
|
96 def frombuffer(mode, size, data, decoder_name="raw", *args): |
|
97 "Load image from string or buffer" |
|
98 |
|
99 + _check_size(size) |
|
100 + |
|
101 # may pass tuple instead of argument list |
|
102 if len(args) == 1 and isTupleType(args[0]): |
|
103 args = args[0] |
|
104 --- Imaging-1.1.7/PIL/EpsImagePlugin.py.orig 2016-11-21 08:07:25.697709727 +0000 |
|
105 +++ Imaging-1.1.7/PIL/EpsImagePlugin.py 2016-11-21 08:12:17.177879463 +0000 |
|
106 @@ -74,12 +74,13 @@ |
|
107 status = gs.close() |
|
108 if status: |
|
109 raise IOError("gs failed (status %d)" % status) |
|
110 - im = Image.core.open_ppm(file) |
|
111 + im = Image.open(outfile) |
|
112 + im.load() |
|
113 finally: |
|
114 try: os.unlink(file) |
|
115 except: pass |
|
116 |
|
117 - return im |
|
118 + return im.im.copy() |
|
119 |
|
120 |
|
121 class PSFile: |
|
122 --- Imaging-1.1.7/PIL/IptcImagePlugin.py.orig 2016-11-21 08:07:25.704128117 +0000 |
|
123 +++ Imaging-1.1.7/PIL/IptcImagePlugin.py 2016-11-21 08:14:00.062399442 +0000 |
|
124 @@ -192,14 +192,9 @@ |
|
125 o.close() |
|
126 |
|
127 try: |
|
128 - try: |
|
129 - # fast |
|
130 - self.im = Image.core.open_ppm(outfile) |
|
131 - except: |
|
132 - # slightly slower |
|
133 - im = Image.open(outfile) |
|
134 - im.load() |
|
135 - self.im = im.im |
|
136 + _im = Image.open(outfile) |
|
137 + _im.load() |
|
138 + self.im = _im.im |
|
139 finally: |
|
140 try: os.unlink(outfile) |
|
141 except: pass |
|
142 --- Imaging-1.1.7/PIL/JpegImagePlugin.py.orig 2016-11-21 08:16:20.362838015 +0000 |
|
143 +++ Imaging-1.1.7/PIL/JpegImagePlugin.py 2016-11-21 08:19:06.852441772 +0000 |
|
144 @@ -352,7 +352,9 @@ |
|
145 raise ValueError("Invalid Filename") |
|
146 |
|
147 try: |
|
148 - self.im = Image.core.open_ppm(path) |
|
149 + _im = Image.open(path) |
|
150 + _im.load() |
|
151 + self.im = _im.im |
|
152 finally: |
|
153 try: os.unlink(path) |
|
154 except: pass |
|
155 --- Imaging-1.1.7/PIL/PpmImagePlugin.py.orig 2016-11-21 08:07:25.708512595 +0000 |
|
156 +++ Imaging-1.1.7/PIL/PpmImagePlugin.py 2016-11-21 08:19:55.525463844 +0000 |
|
157 @@ -93,11 +93,6 @@ |
|
158 self.fp.tell(), |
|
159 (rawmode, 0, 1))] |
|
160 |
|
161 - # ALTERNATIVE: load via builtin debug function |
|
162 - # self.im = Image.core.open_ppm(self.filename) |
|
163 - # self.mode = self.im.mode |
|
164 - # self.size = self.im.size |
|
165 - |
|
166 # |
|
167 # -------------------------------------------------------------------- |
|
168 |
|
169 --- Imaging-1.1.7/_imaging.c.orig 2016-11-21 08:07:25.710428542 +0000 |
|
170 +++ Imaging-1.1.7/_imaging.c 2016-11-21 08:21:03.690730505 +0000 |
|
171 @@ -684,17 +684,6 @@ |
|
172 } |
|
173 |
|
174 static PyObject* |
|
175 -_open_ppm(PyObject* self, PyObject* args) |
|
176 -{ |
|
177 - char* filename; |
|
178 - |
|
179 - if (!PyArg_ParseTuple(args, "s", &filename)) |
|
180 - return NULL; |
|
181 - |
|
182 - return PyImagingNew(ImagingOpenPPM(filename)); |
|
183 -} |
|
184 - |
|
185 -static PyObject* |
|
186 _blend(ImagingObject* self, PyObject* args) |
|
187 { |
|
188 ImagingObject* imagep1; |
|
189 @@ -3215,9 +3204,6 @@ |
|
190 {"crc32", (PyCFunction)_crc32, 1}, |
|
191 {"getcodecstatus", (PyCFunction)_getcodecstatus, 1}, |
|
192 |
|
193 - /* Debugging stuff */ |
|
194 - {"open_ppm", (PyCFunction)_open_ppm, 1}, |
|
195 - |
|
196 /* Special effects (experimental) */ |
|
197 #ifdef WITH_EFFECTS |
|
198 {"effect_mandelbrot", (PyCFunction)_effect_mandelbrot, 1}, |
|
199 --- Imaging-1.1.7/libImaging/File.c.orig 2016-11-21 08:07:25.712276651 +0000 |
|
200 +++ Imaging-1.1.7/libImaging/File.c 2016-11-21 08:22:37.837774950 +0000 |
|
201 @@ -20,116 +20,6 @@ |
|
202 |
|
203 #include <ctype.h> |
|
204 |
|
205 -Imaging |
|
206 -ImagingOpenPPM(const char* infile) |
|
207 -{ |
|
208 - FILE* fp; |
|
209 - int i, c, v; |
|
210 - char* mode; |
|
211 - int x, y, max; |
|
212 - Imaging im; |
|
213 - |
|
214 - if (!infile) |
|
215 - return ImagingError_ValueError(NULL); |
|
216 - |
|
217 - fp = fopen(infile, "rb"); |
|
218 - if (!fp) |
|
219 - return ImagingError_IOError(); |
|
220 - |
|
221 - /* PPM magic */ |
|
222 - if (fgetc(fp) != 'P') |
|
223 - goto error; |
|
224 - switch (fgetc(fp)) { |
|
225 - case '4': /* FIXME: 1-bit images are not yet supported */ |
|
226 - goto error; |
|
227 - case '5': |
|
228 - mode = "L"; |
|
229 - break; |
|
230 - case '6': |
|
231 - mode = "RGB"; |
|
232 - break; |
|
233 - default: |
|
234 - goto error; |
|
235 - } |
|
236 - |
|
237 - i = 0; |
|
238 - c = fgetc(fp); |
|
239 - |
|
240 - x = y = max = 0; |
|
241 - |
|
242 - while (i < 3) { |
|
243 - |
|
244 - /* Ignore optional comment fields */ |
|
245 - while (c == '\n') { |
|
246 - c = fgetc(fp); |
|
247 - if (c == '#') { |
|
248 - do { |
|
249 - c = fgetc(fp); |
|
250 - if (c == EOF) |
|
251 - goto error; |
|
252 - } while (c != '\n'); |
|
253 - c = fgetc(fp); |
|
254 - } |
|
255 - } |
|
256 - |
|
257 - /* Skip forward to next value */ |
|
258 - while (isspace(c)) |
|
259 - c = fgetc(fp); |
|
260 - |
|
261 - /* And parse it */ |
|
262 - v = 0; |
|
263 - while (isdigit(c)) { |
|
264 - v = v * 10 + (c - '0'); |
|
265 - c = fgetc(fp); |
|
266 - } |
|
267 - |
|
268 - if (c == EOF) |
|
269 - goto error; |
|
270 - |
|
271 - switch (i++) { |
|
272 - case 0: |
|
273 - x = v; |
|
274 - break; |
|
275 - case 1: |
|
276 - y = v; |
|
277 - break; |
|
278 - case 2: |
|
279 - max = v; |
|
280 - break; |
|
281 - } |
|
282 - } |
|
283 - |
|
284 - im = ImagingNew(mode, x, y); |
|
285 - if (!im) |
|
286 - return NULL; |
|
287 - |
|
288 - /* if (max != 255) ... FIXME: does anyone ever use this feature? */ |
|
289 - |
|
290 - if (strcmp(im->mode, "L") == 0) { |
|
291 - |
|
292 - /* PPM "L" */ |
|
293 - for (y = 0; y < im->ysize; y++) |
|
294 - if (fread(im->image[y], im->xsize, 1, fp) != 1) |
|
295 - goto error; |
|
296 - |
|
297 - } else { |
|
298 - |
|
299 - /* PPM "RGB" or PyPPM mode */ |
|
300 - for (y = 0; y < im->ysize; y++) |
|
301 - for (x = i = 0; x < im->xsize; x++, i += im->pixelsize) |
|
302 - if (fread(im->image[y]+i, im->bands, 1, fp) != 1) |
|
303 - goto error; |
|
304 - } |
|
305 - |
|
306 - fclose(fp); |
|
307 - |
|
308 - return im; |
|
309 - |
|
310 -error: |
|
311 - fclose(fp); |
|
312 - return ImagingError_IOError(); |
|
313 -} |
|
314 - |
|
315 |
|
316 int |
|
317 ImagingSaveRaw(Imaging im, FILE* fp) |