Skip to content

Commit 8bcda98

Browse files
rscharfegitster
authored andcommitted
oidtree: avoid unaligned access to crit-bit tree
The flexible array member "k" of struct cb_node is used to store the key of the crit-bit tree node. It offers no alignment guarantees -- in fact the current struct layout puts it one byte after a 4-byte aligned address, i.e. guaranteed to be misaligned. oidtree uses a struct object_id as cb_node key. Since cf09832 (hash: add an algo member to struct object_id, 2021-04-26) it requires 4-byte alignment. The mismatch is reported by UndefinedBehaviorSanitizer at runtime like this: hash.h:277:2: runtime error: member access within misaligned address 0x00015000802d for type 'struct object_id', which requires 4 byte alignment 0x00015000802d: note: pointer points here 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ^ SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior hash.h:277:2 in We can fix that by: 1. eliminating the alignment requirement of struct object_id, 2. providing the alignment in struct cb_node, or 3. avoiding the issue by only using memcpy to access "k". Currently we only store one of two values in "algo" in struct object_id. We could use a uint8_t for that instead and widen it only once we add support for our twohundredth algorithm or so. That would not only avoid alignment issues, but also reduce the memory requirements for each instance of struct object_id by ca. 9%. Supporting keys with alignment requirements might be useful to spread the use of crit-bit trees. It can be achieved by using a wider type for "k" (e.g. uintmax_t), using different types for the members "byte" and "otherbits" (e.g. uint16_t or uint32_t for each), or by avoiding the use of flexible arrays like khash.h does. This patch implements the third option, though, because it has the least potential for causing side-effects and we're close to the next release. If one of the other options is implemented later as well to get their additional benefits we can get rid of the extra copies introduced here. Reported-by: Andrzej Hunt <[email protected]> Signed-off-by: René Scharfe <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 5d213e4 commit 8bcda98

File tree

3 files changed

+17
-7
lines changed

3 files changed

+17
-7
lines changed

cbtree.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ struct cb_node {
2525
*/
2626
uint32_t byte;
2727
uint8_t otherbits;
28-
uint8_t k[FLEX_ARRAY]; /* arbitrary data */
28+
uint8_t k[FLEX_ARRAY]; /* arbitrary data, unaligned */
2929
};
3030

3131
struct cb_tree {

hash.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ static inline void git_SHA256_Clone(git_SHA256_CTX *dst, const git_SHA256_CTX *s
115115

116116
struct object_id {
117117
unsigned char hash[GIT_MAX_RAWSZ];
118-
int algo;
118+
int algo; /* XXX requires 4-byte alignment */
119119
};
120120

121121
/* A suitably aligned type for stack allocations of hash contexts. */

oidtree.c

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,19 @@ void oidtree_clear(struct oidtree *ot)
3131
void oidtree_insert(struct oidtree *ot, const struct object_id *oid)
3232
{
3333
struct cb_node *on;
34+
struct object_id k;
3435

3536
if (!oid->algo)
3637
BUG("oidtree_insert requires oid->algo");
3738

3839
on = mem_pool_alloc(&ot->mem_pool, sizeof(*on) + sizeof(*oid));
39-
oidcpy_with_padding((struct object_id *)on->k, oid);
40+
41+
/*
42+
* Clear the padding and copy the result in separate steps to
43+
* respect the 4-byte alignment needed by struct object_id.
44+
*/
45+
oidcpy_with_padding(&k, oid);
46+
memcpy(on->k, &k, sizeof(k));
4047

4148
/*
4249
* n.b. Current callers won't get us duplicates, here. If a
@@ -68,17 +75,20 @@ int oidtree_contains(struct oidtree *ot, const struct object_id *oid)
6875
static enum cb_next iter(struct cb_node *n, void *arg)
6976
{
7077
struct oidtree_iter_data *x = arg;
71-
const struct object_id *oid = (const struct object_id *)n->k;
78+
struct object_id k;
79+
80+
/* Copy to provide 4-byte alignment needed by struct object_id. */
81+
memcpy(&k, n->k, sizeof(k));
7282

73-
if (x->algo != GIT_HASH_UNKNOWN && x->algo != oid->algo)
83+
if (x->algo != GIT_HASH_UNKNOWN && x->algo != k.algo)
7484
return CB_CONTINUE;
7585

7686
if (x->last_nibble_at) {
77-
if ((oid->hash[*x->last_nibble_at] ^ x->last_byte) & 0xf0)
87+
if ((k.hash[*x->last_nibble_at] ^ x->last_byte) & 0xf0)
7888
return CB_CONTINUE;
7989
}
8090

81-
return x->fn(oid, x->arg);
91+
return x->fn(&k, x->arg);
8292
}
8393

8494
void oidtree_each(struct oidtree *ot, const struct object_id *oid,

0 commit comments

Comments
 (0)