uid-range: fix out-of-bounds write in uid_range_partition()
uid_range_partition() filled the grown entries[] buffer backwards in
place. The backward-fill invariant (the write cursor stays above the
read index) only holds when every source entry contributes at least
one partition; an entry with nr < size contributes zero, so the cursor
stalls while the read index keeps descending. A later multi-part
entry's writes then overwrite the still-live zero-part slot, the
corrupted slot is re-read as a one-part entry, and the next
range->entries[--t] underflows.
Add a forward compaction first pass that drops the zero-part entries
before the backward fill.
Follow-up for 025439faaa
Co-Authored-by: Paul Meyer <katexochen0@gmail.com>
This commit is contained in:
co-authored by
Paul Meyer
parent
f66144cf2a
commit
d30e87ea49
@@ -318,6 +318,68 @@ TEST(uid_range_partition) {
|
||||
|
||||
p = uid_range_free(p);
|
||||
|
||||
/* Small entry preceding a large entry: the small entry must be dropped and the large entry
|
||||
* partitioned without the in-place backward-fill write cursor aliasing the still-live small entry
|
||||
* slot. */
|
||||
ASSERT_OK(uid_range_add_str(&p, "0-4"));
|
||||
ASSERT_OK(uid_range_add_str(&p, "100-129"));
|
||||
ASSERT_EQ(uid_range_entries(p), 2U);
|
||||
ASSERT_OK(uid_range_partition(p, 10));
|
||||
ASSERT_EQ(uid_range_entries(p), 3U);
|
||||
ASSERT_EQ(p->entries[0].start, 100U);
|
||||
ASSERT_EQ(p->entries[0].nr, 10U);
|
||||
ASSERT_EQ(p->entries[1].start, 110U);
|
||||
ASSERT_EQ(p->entries[1].nr, 10U);
|
||||
ASSERT_EQ(p->entries[2].start, 120U);
|
||||
ASSERT_EQ(p->entries[2].nr, 10U);
|
||||
|
||||
p = uid_range_free(p);
|
||||
|
||||
/* A too-small entry between two partitionable entries is dropped; the others still partition. */
|
||||
ASSERT_OK(uid_range_add_str(&p, "0-4")); /* nr=5 < size -> dropped */
|
||||
ASSERT_OK(uid_range_add_str(&p, "50-69")); /* nr=20 -> 2 parts */
|
||||
ASSERT_OK(uid_range_add_str(&p, "200-204")); /* nr=5 < size -> dropped */
|
||||
ASSERT_OK(uid_range_add_str(&p, "1000-1029")); /* nr=30 -> 3 parts */
|
||||
ASSERT_EQ(uid_range_entries(p), 4U);
|
||||
ASSERT_OK(uid_range_partition(p, 10));
|
||||
ASSERT_EQ(uid_range_entries(p), 5U);
|
||||
ASSERT_EQ(p->entries[0].start, 50U);
|
||||
ASSERT_EQ(p->entries[0].nr, 10U);
|
||||
ASSERT_EQ(p->entries[1].start, 60U);
|
||||
ASSERT_EQ(p->entries[1].nr, 10U);
|
||||
ASSERT_EQ(p->entries[2].start, 1000U);
|
||||
ASSERT_EQ(p->entries[2].nr, 10U);
|
||||
ASSERT_EQ(p->entries[3].start, 1010U);
|
||||
ASSERT_EQ(p->entries[3].nr, 10U);
|
||||
ASSERT_EQ(p->entries[4].start, 1020U);
|
||||
ASSERT_EQ(p->entries[4].nr, 10U);
|
||||
|
||||
p = uid_range_free(p);
|
||||
|
||||
/* A too-small entry before a partitionable entry is dropped. */
|
||||
ASSERT_OK(uid_range_add_str(&p, "0-4")); /* nr=5 < size -> dropped */
|
||||
ASSERT_OK(uid_range_add_str(&p, "100-119")); /* nr=20 -> 2 parts */
|
||||
ASSERT_OK(uid_range_partition(p, 10));
|
||||
ASSERT_EQ(uid_range_entries(p), 2U);
|
||||
ASSERT_EQ(p->entries[0].start, 100U);
|
||||
ASSERT_EQ(p->entries[0].nr, 10U);
|
||||
ASSERT_EQ(p->entries[1].start, 110U);
|
||||
ASSERT_EQ(p->entries[1].nr, 10U);
|
||||
|
||||
p = uid_range_free(p);
|
||||
|
||||
/* A too-small entry after a partitionable entry is dropped. */
|
||||
ASSERT_OK(uid_range_add_str(&p, "0-199")); /* nr=200 -> 2 parts */
|
||||
ASSERT_OK(uid_range_add_str(&p, "1000-1004")); /* nr=5 < size -> dropped */
|
||||
ASSERT_OK(uid_range_partition(p, 100));
|
||||
ASSERT_EQ(uid_range_entries(p), 2U);
|
||||
ASSERT_EQ(p->entries[0].start, 0U);
|
||||
ASSERT_EQ(p->entries[0].nr, 100U);
|
||||
ASSERT_EQ(p->entries[1].start, 100U);
|
||||
ASSERT_EQ(p->entries[1].nr, 100U);
|
||||
|
||||
p = uid_range_free(p);
|
||||
|
||||
/* Partition size of 1 */
|
||||
ASSERT_OK(uid_range_add_str(&p, "100-102"));
|
||||
ASSERT_OK(uid_range_partition(p, 1));
|
||||
|
||||
Reference in New Issue
Block a user