diff --git a/libfwupdplugin/fu-common.c b/libfwupdplugin/fu-common.c index 5705401db..dd1920e93 100644 --- a/libfwupdplugin/fu-common.c +++ b/libfwupdplugin/fu-common.c @@ -2328,6 +2328,82 @@ fu_memcpy_safe(guint8 *dst, return TRUE; } +/** + * fu_memmem_safe: + * @haystack: destination buffer + * @haystack_sz: maximum size of @haystack, typically `sizeof(haystack)` + * @needle: source buffer + * @needle_sz: maximum size of @haystack, typically `sizeof(needle)` + * @offset: (out) (nullable): offset in bytes @needle has been found in @haystack + * @error: (nullable): optional return location for an error + * + * Finds a block of memory in another block of memory in a safe way. + * + * Returns: %TRUE if the needle was found in the haystack, %FALSE otherwise + * + * Since: 1.7.4 + **/ +gboolean +fu_memmem_safe(const guint8 *haystack, + gsize haystack_sz, + const guint8 *needle, + gsize needle_sz, + gsize *offset, + GError **error) +{ +#ifdef HAVE_MEMMEM + const guint8 *tmp; +#endif + g_return_val_if_fail(haystack != NULL, FALSE); + g_return_val_if_fail(needle != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* nothing to find */ + if (needle_sz == 0) { + if (offset != NULL) + *offset = 0; + return TRUE; + } + + /* impossible */ + if (needle_sz > haystack_sz) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "needle of 0x%02x bytes is larger than haystack of 0x%02x bytes", + (guint)needle_sz, + (guint)haystack_sz); + return FALSE; + } + +#ifdef HAVE_MEMMEM + /* trust glibc to do a binary or linear search as appropriate */ + tmp = memmem(haystack, haystack_sz, needle, needle_sz); + if (tmp != NULL) { + if (offset != NULL) + *offset = tmp - haystack; + return TRUE; + } +#else + for (gsize i = 0; i < haystack_sz - needle_sz; i++) { + if (memcmp(haystack + i, needle, needle_sz) == 0) { + if (offset != NULL) + *offset = i; + return TRUE; + } + } +#endif + + /* not found */ + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "needle of 0x%02x bytes was not found in haystack of 0x%02x bytes", + (guint)needle_sz, + (guint)haystack_sz); + return FALSE; +} + /** * fu_memdup_safe: * @src: source buffer diff --git a/libfwupdplugin/fu-common.h b/libfwupdplugin/fu-common.h index 66ea05da7..c82cef79e 100644 --- a/libfwupdplugin/fu-common.h +++ b/libfwupdplugin/fu-common.h @@ -263,6 +263,13 @@ fu_memcpy_safe(guint8 *dst, gsize n, GError **error) G_GNUC_WARN_UNUSED_RESULT; gboolean +fu_memmem_safe(const guint8 *haystack, + gsize haystack_sz, + const guint8 *needle, + gsize needle_sz, + gsize *offset, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean fu_common_read_uint8_safe(const guint8 *buf, gsize bufsz, gsize offset, diff --git a/libfwupdplugin/fu-fmap-firmware.c b/libfwupdplugin/fu-fmap-firmware.c index 1d1cab89f..adf144f8b 100644 --- a/libfwupdplugin/fu-fmap-firmware.c +++ b/libfwupdplugin/fu-fmap-firmware.c @@ -6,8 +6,6 @@ #include "config.h" -#include - #include "fu-common.h" #include "fu-fmap-firmware.h" @@ -24,31 +22,6 @@ G_DEFINE_TYPE(FuFmapFirmware, fu_fmap_firmware, FU_TYPE_FIRMWARE) -static gboolean -fu_fmap_firmware_find_offset(FuFmapFirmware *self, const guint8 *buf, gsize bufsz, GError **error) -{ -#ifdef HAVE_MEMMEM - const guint8 *tmp; - - g_return_val_if_fail(buf != NULL, FALSE); - - /* trust glibc to do a binary or linear search as appropriate */ - tmp = memmem(buf, bufsz, FMAP_SIGNATURE, 8); - if (tmp == NULL) { - g_set_error_literal(error, - G_IO_ERROR, - G_IO_ERROR_INVALID_DATA, - "fmap header not found"); - return FALSE; - } - fu_firmware_set_offset(FU_FIRMWARE(self), tmp - buf); - return TRUE; -#else - g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "memmem() not available"); - return FALSE; -#endif -} - static gboolean fu_fmap_firmware_parse(FuFirmware *firmware, GBytes *fw, @@ -57,7 +30,6 @@ fu_fmap_firmware_parse(FuFirmware *firmware, FwupdInstallFlags flags, GError **error) { - FuFmapFirmware *self = FU_FMAP_FIRMWARE(firmware); FuFmapFirmwareClass *klass_firmware = FU_FMAP_FIRMWARE_GET_CLASS(firmware); gsize bufsz; const guint8 *buf = g_bytes_get_data(fw, &bufsz); @@ -75,8 +47,9 @@ fu_fmap_firmware_parse(FuFirmware *firmware, /* only search for the fmap signature if not fuzzing */ if ((flags & FWUPD_INSTALL_FLAG_NO_SEARCH) == 0) { - if (!fu_fmap_firmware_find_offset(self, buf, bufsz, error)) + if (!fu_memmem_safe(buf, bufsz, (const guint8 *)FMAP_SIGNATURE, 8, &offset, error)) return FALSE; + fu_firmware_set_offset(firmware, offset); } /* load header */ @@ -85,7 +58,7 @@ fu_fmap_firmware_parse(FuFirmware *firmware, 0x0, /* dst */ buf, bufsz, - fu_firmware_get_offset(firmware), /* src */ + offset, /* src */ sizeof(fmap), error)) return FALSE; @@ -108,7 +81,7 @@ fu_fmap_firmware_parse(FuFirmware *firmware, GUINT16_FROM_LE(fmap.nareas)); return FALSE; } - offset = fu_firmware_get_offset(firmware) + sizeof(fmap); + offset += sizeof(fmap); for (gsize i = 0; i < GUINT16_FROM_LE(fmap.nareas); i++) { FuFmapArea area; diff --git a/libfwupdplugin/fu-self-test.c b/libfwupdplugin/fu-self-test.c index a7764b4df..31b4babc3 100644 --- a/libfwupdplugin/fu-self-test.c +++ b/libfwupdplugin/fu-self-test.c @@ -479,6 +479,30 @@ _strnsplit_nop_cb(GString *token, guint token_idx, gpointer user_data, GError ** return TRUE; } +static void +fu_common_memmem_func(void) +{ + const guint8 haystack[] = {'H', 'A', 'Y', 'S'}; + const guint8 needle[] = {'A', 'Y'}; + gboolean ret; + gsize offset = 0; + g_autoptr(GError) error = NULL; + + ret = fu_memmem_safe(haystack, sizeof(haystack), needle, sizeof(needle), &offset, &error); + g_assert_no_error(error); + g_assert_true(ret); + g_assert_cmpint(offset, ==, 0x1); + + ret = fu_memmem_safe(haystack + 2, + sizeof(haystack) - 2, + needle, + sizeof(needle), + &offset, + &error); + g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND); + g_assert_false(ret); +} + static void fu_common_strnsplit_func(void) { @@ -3789,6 +3813,7 @@ main(int argc, char **argv) g_setenv("FWUPD_LOCALSTATEDIR", "/tmp/fwupd-self-test/var", TRUE); g_test_add_func("/fwupd/common{strnsplit}", fu_common_strnsplit_func); + g_test_add_func("/fwupd/common{memmem}", fu_common_memmem_func); g_test_add_func("/fwupd/progress", fu_progress_func); g_test_add_func("/fwupd/progress{child}", fu_progress_child_func); g_test_add_func("/fwupd/progress{parent-1-step}", fu_progress_parent_one_step_proxy_func); diff --git a/libfwupdplugin/fwupdplugin.map b/libfwupdplugin/fwupdplugin.map index bb9661b32..77a1297c8 100644 --- a/libfwupdplugin/fwupdplugin.map +++ b/libfwupdplugin/fwupdplugin.map @@ -977,3 +977,9 @@ LIBFWUPDPLUGIN_1.7.3 { fu_common_sum8_bytes; local: *; } LIBFWUPDPLUGIN_1.7.2; + +LIBFWUPDPLUGIN_1.7.4 { + global: + fu_memmem_safe; + local: *; +} LIBFWUPDPLUGIN_1.7.3;