Bug Description
ShapeTable.registerWithId (src/session.zig:124-140) and SessionEncoder.encodeWithSchema (:1521-1527) overwrite or accept inconsistent state under adversarial conditions:
pub fn registerWithId(self: *ShapeTable, allocator: Allocator, shape_id: u64, keys: []const []const u8) !bool {
if (self.by_id.get(shape_id)) |existing| {
return eqlNestedBytes(existing.keys, keys);
}
if (try self.getId(allocator, keys)) |existing_id| {
if (existing_id != shape_id) {
return false;
}
}
try self.insertShape(allocator, shape_id, keys);
if (self.next_id <= shape_id) {
self.next_id = shape_id + 1;
}
return true;
}
Three issues:
- If the attacker can inject a
RegisterShape before the legitimate sender's, the attacker's shape becomes canonical. Subsequent ShapedObject(shape_id = X) decodes are read with the attacker's key list.
next_id = shape_id + 1 can be set to u64-near-max by the attacker. Subsequent next_id +%= 1 (wrapping) collisions with previously-registered ids.
- The two directions of "same shape, different id" are checked asymmetrically.
Steps to Reproduce
- Attacker registers shape id=0 with keys=
["admin","role"].
- Legit sender later registers shape id=0 with keys=
["role","user"] → registerWithId returns false (existing matches keys), but legit sender's subsequent ShapedObject(0, [string1, string2]) is decoded with the attacker's keys.
Expected Behavior
- Reject
registerWithId when the same keys already map to a different id (already partially checked at :131, but the inverse direction should also fail).
- Require
next_id updates to use std.math.add(u64, ..., 1) and return RecurramError.InvalidData on overflow.
- Document and harden the trust model for shape registration (e.g., per-peer namespacing).
Actual Behavior
Race-condition hijack possible; wrap-around possible.
Environment
- Project:
recurram-zig
- File:
src/session.zig:124-140
Additional Context
Severity: High — state corruption attack on cooperating session.
Bug Description
ShapeTable.registerWithId(src/session.zig:124-140) andSessionEncoder.encodeWithSchema(:1521-1527) overwrite or accept inconsistent state under adversarial conditions:Three issues:
RegisterShapebefore the legitimate sender's, the attacker's shape becomes canonical. SubsequentShapedObject(shape_id = X)decodes are read with the attacker's key list.next_id = shape_id + 1can be set tou64-near-max by the attacker. Subsequentnext_id +%= 1(wrapping) collisions with previously-registered ids.Steps to Reproduce
["admin","role"].["role","user"]→registerWithIdreturnsfalse(existing matches keys), but legit sender's subsequentShapedObject(0, [string1, string2])is decoded with the attacker's keys.Expected Behavior
registerWithIdwhen the same keys already map to a different id (already partially checked at:131, but the inverse direction should also fail).next_idupdates to usestd.math.add(u64, ..., 1)and returnRecurramError.InvalidDataon overflow.Actual Behavior
Race-condition hijack possible; wrap-around possible.
Environment
recurram-zigsrc/session.zig:124-140Additional Context
Severity: High — state corruption attack on cooperating session.