fix(agent): fail to open a store whose /meta has an unreadable attribute

Group::attrs now leaves out an attribute it cannot decode. The agent read
its settings through it, so a store whose float16 (or compression,
quantized_index, WAL mark, signature...) attribute could not be decoded
opened with the default in its place, and no error. /meta is now read
with attrs_with_errors and any unreadable attribute is a Schema error,
as it was before attrs became tolerant. Found by the adversarial review.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
osobh
2026-09-25 22:43:53 -05:00
co-authored by Claude Opus 5.5
parent 256e7b89e4
commit d6e426e6d5
2 changed files with 75 additions and 23 deletions
@@ -258,3 +258,43 @@ fn an_existing_f32_store_stays_f32() {
assert_eq!(&values[..before.1.len()], before.1.as_slice());
assert_eq!(&values[before.1.len()..], odd.as_slice());
}
/// `Group::attrs` leaves out an attribute it cannot decode. A store whose
/// `float16` setting is unreadable must not open as `float16 = false` (or with
/// any other default in place of a setting it has): it is an error.
#[test]
fn unreadable_meta_attribute_fails_open_instead_of_defaulting() {
let dir = TempDir::new().unwrap();
let path = dir.path().join("store.h5");
{
let mut m = HDF5Memory::create(config(&dir, "store.h5", true)).unwrap();
m.save(entry(1)).unwrap();
m.flush_wal().unwrap();
}
assert!(HDF5Memory::open_read_only(&path).is_ok());
// Give the `float16` attribute message an unknown version (the name is
// at +8 in a version-1 message and +9 in a version-3 one).
let mut bytes = std::fs::read(&path).unwrap();
let name = b"float16\0";
let mut hit = false;
let positions: Vec<usize> = (9..bytes.len() - name.len())
.filter(|&p| &bytes[p..p + name.len()] == name)
.collect();
for pos in positions {
for (back, version) in [(8, 1u8), (9, 3u8)] {
if bytes[pos - back] == version {
bytes[pos - back] = 0x7f;
hit = true;
}
}
}
assert!(hit, "float16 attribute message not found");
std::fs::write(&path, &bytes).unwrap();
match HDF5Memory::open_read_only(&path) {
Err(MemoryError::Schema(msg)) => assert!(msg.contains("/meta"), "{msg}"),
Err(e) => panic!("unexpected error: {e}"),
Ok(_) => panic!("store opened with an unreadable float16 setting"),
}
}