1 | /* gtkiconcachevalidator.c |
2 | * Copyright (C) 2007 Red Hat, Inc |
3 | * |
4 | * This library is free software; you can redistribute it and/or |
5 | * modify it under the terms of the GNU Library General Public |
6 | * License as published by the Free Software Foundation; either |
7 | * version 2 of the License, or (at your option) any later version. |
8 | * |
9 | * This library is distributed in the hope that it will be useful, |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
12 | * Library General Public License for more details. |
13 | * |
14 | * You should have received a copy of the GNU Library General Public |
15 | * License along with this library. If not, see <http://www.gnu.org/licenses/>. |
16 | */ |
17 | #include "config.h" |
18 | #include "gtkiconcachevalidatorprivate.h" |
19 | |
20 | #include <glib.h> |
21 | #include <gdk-pixbuf/gdk-pixdata.h> |
22 | |
23 | |
24 | #define VERBOSE(x) |
25 | |
26 | #define check(name,condition) \ |
27 | if (!(condition)) \ |
28 | { \ |
29 | VERBOSE(g_message ("bad %s", (name))); \ |
30 | return FALSE; \ |
31 | } |
32 | |
33 | static inline gboolean |
34 | get_uint16 (CacheInfo *info, |
35 | guint32 offset, |
36 | guint16 *value) |
37 | { |
38 | if (offset < info->cache_size) |
39 | { |
40 | *value = GUINT16_FROM_BE(*(guint16*)(info->cache + offset)); |
41 | return TRUE; |
42 | } |
43 | else |
44 | { |
45 | *value = 0; |
46 | return FALSE; |
47 | } |
48 | } |
49 | |
50 | static inline gboolean |
51 | get_uint32 (CacheInfo *info, |
52 | guint32 offset, |
53 | guint32 *value) |
54 | { |
55 | if (offset < info->cache_size) |
56 | { |
57 | *value = GUINT32_FROM_BE(*(guint32*)(info->cache + offset)); |
58 | return TRUE; |
59 | } |
60 | else |
61 | { |
62 | *value = 0; |
63 | return FALSE; |
64 | } |
65 | } |
66 | |
67 | static gboolean |
68 | check_version (CacheInfo *info) |
69 | { |
70 | guint16 major, minor; |
71 | |
72 | check ("major version" , get_uint16 (info, 0, &major) && major == 1); |
73 | check ("minor version" , get_uint16 (info, 2, &minor) && minor == 0); |
74 | |
75 | return TRUE; |
76 | } |
77 | |
78 | static gboolean |
79 | check_string (CacheInfo *info, |
80 | guint32 offset) |
81 | { |
82 | check ("string offset" , offset < info->cache_size); |
83 | |
84 | if (info->flags & CHECK_STRINGS) |
85 | { |
86 | int i; |
87 | char c; |
88 | |
89 | /* assume no string is longer than 1k */ |
90 | for (i = 0; i < 1024; i++) |
91 | { |
92 | check ("string offset" , offset + i < info->cache_size) |
93 | c = *(info->cache + offset + i); |
94 | if (c == '\0') |
95 | break; |
96 | check ("string content" , g_ascii_isgraph (c)); |
97 | } |
98 | check ("string length" , i < 1024); |
99 | } |
100 | |
101 | return TRUE; |
102 | } |
103 | |
104 | static gboolean |
105 | check_string_utf8 (CacheInfo *info, |
106 | guint32 offset) |
107 | { |
108 | check ("string offset" , offset < info->cache_size); |
109 | |
110 | if (info->flags & CHECK_STRINGS) |
111 | { |
112 | int i; |
113 | char c; |
114 | |
115 | /* assume no string is longer than 1k */ |
116 | for (i = 0; i < 1024; i++) |
117 | { |
118 | check ("string offset" , offset + i < info->cache_size) |
119 | c = *(info->cache + offset + i); |
120 | if (c == '\0') |
121 | break; |
122 | } |
123 | check ("string length" , i < 1024); |
124 | check ("string utf8 data" , g_utf8_validate((char *)(info->cache + offset), -1, NULL)); |
125 | } |
126 | |
127 | return TRUE; |
128 | } |
129 | |
130 | static gboolean |
131 | check_directory_list (CacheInfo *info, |
132 | guint32 offset) |
133 | { |
134 | guint32 directory_offset; |
135 | int i; |
136 | |
137 | check ("offset, directory list" , get_uint32 (info, offset, &info->n_directories)); |
138 | |
139 | for (i = 0; i < info->n_directories; i++) |
140 | { |
141 | check ("offset, directory" , get_uint32 (info, offset + 4 + 4 * i, &directory_offset)); |
142 | if (!check_string (info, offset: directory_offset)) |
143 | return FALSE; |
144 | } |
145 | |
146 | return TRUE; |
147 | } |
148 | |
149 | static gboolean |
150 | check_pixel_data (CacheInfo *info, |
151 | guint32 offset) |
152 | { |
153 | guint32 type; |
154 | guint32 length; |
155 | |
156 | check ("offset, pixel data type" , get_uint32 (info, offset, &type)); |
157 | check ("offset, pixel data length" , get_uint32 (info, offset + 4, &length)); |
158 | |
159 | check ("pixel data type" , type == 0); |
160 | check ("pixel data length" , offset + 8 + length < info->cache_size); |
161 | |
162 | if (info->flags & CHECK_PIXBUFS) |
163 | { |
164 | GdkPixdata data; |
165 | |
166 | G_GNUC_BEGIN_IGNORE_DEPRECATIONS; |
167 | check ("pixel data" , gdk_pixdata_deserialize (&data, length, |
168 | (const guint8*)info->cache + offset + 8, |
169 | NULL)); |
170 | G_GNUC_END_IGNORE_DEPRECATIONS; |
171 | } |
172 | |
173 | return TRUE; |
174 | } |
175 | |
176 | static gboolean |
177 | check_embedded_rect (CacheInfo *info, |
178 | guint32 offset) |
179 | { |
180 | check ("embedded rect" , offset + 4 < info->cache_size); |
181 | |
182 | return TRUE; |
183 | } |
184 | |
185 | static gboolean |
186 | check_attach_point_list (CacheInfo *info, |
187 | guint32 offset) |
188 | { |
189 | guint32 n_attach_points; |
190 | |
191 | check ("offset, attach point list" , get_uint32 (info, offset, &n_attach_points)); |
192 | check ("attach points" , offset + 4 + 4 * n_attach_points < info->cache_size); |
193 | |
194 | return TRUE; |
195 | } |
196 | |
197 | static gboolean |
198 | check_display_name_list (CacheInfo *info, |
199 | guint32 offset) |
200 | { |
201 | guint32 n_display_names, ofs; |
202 | int i; |
203 | |
204 | check ("offset, display name list" , |
205 | get_uint32 (info, offset, &n_display_names)); |
206 | for (i = 0; i < n_display_names; i++) |
207 | { |
208 | get_uint32(info, offset: offset + 4 + 8 * i, value: &ofs); |
209 | if (!check_string (info, offset: ofs)) |
210 | return FALSE; |
211 | get_uint32(info, offset: offset + 4 + 8 * i + 4, value: &ofs); |
212 | if (!check_string_utf8 (info, offset: ofs)) |
213 | return FALSE; |
214 | } |
215 | |
216 | return TRUE; |
217 | } |
218 | |
219 | static gboolean |
220 | check_meta_data (CacheInfo *info, |
221 | guint32 offset) |
222 | { |
223 | guint32 embedded_rect_offset; |
224 | guint32 attach_point_list_offset; |
225 | guint32 display_name_list_offset; |
226 | |
227 | check ("offset, embedded rect" , |
228 | get_uint32 (info, offset, &embedded_rect_offset)); |
229 | check ("offset, attach point list" , |
230 | get_uint32 (info, offset + 4, &attach_point_list_offset)); |
231 | check ("offset, display name list" , |
232 | get_uint32 (info, offset + 8, &display_name_list_offset)); |
233 | |
234 | if (embedded_rect_offset != 0) |
235 | { |
236 | if (!check_embedded_rect (info, offset: embedded_rect_offset)) |
237 | return FALSE; |
238 | } |
239 | |
240 | if (attach_point_list_offset != 0) |
241 | { |
242 | if (!check_attach_point_list (info, offset: attach_point_list_offset)) |
243 | return FALSE; |
244 | } |
245 | |
246 | if (display_name_list_offset != 0) |
247 | { |
248 | if (!check_display_name_list (info, offset: display_name_list_offset)) |
249 | return FALSE; |
250 | } |
251 | |
252 | return TRUE; |
253 | } |
254 | |
255 | static gboolean |
256 | check_image_data (CacheInfo *info, |
257 | guint32 offset) |
258 | { |
259 | guint32 pixel_data_offset; |
260 | guint32 meta_data_offset; |
261 | |
262 | check ("offset, pixel data" , get_uint32 (info, offset, &pixel_data_offset)); |
263 | check ("offset, meta data" , get_uint32 (info, offset + 4, &meta_data_offset)); |
264 | |
265 | if (pixel_data_offset != 0) |
266 | { |
267 | if (!check_pixel_data (info, offset: pixel_data_offset)) |
268 | return FALSE; |
269 | } |
270 | if (meta_data_offset != 0) |
271 | { |
272 | if (!check_meta_data (info, offset: meta_data_offset)) |
273 | return FALSE; |
274 | } |
275 | |
276 | return TRUE; |
277 | } |
278 | |
279 | static gboolean |
280 | check_image (CacheInfo *info, |
281 | guint32 offset) |
282 | { |
283 | guint16 index; |
284 | guint16 flags; |
285 | guint32 image_data_offset; |
286 | |
287 | check ("offset, image index" , get_uint16 (info, offset, &index)); |
288 | check ("offset, image flags" , get_uint16 (info, offset + 2, &flags)); |
289 | check ("offset, image data offset" , |
290 | get_uint32 (info, offset + 4, &image_data_offset)); |
291 | |
292 | check ("image index" , index < info->n_directories); |
293 | check ("image flags" , flags < 16); |
294 | |
295 | if (image_data_offset != 0) |
296 | { |
297 | if (!check_image_data (info, offset: image_data_offset)) |
298 | return FALSE; |
299 | } |
300 | |
301 | return TRUE; |
302 | } |
303 | |
304 | static gboolean |
305 | check_image_list (CacheInfo *info, |
306 | guint32 offset) |
307 | { |
308 | guint32 n_images; |
309 | int i; |
310 | |
311 | check ("offset, image list" , get_uint32 (info, offset, &n_images)); |
312 | |
313 | for (i = 0; i < n_images; i++) |
314 | { |
315 | if (!check_image (info, offset: offset + 4 + 8 * i)) |
316 | return FALSE; |
317 | } |
318 | |
319 | return TRUE; |
320 | } |
321 | |
322 | static gboolean |
323 | check_icon (CacheInfo *info, |
324 | guint32 offset) |
325 | { |
326 | guint32 chain_offset; |
327 | guint32 name_offset; |
328 | guint32 image_list_offset; |
329 | |
330 | check ("offset, icon chain" , get_uint32 (info, offset, &chain_offset)); |
331 | check ("offset, icon name" , get_uint32 (info, offset + 4, &name_offset)); |
332 | check ("offset, icon image list" , get_uint32 (info, offset + 8, |
333 | &image_list_offset)); |
334 | |
335 | if (!check_string (info, offset: name_offset)) |
336 | return FALSE; |
337 | if (!check_image_list (info, offset: image_list_offset)) |
338 | return FALSE; |
339 | if (chain_offset != 0xffffffff) |
340 | { |
341 | if (!check_icon (info, offset: chain_offset)) |
342 | return FALSE; |
343 | } |
344 | |
345 | return TRUE; |
346 | } |
347 | |
348 | static gboolean |
349 | check_hash (CacheInfo *info, |
350 | guint32 offset) |
351 | { |
352 | guint32 n_buckets, icon_offset; |
353 | int i; |
354 | |
355 | check ("offset, hash size" , get_uint32 (info, offset, &n_buckets)); |
356 | |
357 | for (i = 0; i < n_buckets; i++) |
358 | { |
359 | check ("offset, hash chain" , |
360 | get_uint32 (info, offset + 4 + 4 * i, &icon_offset)); |
361 | if (icon_offset != 0xffffffff) |
362 | { |
363 | if (!check_icon (info, offset: icon_offset)) |
364 | return FALSE; |
365 | } |
366 | } |
367 | |
368 | return TRUE; |
369 | } |
370 | |
371 | /** |
372 | * gtk_icon_cache_validate: |
373 | * @info: a CacheInfo structure |
374 | * |
375 | * Validates the icon cache passed in the @cache and |
376 | * @cache_size fields of the @info structure. The |
377 | * validator checks that offsets specified in the |
378 | * cache do not point outside the mapped area, that |
379 | * strings look reasonable, and that pixbufs can |
380 | * be deserialized. The amount of validation can |
381 | * be controlled with the @flags field. |
382 | * |
383 | * Returns: %TRUE if the cache is valid |
384 | */ |
385 | gboolean |
386 | gtk_icon_cache_validate (CacheInfo *info) |
387 | { |
388 | guint32 hash_offset; |
389 | guint32 directory_list_offset; |
390 | |
391 | if (!check_version (info)) |
392 | return FALSE; |
393 | check ("header, hash offset" , get_uint32 (info, 4, &hash_offset)); |
394 | check ("header, directory list offset" , get_uint32 (info, 8, &directory_list_offset)); |
395 | if (!check_directory_list (info, offset: directory_list_offset)) |
396 | return FALSE; |
397 | |
398 | if (!check_hash (info, offset: hash_offset)) |
399 | return FALSE; |
400 | |
401 | return TRUE; |
402 | } |
403 | |
404 | |