Skip to content

Commit

Permalink
fix(RefCountedSet): Gracefully handle pathological cases
Browse files Browse the repository at this point in the history
Poor hash uniformity and/or a crafted or unlucky input could cause the
bounds of the PSL stats array to be exceeded, which caused memory
corruption (not good!) -- we avoid such cases now by returning an
OutOfMemory error if we're about to insert and there's an item with a
PSL in the last slot.
  • Loading branch information
qwerasd205 committed Dec 23, 2024
1 parent b44ebed commit cb60f9d
Showing 1 changed file with 21 additions and 0 deletions.
21 changes: 21 additions & 0 deletions src/terminal/ref_counted_set.zig
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,12 @@ pub fn RefCountedSet(
/// unlikely. Roughly a (1/table_cap)^32 -- with any normal
/// table capacity that is so unlikely that it's not worth
/// handling.
///
/// However, that assumes a uniform hash function, which
/// is not guaranteed and can be subverted with a crafted
/// input. We handle this gracefully by returning an error
/// anywhere where we're about to insert if there's any
/// item with a PSL in the last slot of the stats array.
psl_stats: [32]Id = [_]Id{0} ** 32,

/// The backing store of items
Expand Down Expand Up @@ -237,6 +243,16 @@ pub fn RefCountedSet(
return id;
}

// While it should be statistically impossible to exceed the
// bounds of `psl_stats`, the hash function is not perfect and
// in such a case we want to remain stable. If we're about to
// insert an item and there's something with a PSL of `len - 1`,
// we may end up with a PSL of `len` which would exceed the bounds.
// In such a case, we claim to be out of memory.
if (self.psl_stats[self.psl_stats.len - 1] > 0) {
return AddError.OutOfMemory;
}

// If the item doesn't exist, we need an available ID.
if (self.next_id >= self.layout.cap) {
// Arbitrarily chosen, threshold for rehashing.
Expand Down Expand Up @@ -284,6 +300,11 @@ pub fn RefCountedSet(

if (id < self.next_id) {
if (items[id].meta.ref == 0) {
// See comment in `addContext` for details.
if (self.psl_stats[self.psl_stats.len - 1] > 0) {
return AddError.OutOfMemory;
}

self.deleteItem(base, id, ctx);

const added_id = self.upsert(base, value, id, ctx);
Expand Down

0 comments on commit cb60f9d

Please sign in to comment.