hwdb: bounds-check trie offsets against the mmap before dereferencing

Fixes a family of OOB reads in the trie walker where attacker-controlled
offsets are loaded from a (possibly hostile) hwdb.bin and added to
hwdb->map without first checking that the resulting pointer stays inside
the mapping. Crash sites:

  - trie_fnmatch_f               sd-hwdb.c:187   (#42340)
  - trie_search_f                sd-hwdb.c:233   (#42341)
  - trie_children_cmp_f /bsearch sd-hwdb.c:94    (#42342)
  - hwdb_add_property            sd-hwdb.c:121   (#42343)
  - trie_node_from_off (UBSAN)   sd-hwdb.c:83
  - trie_fnmatch_f stack overflow on cyclic children (CIFuzz finding)

Commit b45a897edc ("hwdb: reject out-of-bounds fnmatch prefixes")
plugged the first site only at the prefix_off-content level, but the
crash on the read of node->prefix_off itself still happens when node is
already OOB. This is the structural fix: a single hwdb_at() helper
validates that [off, off + size) lies inside the mapping, and
trie_node_from_off() / trie_string() return NULL on OOB; every caller
treats the result as nullable and surfaces -EBADMSG.

trie_fnmatch_f() additionally caps its recursion depth at 2048; without
the cap a corrupt trie whose child offsets form a cycle (or just a deep
linear chain) drives the parser into stack-overflow rather than
returning -EBADMSG (caught by CIFuzz address sanitizer on this branch).

Additions:

  - A fuzz-hwdb libFuzzer harness that drives sd_hwdb_new_from_path,
    sd_hwdb_get, sd_hwdb_seek, and sd_hwdb_enumerate on attacker bytes.
  - Five hand-crafted unit tests in test-sd-hwdb.c (one per crash
    bucket, plus the cyclic-trie recursion case) that build a malformed
    in-memory hwdb.bin and assert -EBADMSG rather than SIGSEGV /
    stack-overflow.
  - Regression corpus files under test/fuzz/fuzz-hwdb/ pinning each
    fixed bucket, including the CIFuzz stack-overflow reproducer.

Closes #42340.
Closes #42341.
Closes #42342.
Closes #42343.

Reported-by: AI-assisted libFuzzer campaign
Co-developed-by: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
jeffhuang
2026-05-28 15:05:24 +09:00
committed by Yu Watanabe
parent 6f31db9b07
commit 73fea38cf1
12 changed files with 432 additions and 35 deletions
+1
View File
@@ -311,4 +311,5 @@ endif
simple_fuzzers += files(
'sd-bus/fuzz-bus-match.c',
'sd-bus/fuzz-bus-message.c',
'sd-hwdb/fuzz-hwdb.c',
)
+73
View File
@@ -0,0 +1,73 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
/* Fuzz target for the sd-hwdb binary trie parser.
*
* sd-hwdb consumes an mmap'd hwdb.bin produced by `systemd-hwdb update`. The
* file is walked via offset-chasing into struct trie_node_f / trie_child_entry_f
* / trie_value_entry_f records. A malformed file can drive the walker outside
* the mapped region; the validators in trie_node_from_off / trie_string don't
* guard every dereference site.
*
* Expected input: bytes that look like an `hwdb.bin` file (header magic
* "KSLPHHRH" + trie). The harness writes the fuzzer bytes to a temp file,
* mmaps it via sd_hwdb_new_from_path, then exercises sd_hwdb_get (which
* drives trie_search_f / trie_fnmatch_f) and sd_hwdb_seek + sd_hwdb_enumerate
* (capped to bound infinite loops via crafted offset cycles).
*/
#include <unistd.h>
#include "sd-hwdb.h"
#include "fd-util.h"
#include "fileio.h"
#include "fuzz.h"
#include "tests.h"
#include "tmpfile-util.h"
#define MAX_ENUMERATE 1024
static const char * const modaliases[] = {
"pci:v00008086d00000A04",
"usb:v1234p5678",
"acpi:PNP0C0D:",
"dmi:bvnLENOVO:bvrR0KET20W:bd02/01/2020:svnLENOVO:pnThinkPadX1:",
"*",
};
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
_cleanup_(unlink_tempfilep) char filename[] = "/tmp/fuzz-hwdb.XXXXXX";
_cleanup_fclose_ FILE *f = NULL;
_cleanup_(sd_hwdb_unrefp) sd_hwdb *hwdb = NULL;
if (outside_size_range(size, 0, 4 * 1024 * 1024))
return 0;
fuzz_setup_logging();
ASSERT_OK(fmkostemp_safe(filename, "r+", &f));
if (size != 0)
ASSERT_OK_EQ_ERRNO((ssize_t) fwrite(data, size, 1, f), (ssize_t) 1);
ASSERT_OK(fflush_and_check(f));
if (sd_hwdb_new_from_path(filename, &hwdb) < 0)
return 0;
FOREACH_ELEMENT(modalias, modaliases) {
const char *key, *value;
size_t n = 0;
(void) sd_hwdb_get(hwdb, *modalias, "ID_VENDOR", &value);
if (sd_hwdb_seek(hwdb, *modalias) < 0)
continue;
while (sd_hwdb_enumerate(hwdb, &key, &value) > 0) {
DO_NOT_OPTIMIZE(key);
DO_NOT_OPTIMIZE(value);
if (++n >= MAX_ENUMERATE)
break;
}
}
return 0;
}
+125 -24
View File
@@ -3,6 +3,7 @@
Copyright © 2008 Alan Jenkins <alan.christopher.jenkins@googlemail.com>
***/
#include <errno.h>
#include <fnmatch.h>
#include <stdio.h>
#include <stdlib.h>
@@ -79,12 +80,74 @@ static const struct trie_value_entry_f *trie_node_value(sd_hwdb *hwdb, const str
return (const struct trie_value_entry_f *)base;
}
static const struct trie_node_f *trie_node_from_off(sd_hwdb *hwdb, le64_t off) {
return (const struct trie_node_f *)(hwdb->map + le64toh(off));
static const void* hwdb_at(sd_hwdb *hwdb, uint64_t off, uint64_t size) {
assert(hwdb);
uint64_t file_size = hwdb->st.st_size;
/* off == file_size is rejected too: a read at EOF is always OOB, and this keeps the
* boundary unambiguous even for a size == 0 caller (which would otherwise get a
* one-past-the-end pointer). */
if (off >= file_size)
return NULL;
if (file_size - off < size)
return NULL;
return (const uint8_t*) hwdb->map + off;
}
static const char *trie_string(sd_hwdb *hwdb, le64_t off) {
return hwdb->map + le64toh(off);
static const struct trie_node_f* trie_node_from_off(sd_hwdb *hwdb, le64_t off) {
assert(hwdb);
assert(hwdb->head);
uint64_t offset = le64toh(off);
uint64_t node_size = le64toh(hwdb->head->node_size);
uint64_t child_entry_size = le64toh(hwdb->head->child_entry_size);
uint64_t value_entry_size = le64toh(hwdb->head->value_entry_size);
if (node_size < sizeof(struct trie_node_f))
return NULL;
if (child_entry_size < sizeof(struct trie_child_entry_f))
return NULL;
if (value_entry_size < sizeof(struct trie_value_entry_f))
return NULL;
const struct trie_node_f *node = hwdb_at(hwdb, offset, node_size);
if (!node)
return NULL;
uint64_t children_bytes, values_bytes, total;
if (!MUL_SAFE(&children_bytes, (uint64_t) node->children_count, child_entry_size))
return NULL;
if (!MUL_SAFE(&values_bytes, le64toh(node->values_count), value_entry_size))
return NULL;
if (!ADD_SAFE(&total, node_size, children_bytes))
return NULL;
if (!INC_SAFE(&total, values_bytes))
return NULL;
if (!hwdb_at(hwdb, offset, total))
return NULL;
return node;
}
static const char* trie_string(sd_hwdb *hwdb, le64_t off) {
assert(hwdb);
uint64_t file_size = hwdb->st.st_size;
uint64_t offset = le64toh(off);
const char *p = hwdb_at(hwdb, offset, /* size= */ 1);
if (!p)
return NULL;
/* Clamp to SIZE_MAX so memchr()'s size_t arg cannot truncate on 32-bit. */
size_t avail = (size_t) MIN(file_size - offset, (uint64_t) SIZE_MAX);
if (!memchr(p, '\0', avail))
return NULL;
return p;
}
static int trie_children_cmp_f(const void *v1, const void *v2) {
@@ -102,7 +165,9 @@ static const struct trie_node_f *node_lookup_f(sd_hwdb *hwdb, const struct trie_
child = bsearch(&search, (const char *)node + le64toh(hwdb->head->node_size), node->children_count,
le64toh(hwdb->head->child_entry_size), trie_children_cmp_f);
if (child)
/* Treat corrupt child offsets like lookup misses. */
return trie_node_from_off(hwdb, child->child_off);
return NULL;
}
@@ -113,6 +178,8 @@ static int hwdb_add_property(sd_hwdb *hwdb, const struct trie_value_entry_f *ent
assert(hwdb);
key = trie_string(hwdb, entry->key_off);
if (!key)
return -EBADMSG;
/*
* Silently ignore all properties which do not start with a
@@ -173,8 +240,14 @@ static int hwdb_add_property(sd_hwdb *hwdb, const struct trie_value_entry_f *ent
return 0;
}
/* Cap recursion depth so a corrupt hwdb.bin whose children offsets form a
* cycle (or just a deep linear chain) cannot exhaust the stack. Real-world
* hwdb files do not approach this; the deepest legitimate trie key is well
* under a kilobyte. */
#define HWDB_RECURSION_MAX 2048U
static int trie_fnmatch_f(sd_hwdb *hwdb, const struct trie_node_f *node, size_t p,
struct linebuf *buf, const char *search) {
struct linebuf *buf, const char *search, unsigned depth) {
size_t len;
size_t i;
const char *prefix;
@@ -183,21 +256,31 @@ static int trie_fnmatch_f(sd_hwdb *hwdb, const struct trie_node_f *node, size_t
assert(hwdb);
assert(node);
/* Ensure the prefix is within bounds */
uint64_t file_size = hwdb->st.st_size, prefix_off = le64toh(node->prefix_off);
if (prefix_off >= file_size || p >= file_size - prefix_off)
return -EINVAL;
if (depth >= HWDB_RECURSION_MAX)
return -EBADMSG;
prefix = trie_string(hwdb, node->prefix_off);
len = strnlen(prefix + p, file_size - prefix_off - p);
if (!prefix)
return -EBADMSG;
len = strlen(prefix);
if (p > len)
return -EBADMSG;
len -= p;
if (!linebuf_add(buf, prefix + p, len))
return -EINVAL;
for (i = 0; i < node->children_count; i++) {
const struct trie_child_entry_f *child = trie_node_child(hwdb, node, i);
const struct trie_node_f *child_node;
linebuf_add_char(buf, child->c);
err = trie_fnmatch_f(hwdb, trie_node_from_off(hwdb, child->child_off), 0, buf, search);
child_node = trie_node_from_off(hwdb, child->child_off);
if (!child_node)
return -EBADMSG;
err = trie_fnmatch_f(hwdb, child_node, 0, buf, search, depth + 1);
if (err < 0)
return err;
linebuf_rem_char(buf);
@@ -223,16 +306,24 @@ static int trie_search_f(sd_hwdb *hwdb, const char *search) {
linebuf_init(&buf);
node = trie_node_from_off(hwdb, hwdb->head->nodes_root_off);
if (!node)
return -EBADMSG;
while (node) {
const struct trie_node_f *child;
size_t p = 0;
if (node->prefix_off) {
const char *prefix;
char c;
for (; (c = trie_string(hwdb, node->prefix_off)[p]); p++) {
prefix = trie_string(hwdb, node->prefix_off);
if (!prefix)
return -EBADMSG;
for (; (c = prefix[p]); p++) {
if (IN_SET(c, '*', '?', '['))
return trie_fnmatch_f(hwdb, node, p, &buf, search + i + p);
return trie_fnmatch_f(hwdb, node, p, &buf, search + i + p, 0);
if (c != search[i + p])
return 0;
}
@@ -242,7 +333,7 @@ static int trie_search_f(sd_hwdb *hwdb, const char *search) {
child = node_lookup_f(hwdb, node, '*');
if (child) {
linebuf_add_char(&buf, '*');
err = trie_fnmatch_f(hwdb, child, 0, &buf, search + i);
err = trie_fnmatch_f(hwdb, child, 0, &buf, search + i, 0);
if (err < 0)
return err;
linebuf_rem_char(&buf);
@@ -251,7 +342,7 @@ static int trie_search_f(sd_hwdb *hwdb, const char *search) {
child = node_lookup_f(hwdb, node, '?');
if (child) {
linebuf_add_char(&buf, '?');
err = trie_fnmatch_f(hwdb, child, 0, &buf, search + i);
err = trie_fnmatch_f(hwdb, child, 0, &buf, search + i, 0);
if (err < 0)
return err;
linebuf_rem_char(&buf);
@@ -260,7 +351,7 @@ static int trie_search_f(sd_hwdb *hwdb, const char *search) {
child = node_lookup_f(hwdb, node, '[');
if (child) {
linebuf_add_char(&buf, '[');
err = trie_fnmatch_f(hwdb, child, 0, &buf, search + i);
err = trie_fnmatch_f(hwdb, child, 0, &buf, search + i, 0);
if (err < 0)
return err;
linebuf_rem_char(&buf);
@@ -379,14 +470,15 @@ static int properties_prepare(sd_hwdb *hwdb, const char *modalias) {
return trie_search_f(hwdb, modalias);
}
_public_ int sd_hwdb_get(sd_hwdb *hwdb, const char *modalias, const char *key, const char **_value) {
_public_ int sd_hwdb_get(sd_hwdb *hwdb, const char *modalias, const char *key, const char **ret) {
const struct trie_value_entry_f *entry;
const char *value;
int r;
assert_return(hwdb, -EINVAL);
assert_return(hwdb->f, -EINVAL);
assert_return(modalias, -EINVAL);
assert_return(_value, -EINVAL);
assert_return(ret, -EINVAL);
r = properties_prepare(hwdb, modalias);
if (r < 0)
@@ -396,7 +488,11 @@ _public_ int sd_hwdb_get(sd_hwdb *hwdb, const char *modalias, const char *key, c
if (!entry)
return -ENOENT;
*_value = trie_string(hwdb, entry->value_off);
value = trie_string(hwdb, entry->value_off);
if (!value)
return -EBADMSG;
*ret = value;
return 0;
}
@@ -418,13 +514,14 @@ _public_ int sd_hwdb_seek(sd_hwdb *hwdb, const char *modalias) {
return 0;
}
_public_ int sd_hwdb_enumerate(sd_hwdb *hwdb, const char **key, const char **value) {
_public_ int sd_hwdb_enumerate(sd_hwdb *hwdb, const char **ret_key, const char **ret_value) {
const struct trie_value_entry_f *entry;
const char *v;
const void *k;
assert_return(hwdb, -EINVAL);
assert_return(key, -EINVAL);
assert_return(value, -EINVAL);
assert_return(ret_key, -EINVAL);
assert_return(ret_value, -EINVAL);
if (hwdb->properties_modified)
return -EAGAIN;
@@ -432,8 +529,12 @@ _public_ int sd_hwdb_enumerate(sd_hwdb *hwdb, const char **key, const char **val
if (!ordered_hashmap_iterate(hwdb->properties, &hwdb->properties_iterator, (void **)&entry, &k))
return 0;
*key = k;
*value = trie_string(hwdb, entry->value_off);
v = trie_string(hwdb, entry->value_off);
if (!v)
return -EBADMSG;
*ret_key = k;
*ret_value = v;
return 1;
}
+231 -9
View File
@@ -76,10 +76,82 @@ TEST(sd_hwdb_new_from_path) {
assert_se(r >= 0);
}
TEST(sd_hwdb_seek_rejects_invalid_fnmatch_prefix) {
static sd_hwdb* hwdb_new_from_blob(const void *data, size_t size) {
_cleanup_(rm_rf_physical_and_freep) char *tmp = NULL;
_cleanup_(sd_hwdb_unrefp) sd_hwdb *hwdb = NULL;
_cleanup_free_ char *path = NULL;
_cleanup_close_ int fd = -EBADF;
ASSERT_OK(mkdtemp_malloc(/* template= */ NULL, &tmp));
_cleanup_free_ char *path = ASSERT_NOT_NULL(path_join(tmp, "hwdb.bin"));
fd = ASSERT_OK_ERRNO(open(path, O_WRONLY|O_CREAT|O_CLOEXEC|O_TRUNC, 0644));
ASSERT_OK_EQ_ERRNO(write(fd, data, size), (ssize_t) size);
fd = safe_close(fd);
ASSERT_OK(sd_hwdb_new_from_path(path, &hwdb));
return TAKE_PTR(hwdb);
}
TEST(sd_hwdb_seek_rejects_invalid_fnmatch_child_node) {
_cleanup_(sd_hwdb_unrefp) sd_hwdb *hwdb = NULL;
struct invalid_hwdb {
struct trie_header_f header;
struct trie_node_f root_node;
struct trie_child_entry_f child1;
struct trie_node_f mid_node;
struct trie_child_entry_f child2;
struct trie_node_f wildcard_node;
struct trie_child_entry_f child3;
char strings[STRLEN("usb:") + 1];
} _packed_ data = {
.header = {
.signature = HWDB_SIG,
.tool_version = htole64(PROJECT_VERSION),
.file_size = htole64(sizeof(struct invalid_hwdb)),
.header_size = htole64(sizeof(struct trie_header_f)),
.node_size = htole64(sizeof(struct trie_node_f)),
.child_entry_size = htole64(sizeof(struct trie_child_entry_f)),
.value_entry_size = htole64(sizeof(struct trie_value_entry_f)),
.nodes_root_off = htole64(offsetof(struct invalid_hwdb, root_node)),
.nodes_len = htole64(offsetof(struct invalid_hwdb, strings) - offsetof(struct invalid_hwdb, root_node)),
.strings_len = htole64(STRLEN("usb:") + 1),
},
.root_node = {
.prefix_off = htole64(offsetof(struct invalid_hwdb, strings)),
.children_count = 1,
},
.child1 = {
.c = 'v',
.child_off = htole64(offsetof(struct invalid_hwdb, mid_node)),
},
.mid_node = {
.prefix_off = htole64(offsetof(struct invalid_hwdb, strings) + STRLEN("usb:")),
.children_count = 1,
},
.child2 = {
.c = '*',
.child_off = htole64(offsetof(struct invalid_hwdb, wildcard_node)),
},
.wildcard_node = {
.prefix_off = htole64(offsetof(struct invalid_hwdb, strings) + STRLEN("usb:")),
.children_count = 1,
},
.child3 = {
.c = 'x',
.child_off = htole64(0xDEAD0000),
},
.strings = "usb:",
};
hwdb = ASSERT_NOT_NULL(hwdb_new_from_blob(&data, sizeof(data)));
ASSERT_ERROR(sd_hwdb_seek(hwdb, "usb:vx"), EBADMSG);
}
TEST(sd_hwdb_seek_rejects_invalid_fnmatch_prefix) {
_cleanup_(sd_hwdb_unrefp) sd_hwdb *hwdb = NULL;
struct invalid_hwdb {
struct trie_header_f header;
@@ -124,15 +196,165 @@ TEST(sd_hwdb_seek_rejects_invalid_fnmatch_prefix) {
.strings = "usb:",
};
ASSERT_OK(mkdtemp_malloc(NULL, &tmp));
ASSERT_NOT_NULL(path = path_join(tmp, "hwdb.bin"));
hwdb = ASSERT_NOT_NULL(hwdb_new_from_blob(&data, sizeof(data)));
_cleanup_close_ int fd = ASSERT_OK_ERRNO(open(path, O_WRONLY|O_CREAT|O_CLOEXEC|O_TRUNC, 0644));
assert_se(write(fd, &data, sizeof(data)) == (ssize_t) sizeof(data));
fd = safe_close(fd);
ASSERT_ERROR(sd_hwdb_seek(hwdb, "usb:v*"), EBADMSG);
}
ASSERT_OK(sd_hwdb_new_from_path(path, &hwdb));
ASSERT_ERROR(sd_hwdb_seek(hwdb, "usb:v*"), EINVAL);
TEST(sd_hwdb_seek_rejects_invalid_search_prefix) {
_cleanup_(sd_hwdb_unrefp) sd_hwdb *hwdb = NULL;
struct invalid_hwdb {
struct trie_header_f header;
struct trie_node_f root_node;
char strings[1];
} _packed_ data = {
.header = {
.signature = HWDB_SIG,
.tool_version = htole64(PROJECT_VERSION),
.file_size = htole64(sizeof(struct invalid_hwdb)),
.header_size = htole64(sizeof(struct trie_header_f)),
.node_size = htole64(sizeof(struct trie_node_f)),
.child_entry_size = htole64(sizeof(struct trie_child_entry_f)),
.value_entry_size = htole64(sizeof(struct trie_value_entry_f)),
.nodes_root_off = htole64(offsetof(struct invalid_hwdb, root_node)),
.nodes_len = htole64(sizeof(struct trie_node_f)),
.strings_len = htole64(1),
},
.root_node = {
.prefix_off = htole64(0xDEAD0000),
},
};
hwdb = ASSERT_NOT_NULL(hwdb_new_from_blob(&data, sizeof(data)));
ASSERT_ERROR(sd_hwdb_seek(hwdb, "usb:v1234"), EBADMSG);
}
TEST(sd_hwdb_seek_rejects_truncated_children_array) {
_cleanup_(sd_hwdb_unrefp) sd_hwdb *hwdb = NULL;
struct invalid_hwdb {
struct trie_header_f header;
struct trie_node_f root_node;
} _packed_ data = {
.header = {
.signature = HWDB_SIG,
.tool_version = htole64(PROJECT_VERSION),
.file_size = htole64(sizeof(struct invalid_hwdb)),
.header_size = htole64(sizeof(struct trie_header_f)),
.node_size = htole64(sizeof(struct trie_node_f)),
.child_entry_size = htole64(sizeof(struct trie_child_entry_f)),
.value_entry_size = htole64(sizeof(struct trie_value_entry_f)),
.nodes_root_off = htole64(offsetof(struct invalid_hwdb, root_node)),
.nodes_len = htole64(sizeof(struct trie_node_f)),
},
.root_node = {
.children_count = 1,
},
};
hwdb = ASSERT_NOT_NULL(hwdb_new_from_blob(&data, sizeof(data)));
ASSERT_ERROR(sd_hwdb_seek(hwdb, "x"), EBADMSG);
}
TEST(sd_hwdb_seek_rejects_invalid_property_key) {
_cleanup_(sd_hwdb_unrefp) sd_hwdb *hwdb = NULL;
struct invalid_hwdb {
struct trie_header_f header;
struct trie_node_f root_node;
struct trie_value_entry_f value;
char strings[STRLEN("usb") + 1 + STRLEN("value") + 1];
} _packed_ data = {
.header = {
.signature = HWDB_SIG,
.tool_version = htole64(PROJECT_VERSION),
.file_size = htole64(sizeof(struct invalid_hwdb)),
.header_size = htole64(sizeof(struct trie_header_f)),
.node_size = htole64(sizeof(struct trie_node_f)),
.child_entry_size = htole64(sizeof(struct trie_child_entry_f)),
.value_entry_size = htole64(sizeof(struct trie_value_entry_f)),
.nodes_root_off = htole64(offsetof(struct invalid_hwdb, root_node)),
.nodes_len = htole64(offsetof(struct invalid_hwdb, strings) - offsetof(struct invalid_hwdb, root_node)),
.strings_len = htole64(STRLEN("usb") + 1 + STRLEN("value") + 1),
},
.root_node = {
.prefix_off = htole64(offsetof(struct invalid_hwdb, strings)),
.values_count = htole64(1),
},
.value = {
.key_off = htole64(0xDEAD0000),
.value_off = htole64(offsetof(struct invalid_hwdb, strings) + STRLEN("usb") + 1),
},
.strings = "usb\0value",
};
hwdb = ASSERT_NOT_NULL(hwdb_new_from_blob(&data, sizeof(data)));
ASSERT_ERROR(sd_hwdb_seek(hwdb, "usb"), EBADMSG);
}
TEST(sd_hwdb_seek_rejects_cyclic_trie) {
_cleanup_(sd_hwdb_unrefp) sd_hwdb *hwdb = NULL;
/* Cyclic trie: the wildcard_node's only child points back to
* itself, which would drive trie_fnmatch_f() into infinite
* recursion (stack-overflow CVE-class bug found by CIFuzz).
* The recursion-depth cap must turn this into a clean -EBADMSG. */
struct invalid_hwdb {
struct trie_header_f header;
struct trie_node_f root_node;
struct trie_child_entry_f child1;
struct trie_node_f mid_node;
struct trie_child_entry_f child2;
struct trie_node_f wildcard_node;
struct trie_child_entry_f wildcard_self_child;
char strings[STRLEN("usb:") + 1];
} _packed_ data = {
.header = {
.signature = HWDB_SIG,
.tool_version = htole64(PROJECT_VERSION),
.file_size = htole64(sizeof(struct invalid_hwdb)),
.header_size = htole64(sizeof(struct trie_header_f)),
.node_size = htole64(sizeof(struct trie_node_f)),
.child_entry_size = htole64(sizeof(struct trie_child_entry_f)),
.value_entry_size = htole64(sizeof(struct trie_value_entry_f)),
.nodes_root_off = htole64(offsetof(struct invalid_hwdb, root_node)),
.nodes_len = htole64(offsetof(struct invalid_hwdb, strings) - offsetof(struct invalid_hwdb, root_node)),
.strings_len = htole64(STRLEN("usb:") + 1),
},
.root_node = {
.prefix_off = htole64(offsetof(struct invalid_hwdb, strings)),
.children_count = 1,
},
.child1 = {
.c = 'v',
.child_off = htole64(offsetof(struct invalid_hwdb, mid_node)),
},
.mid_node = {
.prefix_off = htole64(offsetof(struct invalid_hwdb, strings) + STRLEN("usb:")),
.children_count = 1,
},
.child2 = {
.c = '*',
.child_off = htole64(offsetof(struct invalid_hwdb, wildcard_node)),
},
.wildcard_node = {
.prefix_off = htole64(offsetof(struct invalid_hwdb, strings) + STRLEN("usb:")),
.children_count = 1,
},
.wildcard_self_child = {
.c = 'a',
.child_off = htole64(offsetof(struct invalid_hwdb, wildcard_node)),
},
.strings = "usb:",
};
hwdb = ASSERT_NOT_NULL(hwdb_new_from_blob(&data, sizeof(data)));
ASSERT_ERROR(sd_hwdb_seek(hwdb, "usb:v*"), EBADMSG);
}
static int intro(void) {
+2 -2
View File
@@ -28,10 +28,10 @@ _SD_DECLARE_TRIVIAL_REF_UNREF_FUNC(sd_hwdb);
int sd_hwdb_new(sd_hwdb **ret);
int sd_hwdb_new_from_path(const char *path, sd_hwdb **ret);
int sd_hwdb_get(sd_hwdb *hwdb, const char *modalias, const char *key, const char **value);
int sd_hwdb_get(sd_hwdb *hwdb, const char *modalias, const char *key, const char **ret);
int sd_hwdb_seek(sd_hwdb *hwdb, const char *modalias);
int sd_hwdb_enumerate(sd_hwdb *hwdb, const char **key, const char **value);
int sd_hwdb_enumerate(sd_hwdb *hwdb, const char **ret_key, const char **ret_value);
/* the inverse condition avoids ambiguity of dangling 'else' after the macro */
#define SD_HWDB_FOREACH_PROPERTY(hwdb, modalias, key, value) \
View File
Binary file not shown.