/src/fwupd/libfwupdplugin/fu-mem.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright 2017 Richard Hughes <richard@hughsie.com> |
3 | | * |
4 | | * SPDX-License-Identifier: LGPL-2.1-or-later |
5 | | */ |
6 | | |
7 | 0 | #define G_LOG_DOMAIN "FuCommon" |
8 | | |
9 | | #include "config.h" |
10 | | |
11 | | #include "fwupd-error.h" |
12 | | |
13 | | #include "fu-mem-private.h" |
14 | | #include "fu-string.h" |
15 | | |
16 | | /** |
17 | | * fu_memwrite_uint16: |
18 | | * @buf: a writable buffer |
19 | | * @val_native: a value in host byte-order |
20 | | * @endian: an endian type, e.g. %G_LITTLE_ENDIAN |
21 | | * |
22 | | * Writes a value to a buffer using a specified endian. |
23 | | * |
24 | | * Since: 1.8.2 |
25 | | **/ |
26 | | void |
27 | | fu_memwrite_uint16(guint8 *buf, guint16 val_native, FuEndianType endian) |
28 | 50.4M | { |
29 | 50.4M | guint16 val_hw; |
30 | 50.4M | switch (endian) { |
31 | 495k | case G_BIG_ENDIAN: |
32 | 495k | val_hw = GUINT16_TO_BE(val_native); /* nocheck:blocked */ |
33 | 495k | break; |
34 | 49.9M | case G_LITTLE_ENDIAN: |
35 | 49.9M | val_hw = GUINT16_TO_LE(val_native); /* nocheck:blocked */ |
36 | 49.9M | break; |
37 | 0 | default: |
38 | 0 | val_hw = val_native; |
39 | 0 | break; |
40 | 50.4M | } |
41 | 50.4M | memcpy(buf, &val_hw, sizeof(val_hw)); /* nocheck:blocked */ |
42 | 50.4M | } |
43 | | |
44 | | /** |
45 | | * fu_memwrite_uint24: |
46 | | * @buf: a writable buffer |
47 | | * @val_native: a value in host byte-order |
48 | | * @endian: an endian type, e.g. %G_LITTLE_ENDIAN |
49 | | * |
50 | | * Writes a value to a buffer using a specified endian. |
51 | | * |
52 | | * Since: 1.8.2 |
53 | | **/ |
54 | | void |
55 | | fu_memwrite_uint24(guint8 *buf, guint32 val_native, FuEndianType endian) |
56 | 14.1k | { |
57 | 14.1k | guint32 val_hw; |
58 | 14.1k | switch (endian) { |
59 | 0 | case G_BIG_ENDIAN: |
60 | 0 | val_hw = GUINT32_TO_BE(val_native); /* nocheck:blocked */ |
61 | 0 | memcpy(buf, ((const guint8 *)&val_hw) + 0x1, 0x3); /* nocheck:blocked */ |
62 | 0 | break; |
63 | 14.1k | case G_LITTLE_ENDIAN: |
64 | 14.1k | val_hw = GUINT32_TO_LE(val_native); /* nocheck:blocked */ |
65 | 14.1k | memcpy(buf, &val_hw, 0x3); /* nocheck:blocked */ |
66 | 14.1k | break; |
67 | 0 | default: |
68 | 0 | g_assert_not_reached(); |
69 | 14.1k | } |
70 | 14.1k | } |
71 | | |
72 | | /** |
73 | | * fu_memwrite_uint32: |
74 | | * @buf: a writable buffer |
75 | | * @val_native: a value in host byte-order |
76 | | * @endian: an endian type, e.g. %G_LITTLE_ENDIAN |
77 | | * |
78 | | * Writes a value to a buffer using a specified endian. |
79 | | * |
80 | | * Since: 1.8.2 |
81 | | **/ |
82 | | void |
83 | | fu_memwrite_uint32(guint8 *buf, guint32 val_native, FuEndianType endian) |
84 | 366k | { |
85 | 366k | guint32 val_hw; |
86 | 366k | switch (endian) { |
87 | 264k | case G_BIG_ENDIAN: |
88 | 264k | val_hw = GUINT32_TO_BE(val_native); /* nocheck:blocked */ |
89 | 264k | break; |
90 | 102k | case G_LITTLE_ENDIAN: |
91 | 102k | val_hw = GUINT32_TO_LE(val_native); /* nocheck:blocked */ |
92 | 102k | break; |
93 | 0 | default: |
94 | 0 | val_hw = val_native; |
95 | 0 | break; |
96 | 366k | } |
97 | 366k | memcpy(buf, &val_hw, sizeof(val_hw)); /* nocheck:blocked */ |
98 | 366k | } |
99 | | |
100 | | /** |
101 | | * fu_memwrite_uint64: |
102 | | * @buf: a writable buffer |
103 | | * @val_native: a value in host byte-order |
104 | | * @endian: an endian type, e.g. %G_LITTLE_ENDIAN |
105 | | * |
106 | | * Writes a value to a buffer using a specified endian. |
107 | | * |
108 | | * Since: 1.8.2 |
109 | | **/ |
110 | | void |
111 | | fu_memwrite_uint64(guint8 *buf, guint64 val_native, FuEndianType endian) |
112 | 11.5k | { |
113 | 11.5k | guint64 val_hw; |
114 | 11.5k | switch (endian) { |
115 | 0 | case G_BIG_ENDIAN: |
116 | 0 | val_hw = GUINT64_TO_BE(val_native); /* nocheck:blocked */ |
117 | 0 | break; |
118 | 11.5k | case G_LITTLE_ENDIAN: |
119 | 11.5k | val_hw = GUINT64_TO_LE(val_native); /* nocheck:blocked */ |
120 | 11.5k | break; |
121 | 0 | default: |
122 | 0 | val_hw = val_native; |
123 | 0 | break; |
124 | 11.5k | } |
125 | 11.5k | memcpy(buf, &val_hw, sizeof(val_hw)); /* nocheck:blocked */ |
126 | 11.5k | } |
127 | | |
128 | | /** |
129 | | * fu_memread_uint16: |
130 | | * @buf: a readable buffer |
131 | | * @endian: an endian type, e.g. %G_LITTLE_ENDIAN |
132 | | * |
133 | | * Read a value from a buffer using a specified endian. |
134 | | * |
135 | | * Returns: a value in host byte-order |
136 | | * |
137 | | * Since: 1.8.2 |
138 | | **/ |
139 | | guint16 |
140 | | fu_memread_uint16(const guint8 *buf, FuEndianType endian) |
141 | 91.9M | { |
142 | 91.9M | guint16 val_hw, val_native; |
143 | 91.9M | memcpy(&val_hw, buf, sizeof(val_hw)); /* nocheck:blocked */ |
144 | 91.9M | switch (endian) { |
145 | 12.5k | case G_BIG_ENDIAN: |
146 | 12.5k | val_native = GUINT16_FROM_BE(val_hw); /* nocheck:blocked */ |
147 | 12.5k | break; |
148 | 91.9M | case G_LITTLE_ENDIAN: |
149 | 91.9M | val_native = GUINT16_FROM_LE(val_hw); /* nocheck:blocked */ |
150 | 91.9M | break; |
151 | 0 | default: |
152 | 0 | val_native = val_hw; |
153 | 0 | break; |
154 | 91.9M | } |
155 | 91.9M | return val_native; |
156 | 91.9M | } |
157 | | |
158 | | /** |
159 | | * fu_memread_uint24: |
160 | | * @buf: a readable buffer |
161 | | * @endian: an endian type, e.g. %G_LITTLE_ENDIAN |
162 | | * |
163 | | * Read a value from a buffer using a specified endian. |
164 | | * |
165 | | * Returns: a value in host byte-order |
166 | | * |
167 | | * Since: 1.8.2 |
168 | | **/ |
169 | | guint32 |
170 | | fu_memread_uint24(const guint8 *buf, FuEndianType endian) |
171 | 124k | { |
172 | 124k | guint32 val_hw = 0; |
173 | 124k | guint32 val_native; |
174 | 124k | switch (endian) { |
175 | 0 | case G_BIG_ENDIAN: |
176 | 0 | memcpy(((guint8 *)&val_hw) + 0x1, buf, 0x3); /* nocheck:blocked */ |
177 | 0 | val_native = GUINT32_FROM_BE(val_hw); /* nocheck:blocked */ |
178 | 0 | break; |
179 | 124k | case G_LITTLE_ENDIAN: |
180 | 124k | memcpy(&val_hw, buf, 0x3); /* nocheck:blocked */ |
181 | 124k | val_native = GUINT32_FROM_LE(val_hw); /* nocheck:blocked */ |
182 | 124k | break; |
183 | 0 | default: |
184 | 0 | val_native = val_hw; |
185 | 0 | break; |
186 | 124k | } |
187 | 124k | return val_native; |
188 | 124k | } |
189 | | |
190 | | /** |
191 | | * fu_memread_uint32: |
192 | | * @buf: a readable buffer |
193 | | * @endian: an endian type, e.g. %G_LITTLE_ENDIAN |
194 | | * |
195 | | * Read a value from a buffer using a specified endian. |
196 | | * |
197 | | * Returns: a value in host byte-order |
198 | | * |
199 | | * Since: 1.8.2 |
200 | | **/ |
201 | | guint32 |
202 | | fu_memread_uint32(const guint8 *buf, FuEndianType endian) |
203 | 100M | { |
204 | 100M | guint32 val_hw, val_native; |
205 | 100M | memcpy(&val_hw, buf, sizeof(val_hw)); /* nocheck:blocked */ |
206 | 100M | switch (endian) { |
207 | 23.5M | case G_BIG_ENDIAN: |
208 | 23.5M | val_native = GUINT32_FROM_BE(val_hw); /* nocheck:blocked */ |
209 | 23.5M | break; |
210 | 76.8M | case G_LITTLE_ENDIAN: |
211 | 76.8M | val_native = GUINT32_FROM_LE(val_hw); /* nocheck:blocked */ |
212 | 76.8M | break; |
213 | 0 | default: |
214 | 0 | val_native = val_hw; |
215 | 0 | break; |
216 | 100M | } |
217 | 100M | return val_native; |
218 | 100M | } |
219 | | |
220 | | /** |
221 | | * fu_memread_uint64: |
222 | | * @buf: a readable buffer |
223 | | * @endian: an endian type, e.g. %G_LITTLE_ENDIAN |
224 | | * |
225 | | * Read a value from a buffer using a specified endian. |
226 | | * |
227 | | * Returns: a value in host byte-order |
228 | | * |
229 | | * Since: 1.8.2 |
230 | | **/ |
231 | | guint64 |
232 | | fu_memread_uint64(const guint8 *buf, FuEndianType endian) |
233 | 16.3M | { |
234 | 16.3M | guint64 val_hw, val_native; |
235 | 16.3M | memcpy(&val_hw, buf, sizeof(val_hw)); /* nocheck:blocked */ |
236 | 16.3M | switch (endian) { |
237 | 7.81M | case G_BIG_ENDIAN: |
238 | 7.81M | val_native = GUINT64_FROM_BE(val_hw); /* nocheck:blocked */ |
239 | 7.81M | break; |
240 | 8.56M | case G_LITTLE_ENDIAN: |
241 | 8.56M | val_native = GUINT64_FROM_LE(val_hw); /* nocheck:blocked */ |
242 | 8.56M | break; |
243 | 0 | default: |
244 | 0 | val_native = val_hw; |
245 | 0 | break; |
246 | 16.3M | } |
247 | 16.3M | return val_native; |
248 | 16.3M | } |
249 | | |
250 | | /** |
251 | | * fu_memcmp_safe: |
252 | | * @buf1: a buffer |
253 | | * @buf1_sz: sizeof @buf1 |
254 | | * @buf1_offset: offset into @buf1 |
255 | | * @buf2: another buffer |
256 | | * @buf2_sz: sizeof @buf2 |
257 | | * @buf2_offset: offset into @buf1 |
258 | | * @n: number of bytes to compare from @buf1+@buf1_offset from |
259 | | * @error: (nullable): optional return location for an error |
260 | | * |
261 | | * Compares the buffers for equality. |
262 | | * |
263 | | * Returns: %TRUE if @buf1 and @buf2 are identical |
264 | | * |
265 | | * Since: 1.8.2 |
266 | | **/ |
267 | | gboolean |
268 | | fu_memcmp_safe(const guint8 *buf1, |
269 | | gsize buf1_sz, |
270 | | gsize buf1_offset, |
271 | | const guint8 *buf2, |
272 | | gsize buf2_sz, |
273 | | gsize buf2_offset, |
274 | | gsize n, |
275 | | GError **error) |
276 | 0 | { |
277 | 0 | g_return_val_if_fail(buf1 != NULL, FALSE); |
278 | 0 | g_return_val_if_fail(buf2 != NULL, FALSE); |
279 | 0 | g_return_val_if_fail(error == NULL || *error == NULL, FALSE); |
280 | | |
281 | 0 | if (!fu_memchk_read(buf1_sz, buf1_offset, n, error)) |
282 | 0 | return FALSE; |
283 | 0 | if (!fu_memchk_read(buf2_sz, buf2_offset, n, error)) |
284 | 0 | return FALSE; |
285 | | |
286 | | /* check matches */ |
287 | 0 | for (guint i = 0x0; i < n; i++) { |
288 | 0 | if (buf1[buf1_offset + i] != buf2[buf2_offset + i]) { |
289 | 0 | g_set_error(error, |
290 | 0 | FWUPD_ERROR, |
291 | 0 | FWUPD_ERROR_INVALID_DATA, |
292 | 0 | "got 0x%02x, expected 0x%02x @ 0x%04x", |
293 | 0 | buf1[buf1_offset + i], |
294 | 0 | buf2[buf2_offset + i], |
295 | 0 | i); |
296 | 0 | return FALSE; |
297 | 0 | } |
298 | 0 | } |
299 | | |
300 | | /* success */ |
301 | 0 | return TRUE; |
302 | 0 | } |
303 | | |
304 | | /** |
305 | | * fu_memchk_read: |
306 | | * @bufsz: maximum size of a buffer, typically `sizeof(buf)` |
307 | | * @offset: offset in bytes |
308 | | * @n: number of bytes |
309 | | * @error: (nullable): optional return location for an error |
310 | | * |
311 | | * Works out if reading from a buffer is safe. Providing the buffer sizes allows us to check for |
312 | | * buffer overflow. |
313 | | * |
314 | | * You don't need to use this function in "obviously correct" cases, nor should |
315 | | * you use it when performance is a concern. Only us it when you're not sure if |
316 | | * malicious data from a device or firmware could cause memory corruption. |
317 | | * |
318 | | * Returns: %TRUE if the access is safe, %FALSE otherwise |
319 | | * |
320 | | * Since: 1.9.1 |
321 | | **/ |
322 | | gboolean |
323 | | fu_memchk_read(gsize bufsz, gsize offset, gsize n, GError **error) |
324 | 14.3M | { |
325 | 14.3M | g_return_val_if_fail(error == NULL || *error == NULL, FALSE); |
326 | 14.3M | if (n == 0) |
327 | 0 | return TRUE; |
328 | 14.3M | if (n > bufsz) { |
329 | 353 | g_set_error(error, |
330 | 353 | FWUPD_ERROR, |
331 | 353 | FWUPD_ERROR_READ, |
332 | 353 | "attempted to read 0x%02x bytes from buffer of 0x%02x", |
333 | 353 | (guint)n, |
334 | 353 | (guint)bufsz); |
335 | 353 | return FALSE; |
336 | 353 | } |
337 | 14.3M | if (offset > bufsz || n + offset > bufsz) { |
338 | 1.12k | g_set_error(error, |
339 | 1.12k | FWUPD_ERROR, |
340 | 1.12k | FWUPD_ERROR_READ, |
341 | 1.12k | "attempted to read 0x%02x bytes at offset 0x%02x from buffer of 0x%02x", |
342 | 1.12k | (guint)n, |
343 | 1.12k | (guint)offset, |
344 | 1.12k | (guint)bufsz); |
345 | 1.12k | return FALSE; |
346 | 1.12k | } |
347 | 14.3M | return TRUE; |
348 | 14.3M | } |
349 | | |
350 | | /** |
351 | | * fu_memchk_write: |
352 | | * @bufsz: maximum size of a buffer, typically `sizeof(buf)` |
353 | | * @offset: offset in bytes |
354 | | * @n: number of bytes |
355 | | * @error: (nullable): optional return location for an error |
356 | | * |
357 | | * Works out if writing to a buffer is safe. Providing the buffer sizes allows us to check for |
358 | | * buffer overflow. |
359 | | * |
360 | | * You don't need to use this function in "obviously correct" cases, nor should |
361 | | * you use it when performance is a concern. Only us it when you're not sure if |
362 | | * malicious data from a device or firmware could cause memory corruption. |
363 | | * |
364 | | * Returns: %TRUE if the access is safe, %FALSE otherwise |
365 | | * |
366 | | * Since: 1.9.1 |
367 | | **/ |
368 | | gboolean |
369 | | fu_memchk_write(gsize bufsz, gsize offset, gsize n, GError **error) |
370 | 31.1M | { |
371 | 31.1M | g_return_val_if_fail(error == NULL || *error == NULL, FALSE); |
372 | 31.1M | if (n == 0) |
373 | 0 | return TRUE; |
374 | 31.1M | if (n > bufsz) { |
375 | 0 | g_set_error(error, |
376 | 0 | FWUPD_ERROR, |
377 | 0 | FWUPD_ERROR_WRITE, |
378 | 0 | "attempted to write 0x%02x bytes to buffer of 0x%02x", |
379 | 0 | (guint)n, |
380 | 0 | (guint)bufsz); |
381 | 0 | return FALSE; |
382 | 0 | } |
383 | 31.1M | if (offset > bufsz || n + offset > bufsz) { |
384 | 60 | g_set_error(error, |
385 | 60 | FWUPD_ERROR, |
386 | 60 | FWUPD_ERROR_WRITE, |
387 | 60 | "attempted to write 0x%02x bytes at offset 0x%02x to buffer of 0x%02x", |
388 | 60 | (guint)n, |
389 | 60 | (guint)offset, |
390 | 60 | (guint)bufsz); |
391 | 60 | return FALSE; |
392 | 60 | } |
393 | 31.1M | return TRUE; |
394 | 31.1M | } |
395 | | |
396 | | /** |
397 | | * fu_memcpy_safe: |
398 | | * @dst: destination buffer |
399 | | * @dst_sz: maximum size of @dst, typically `sizeof(dst)` |
400 | | * @dst_offset: offset in bytes into @dst to copy to |
401 | | * @src: source buffer |
402 | | * @src_sz: maximum size of @dst, typically `sizeof(src)` |
403 | | * @src_offset: offset in bytes into @src to copy from |
404 | | * @n: number of bytes to copy from @src+@offset from |
405 | | * @error: (nullable): optional return location for an error |
406 | | * |
407 | | * Copies some memory using memcpy in a safe way. Providing the buffer sizes |
408 | | * of both the destination and the source allows us to check for buffer overflow. |
409 | | * |
410 | | * Providing the buffer offsets also allows us to check reading past the end of |
411 | | * the source buffer. For this reason the caller should NEVER add an offset to |
412 | | * @src or @dst. |
413 | | * |
414 | | * You don't need to use this function in "obviously correct" cases, nor should |
415 | | * you use it when performance is a concern. Only us it when you're not sure if |
416 | | * malicious data from a device or firmware could cause memory corruption. |
417 | | * |
418 | | * Returns: %TRUE if the bytes were copied, %FALSE otherwise |
419 | | * |
420 | | * Since: 1.8.2 |
421 | | **/ |
422 | | gboolean |
423 | | fu_memcpy_safe(guint8 *dst, |
424 | | gsize dst_sz, |
425 | | gsize dst_offset, |
426 | | const guint8 *src, |
427 | | gsize src_sz, |
428 | | gsize src_offset, |
429 | | gsize n, |
430 | | GError **error) |
431 | 14.1M | { |
432 | 14.1M | g_return_val_if_fail(dst != NULL, FALSE); |
433 | 14.1M | g_return_val_if_fail(src != NULL, FALSE); |
434 | 14.1M | g_return_val_if_fail(error == NULL || *error == NULL, FALSE); |
435 | | |
436 | 14.1M | if (!fu_memchk_read(src_sz, src_offset, n, error)) |
437 | 1.36k | return FALSE; |
438 | 14.1M | if (!fu_memchk_write(dst_sz, dst_offset, n, error)) |
439 | 60 | return FALSE; |
440 | 14.1M | memcpy(dst + dst_offset, src + src_offset, n); /* nocheck:blocked */ |
441 | 14.1M | return TRUE; |
442 | 14.1M | } |
443 | | |
444 | | /** |
445 | | * fu_memmem_safe: |
446 | | * @haystack: destination buffer |
447 | | * @haystack_sz: maximum size of @haystack, typically `sizeof(haystack)` |
448 | | * @needle: source buffer |
449 | | * @needle_sz: maximum size of @haystack, typically `sizeof(needle)` |
450 | | * @offset: (out) (nullable): offset in bytes @needle has been found in @haystack |
451 | | * @error: (nullable): optional return location for an error |
452 | | * |
453 | | * Finds a block of memory in another block of memory in a safe way. |
454 | | * |
455 | | * Returns: %TRUE if the needle was found in the haystack, %FALSE otherwise |
456 | | * |
457 | | * Since: 1.8.2 |
458 | | **/ |
459 | | gboolean |
460 | | fu_memmem_safe(const guint8 *haystack, |
461 | | gsize haystack_sz, |
462 | | const guint8 *needle, |
463 | | gsize needle_sz, |
464 | | gsize *offset, |
465 | | GError **error) |
466 | 1.15k | { |
467 | | #ifdef HAVE_MEMMEM |
468 | | const guint8 *tmp; |
469 | | #endif |
470 | 1.15k | g_return_val_if_fail(haystack != NULL, FALSE); |
471 | 1.15k | g_return_val_if_fail(needle != NULL, FALSE); |
472 | 1.15k | g_return_val_if_fail(error == NULL || *error == NULL, FALSE); |
473 | | |
474 | | /* nothing to find */ |
475 | 1.15k | if (needle_sz == 0) { |
476 | 0 | if (offset != NULL) |
477 | 0 | *offset = 0; |
478 | 0 | return TRUE; |
479 | 0 | } |
480 | | |
481 | | /* impossible */ |
482 | 1.15k | if (needle_sz > haystack_sz) { |
483 | 23 | g_set_error(error, |
484 | 23 | FWUPD_ERROR, |
485 | 23 | FWUPD_ERROR_NOT_FOUND, |
486 | 23 | "needle of 0x%02x bytes is larger than haystack of 0x%02x bytes", |
487 | 23 | (guint)needle_sz, |
488 | 23 | (guint)haystack_sz); |
489 | 23 | return FALSE; |
490 | 23 | } |
491 | | |
492 | | #ifdef HAVE_MEMMEM |
493 | | /* trust glibc to do a binary or linear search as appropriate */ |
494 | | tmp = memmem(haystack, haystack_sz, needle, needle_sz); |
495 | | if (tmp != NULL) { |
496 | | if (offset != NULL) |
497 | | *offset = tmp - haystack; |
498 | | return TRUE; |
499 | | } |
500 | | #else |
501 | 3.39M | for (gsize i = 0; i < haystack_sz - needle_sz; i++) { |
502 | 3.39M | if (memcmp(haystack + i, needle, needle_sz) == 0) { |
503 | 850 | if (offset != NULL) |
504 | 850 | *offset = i; |
505 | 850 | return TRUE; |
506 | 850 | } |
507 | 3.39M | } |
508 | 281 | #endif |
509 | | |
510 | | /* not found */ |
511 | 281 | g_set_error(error, |
512 | 281 | FWUPD_ERROR, |
513 | 281 | FWUPD_ERROR_NOT_FOUND, |
514 | 281 | "needle of 0x%02x bytes was not found in haystack of 0x%02x bytes", |
515 | 281 | (guint)needle_sz, |
516 | 281 | (guint)haystack_sz); |
517 | 281 | return FALSE; |
518 | 1.13k | } |
519 | | |
520 | | /** |
521 | | * fu_memdup_safe: |
522 | | * @src: (nullable): source buffer |
523 | | * @n: number of bytes to copy from @src |
524 | | * @error: (nullable): optional return location for an error |
525 | | * |
526 | | * Duplicates some memory using memdup in a safe way. |
527 | | * |
528 | | * You don't need to use this function in "obviously correct" cases, nor should |
529 | | * you use it when performance is a concern. Only us it when you're not sure if |
530 | | * malicious data from a device or firmware could cause memory corruption. |
531 | | * |
532 | | * NOTE: This function intentionally limits allocation size to 1GB. |
533 | | * |
534 | | * Returns: (transfer full): block of allocated memory, or %NULL for an error. |
535 | | * |
536 | | * Since: 1.8.2 |
537 | | **/ |
538 | | guint8 * |
539 | | fu_memdup_safe(const guint8 *src, gsize n, GError **error) |
540 | 0 | { |
541 | | /* sanity check */ |
542 | 0 | if (n > 0x40000000) { |
543 | 0 | g_set_error(error, |
544 | 0 | FWUPD_ERROR, |
545 | 0 | FWUPD_ERROR_NOT_SUPPORTED, |
546 | 0 | "cannot allocate %uGB of memory", |
547 | 0 | (guint)(n / 0x40000000)); |
548 | 0 | return NULL; |
549 | 0 | } |
550 | | |
551 | | /* linear block of memory */ |
552 | 0 | return g_memdup2(src, n); |
553 | 0 | } |
554 | | |
555 | | /** |
556 | | * fu_memread_uint8_safe: |
557 | | * @buf: source buffer |
558 | | * @bufsz: maximum size of @buf, typically `sizeof(buf)` |
559 | | * @offset: offset in bytes into @buf to copy from |
560 | | * @value: (out) (nullable): the parsed value |
561 | | * @error: (nullable): optional return location for an error |
562 | | * |
563 | | * Read a value from a buffer in a safe way. |
564 | | * |
565 | | * You don't need to use this function in "obviously correct" cases, nor should |
566 | | * you use it when performance is a concern. Only us it when you're not sure if |
567 | | * malicious data from a device or firmware could cause memory corruption. |
568 | | * |
569 | | * Returns: %TRUE if @value was set, %FALSE otherwise |
570 | | * |
571 | | * Since: 1.8.2 |
572 | | **/ |
573 | | gboolean |
574 | | fu_memread_uint8_safe(const guint8 *buf, gsize bufsz, gsize offset, guint8 *value, GError **error) |
575 | 6.38k | { |
576 | 6.38k | guint8 tmp; |
577 | | |
578 | 6.38k | g_return_val_if_fail(buf != NULL, FALSE); |
579 | 6.38k | g_return_val_if_fail(error == NULL || *error == NULL, FALSE); |
580 | | |
581 | 6.38k | if (!fu_memcpy_safe(&tmp, |
582 | 6.38k | sizeof(tmp), |
583 | 6.38k | 0x0, /* dst */ |
584 | 6.38k | buf, |
585 | 6.38k | bufsz, |
586 | 6.38k | offset, /* src */ |
587 | 6.38k | sizeof(tmp), |
588 | 6.38k | error)) |
589 | 0 | return FALSE; |
590 | 6.38k | if (value != NULL) |
591 | 6.38k | *value = tmp; |
592 | 6.38k | return TRUE; |
593 | 6.38k | } |
594 | | |
595 | | /** |
596 | | * fu_memread_uint16_safe: |
597 | | * @buf: source buffer |
598 | | * @bufsz: maximum size of @buf, typically `sizeof(buf)` |
599 | | * @offset: offset in bytes into @buf to copy from |
600 | | * @value: (out) (nullable): the parsed value |
601 | | * @endian: an endian type, e.g. %G_LITTLE_ENDIAN |
602 | | * @error: (nullable): optional return location for an error |
603 | | * |
604 | | * Read a value from a buffer using a specified endian in a safe way. |
605 | | * |
606 | | * You don't need to use this function in "obviously correct" cases, nor should |
607 | | * you use it when performance is a concern. Only us it when you're not sure if |
608 | | * malicious data from a device or firmware could cause memory corruption. |
609 | | * |
610 | | * Returns: %TRUE if @value was set, %FALSE otherwise |
611 | | * |
612 | | * Since: 1.8.2 |
613 | | **/ |
614 | | gboolean |
615 | | fu_memread_uint16_safe(const guint8 *buf, |
616 | | gsize bufsz, |
617 | | gsize offset, |
618 | | guint16 *value, |
619 | | FuEndianType endian, |
620 | | GError **error) |
621 | 30.6k | { |
622 | 30.6k | guint8 dst[2] = {0x0}; |
623 | | |
624 | 30.6k | g_return_val_if_fail(buf != NULL, FALSE); |
625 | 30.6k | g_return_val_if_fail(error == NULL || *error == NULL, FALSE); |
626 | | |
627 | 30.6k | if (!fu_memcpy_safe(dst, |
628 | 30.6k | sizeof(dst), |
629 | 30.6k | 0x0, /* dst */ |
630 | 30.6k | buf, |
631 | 30.6k | bufsz, |
632 | 30.6k | offset, /* src */ |
633 | 30.6k | sizeof(dst), |
634 | 30.6k | error)) |
635 | 12 | return FALSE; |
636 | 30.6k | if (value != NULL) |
637 | 30.6k | *value = fu_memread_uint16(dst, endian); |
638 | 30.6k | return TRUE; |
639 | 30.6k | } |
640 | | |
641 | | /** |
642 | | * fu_memread_uint24_safe: |
643 | | * @buf: source buffer |
644 | | * @bufsz: maximum size of @buf, typically `sizeof(buf)` |
645 | | * @offset: offset in bytes into @buf to copy from |
646 | | * @value: (out) (nullable): the parsed value |
647 | | * @endian: an endian type, e.g. %G_LITTLE_ENDIAN |
648 | | * @error: (nullable): optional return location for an error |
649 | | * |
650 | | * Read a value from a buffer using a specified endian in a safe way. |
651 | | * |
652 | | * You don't need to use this function in "obviously correct" cases, nor should |
653 | | * you use it when performance is a concern. Only us it when you're not sure if |
654 | | * malicious data from a device or firmware could cause memory corruption. |
655 | | * |
656 | | * Returns: %TRUE if @value was set, %FALSE otherwise |
657 | | * |
658 | | * Since: 1.8.3 |
659 | | **/ |
660 | | gboolean |
661 | | fu_memread_uint24_safe(const guint8 *buf, |
662 | | gsize bufsz, |
663 | | gsize offset, |
664 | | guint32 *value, |
665 | | FuEndianType endian, |
666 | | GError **error) |
667 | 0 | { |
668 | 0 | guint8 dst[3] = {0x0}; |
669 | |
|
670 | 0 | g_return_val_if_fail(buf != NULL, FALSE); |
671 | 0 | g_return_val_if_fail(error == NULL || *error == NULL, FALSE); |
672 | | |
673 | 0 | if (!fu_memcpy_safe(dst, |
674 | 0 | sizeof(dst), |
675 | 0 | 0x0, /* dst */ |
676 | 0 | buf, |
677 | 0 | bufsz, |
678 | 0 | offset, /* src */ |
679 | 0 | sizeof(dst), |
680 | 0 | error)) |
681 | 0 | return FALSE; |
682 | 0 | if (value != NULL) |
683 | 0 | *value = fu_memread_uint24(dst, endian); |
684 | 0 | return TRUE; |
685 | 0 | } |
686 | | |
687 | | /** |
688 | | * fu_memread_uint32_safe: |
689 | | * @buf: source buffer |
690 | | * @bufsz: maximum size of @buf, typically `sizeof(buf)` |
691 | | * @offset: offset in bytes into @buf to copy from |
692 | | * @value: (out) (nullable): the parsed value |
693 | | * @endian: an endian type, e.g. %G_LITTLE_ENDIAN |
694 | | * @error: (nullable): optional return location for an error |
695 | | * |
696 | | * Read a value from a buffer using a specified endian in a safe way. |
697 | | * |
698 | | * You don't need to use this function in "obviously correct" cases, nor should |
699 | | * you use it when performance is a concern. Only us it when you're not sure if |
700 | | * malicious data from a device or firmware could cause memory corruption. |
701 | | * |
702 | | * Returns: %TRUE if @value was set, %FALSE otherwise |
703 | | * |
704 | | * Since: 1.8.2 |
705 | | **/ |
706 | | gboolean |
707 | | fu_memread_uint32_safe(const guint8 *buf, |
708 | | gsize bufsz, |
709 | | gsize offset, |
710 | | guint32 *value, |
711 | | FuEndianType endian, |
712 | | GError **error) |
713 | 485k | { |
714 | 485k | guint8 dst[4] = {0x0}; |
715 | | |
716 | 485k | g_return_val_if_fail(buf != NULL, FALSE); |
717 | 485k | g_return_val_if_fail(error == NULL || *error == NULL, FALSE); |
718 | | |
719 | 485k | if (!fu_memcpy_safe(dst, |
720 | 485k | sizeof(dst), |
721 | 485k | 0x0, /* dst */ |
722 | 485k | buf, |
723 | 485k | bufsz, |
724 | 485k | offset, /* src */ |
725 | 485k | sizeof(dst), |
726 | 485k | error)) |
727 | 353 | return FALSE; |
728 | 485k | if (value != NULL) |
729 | 485k | *value = fu_memread_uint32(dst, endian); |
730 | 485k | return TRUE; |
731 | 485k | } |
732 | | |
733 | | /** |
734 | | * fu_memread_uint64_safe: |
735 | | * @buf: source buffer |
736 | | * @bufsz: maximum size of @buf, typically `sizeof(buf)` |
737 | | * @offset: offset in bytes into @buf to copy from |
738 | | * @value: (out) (nullable): the parsed value |
739 | | * @endian: an endian type, e.g. %G_LITTLE_ENDIAN |
740 | | * @error: (nullable): optional return location for an error |
741 | | * |
742 | | * Read a value from a buffer using a specified endian in a safe way. |
743 | | * |
744 | | * You don't need to use this function in "obviously correct" cases, nor should |
745 | | * you use it when performance is a concern. Only us it when you're not sure if |
746 | | * malicious data from a device or firmware could cause memory corruption. |
747 | | * |
748 | | * Returns: %TRUE if @value was set, %FALSE otherwise |
749 | | * |
750 | | * Since: 1.8.2 |
751 | | **/ |
752 | | gboolean |
753 | | fu_memread_uint64_safe(const guint8 *buf, |
754 | | gsize bufsz, |
755 | | gsize offset, |
756 | | guint64 *value, |
757 | | FuEndianType endian, |
758 | | GError **error) |
759 | 0 | { |
760 | 0 | guint8 dst[8] = {0x0}; |
761 | |
|
762 | 0 | g_return_val_if_fail(buf != NULL, FALSE); |
763 | 0 | g_return_val_if_fail(error == NULL || *error == NULL, FALSE); |
764 | | |
765 | 0 | if (!fu_memcpy_safe(dst, |
766 | 0 | sizeof(dst), |
767 | 0 | 0x0, /* dst */ |
768 | 0 | buf, |
769 | 0 | bufsz, |
770 | 0 | offset, /* src */ |
771 | 0 | sizeof(dst), |
772 | 0 | error)) |
773 | 0 | return FALSE; |
774 | 0 | if (value != NULL) |
775 | 0 | *value = fu_memread_uint64(dst, endian); |
776 | 0 | return TRUE; |
777 | 0 | } |
778 | | |
779 | | /** |
780 | | * fu_memwrite_uint8_safe: |
781 | | * @buf: source buffer |
782 | | * @bufsz: maximum size of @buf, typically `sizeof(buf)` |
783 | | * @offset: offset in bytes into @buf to write to |
784 | | * @value: the value to write |
785 | | * @error: (nullable): optional return location for an error |
786 | | * |
787 | | * Write a value to a buffer in a safe way. |
788 | | * |
789 | | * You don't need to use this function in "obviously correct" cases, nor should |
790 | | * you use it when performance is a concern. Only us it when you're not sure if |
791 | | * malicious data from a device or firmware could cause memory corruption. |
792 | | * |
793 | | * Returns: %TRUE if @value was written, %FALSE otherwise |
794 | | * |
795 | | * Since: 1.8.2 |
796 | | **/ |
797 | | gboolean |
798 | | fu_memwrite_uint8_safe(guint8 *buf, gsize bufsz, gsize offset, guint8 value, GError **error) |
799 | 0 | { |
800 | 0 | g_return_val_if_fail(buf != NULL, FALSE); |
801 | 0 | g_return_val_if_fail(error == NULL || *error == NULL, FALSE); |
802 | | |
803 | 0 | return fu_memcpy_safe(buf, |
804 | 0 | bufsz, |
805 | 0 | offset, /* dst */ |
806 | 0 | &value, |
807 | 0 | sizeof(value), |
808 | 0 | 0x0, /* src */ |
809 | 0 | sizeof(value), |
810 | 0 | error); |
811 | 0 | } |
812 | | |
813 | | /** |
814 | | * fu_memwrite_uint16_safe: |
815 | | * @buf: source buffer |
816 | | * @bufsz: maximum size of @buf, typically `sizeof(buf)` |
817 | | * @offset: offset in bytes into @buf to write to |
818 | | * @value: the value to write |
819 | | * @endian: an endian type, e.g. %G_LITTLE_ENDIAN |
820 | | * @error: (nullable): optional return location for an error |
821 | | * |
822 | | * Write a value to a buffer using a specified endian in a safe way. |
823 | | * |
824 | | * You don't need to use this function in "obviously correct" cases, nor should |
825 | | * you use it when performance is a concern. Only us it when you're not sure if |
826 | | * malicious data from a device or firmware could cause memory corruption. |
827 | | * |
828 | | * Returns: %TRUE if @value was written, %FALSE otherwise |
829 | | * |
830 | | * Since: 1.8.2 |
831 | | **/ |
832 | | gboolean |
833 | | fu_memwrite_uint16_safe(guint8 *buf, |
834 | | gsize bufsz, |
835 | | gsize offset, |
836 | | guint16 value, |
837 | | FuEndianType endian, |
838 | | GError **error) |
839 | 373 | { |
840 | 373 | guint8 tmp[2] = {0x0}; |
841 | | |
842 | 373 | g_return_val_if_fail(buf != NULL, FALSE); |
843 | 373 | g_return_val_if_fail(error == NULL || *error == NULL, FALSE); |
844 | | |
845 | 373 | fu_memwrite_uint16(tmp, value, endian); |
846 | 373 | return fu_memcpy_safe(buf, |
847 | 373 | bufsz, |
848 | 373 | offset, /* dst */ |
849 | 373 | tmp, |
850 | 373 | sizeof(tmp), |
851 | 373 | 0x0, /* src */ |
852 | 373 | sizeof(tmp), |
853 | 373 | error); |
854 | 373 | } |
855 | | |
856 | | /** |
857 | | * fu_memwrite_uint32_safe: |
858 | | * @buf: source buffer |
859 | | * @bufsz: maximum size of @buf, typically `sizeof(buf)` |
860 | | * @offset: offset in bytes into @buf to write to |
861 | | * @value: the value to write |
862 | | * @endian: an endian type, e.g. %G_LITTLE_ENDIAN |
863 | | * @error: (nullable): optional return location for an error |
864 | | * |
865 | | * Write a value to a buffer using a specified endian in a safe way. |
866 | | * |
867 | | * You don't need to use this function in "obviously correct" cases, nor should |
868 | | * you use it when performance is a concern. Only us it when you're not sure if |
869 | | * malicious data from a device or firmware could cause memory corruption. |
870 | | * |
871 | | * Returns: %TRUE if @value was written, %FALSE otherwise |
872 | | * |
873 | | * Since: 1.8.2 |
874 | | **/ |
875 | | gboolean |
876 | | fu_memwrite_uint32_safe(guint8 *buf, |
877 | | gsize bufsz, |
878 | | gsize offset, |
879 | | guint32 value, |
880 | | FuEndianType endian, |
881 | | GError **error) |
882 | 387 | { |
883 | 387 | guint8 tmp[4] = {0x0}; |
884 | | |
885 | 387 | g_return_val_if_fail(buf != NULL, FALSE); |
886 | 387 | g_return_val_if_fail(error == NULL || *error == NULL, FALSE); |
887 | | |
888 | 387 | fu_memwrite_uint32(tmp, value, endian); |
889 | 387 | return fu_memcpy_safe(buf, |
890 | 387 | bufsz, |
891 | 387 | offset, /* dst */ |
892 | 387 | tmp, |
893 | 387 | sizeof(tmp), |
894 | 387 | 0x0, /* src */ |
895 | 387 | sizeof(tmp), |
896 | 387 | error); |
897 | 387 | } |
898 | | |
899 | | /** |
900 | | * fu_memwrite_uint64_safe: |
901 | | * @buf: source buffer |
902 | | * @bufsz: maximum size of @buf, typically `sizeof(buf)` |
903 | | * @offset: offset in bytes into @buf to write to |
904 | | * @value: the value to write |
905 | | * @endian: an endian type, e.g. %G_LITTLE_ENDIAN |
906 | | * @error: (nullable): optional return location for an error |
907 | | * |
908 | | * Write a value to a buffer using a specified endian in a safe way. |
909 | | * |
910 | | * You don't need to use this function in "obviously correct" cases, nor should |
911 | | * you use it when performance is a concern. Only us it when you're not sure if |
912 | | * malicious data from a device or firmware could cause memory corruption. |
913 | | * |
914 | | * Returns: %TRUE if @value was written, %FALSE otherwise |
915 | | * |
916 | | * Since: 1.8.2 |
917 | | **/ |
918 | | gboolean |
919 | | fu_memwrite_uint64_safe(guint8 *buf, |
920 | | gsize bufsz, |
921 | | gsize offset, |
922 | | guint64 value, |
923 | | FuEndianType endian, |
924 | | GError **error) |
925 | 0 | { |
926 | 0 | guint8 tmp[8] = {0x0}; |
927 | |
|
928 | 0 | g_return_val_if_fail(buf != NULL, FALSE); |
929 | 0 | g_return_val_if_fail(error == NULL || *error == NULL, FALSE); |
930 | | |
931 | 0 | fu_memwrite_uint64(tmp, value, endian); |
932 | 0 | return fu_memcpy_safe(buf, |
933 | 0 | bufsz, |
934 | 0 | offset, /* dst */ |
935 | 0 | tmp, |
936 | 0 | sizeof(tmp), |
937 | 0 | 0x0, /* src */ |
938 | 0 | sizeof(tmp), |
939 | 0 | error); |
940 | 0 | } |
941 | | |
942 | | /** |
943 | | * fu_memstrsafe: |
944 | | * @buf: source buffer |
945 | | * @bufsz: maximum size of @buf, typically `sizeof(buf)` |
946 | | * @offset: offset in bytes into @buf to read from |
947 | | * @maxsz: maximum size of returned string |
948 | | * @error: (nullable): optional return location for an error |
949 | | * |
950 | | * Converts a byte buffer to a ASCII string. |
951 | | * |
952 | | * Returns: (transfer full): a string, or %NULL on error |
953 | | * |
954 | | * Since: 1.9.3 |
955 | | **/ |
956 | | gchar * |
957 | | fu_memstrsafe(const guint8 *buf, gsize bufsz, gsize offset, gsize maxsz, GError **error) |
958 | 132k | { |
959 | 132k | g_autofree gchar *str = NULL; |
960 | | |
961 | 132k | g_return_val_if_fail(buf != NULL, NULL); |
962 | 132k | g_return_val_if_fail(error == NULL || *error == NULL, NULL); |
963 | | |
964 | 132k | if (!fu_memchk_read(bufsz, offset, maxsz, error)) |
965 | 13 | return NULL; |
966 | 132k | str = fu_strsafe((const gchar *)buf + offset, maxsz); |
967 | 132k | if (str == NULL) { |
968 | 57.2k | g_set_error_literal(error, |
969 | 57.2k | FWUPD_ERROR, |
970 | 57.2k | FWUPD_ERROR_INVALID_DATA, |
971 | 57.2k | "invalid ASCII string"); |
972 | 57.2k | return NULL; |
973 | 57.2k | } |
974 | 74.9k | return g_steal_pointer(&str); |
975 | 132k | } |