h5rs tools, browser reader, libhdf5 header checks, plugin filters, concurrency benchmark #14
@@ -85,7 +85,9 @@ fn clawhdf5_reads(path: &Path) -> Result<(), String> {
|
||||
let ds = file.dataset("d").map_err(|e| format!("dataset: {e}"))?;
|
||||
ds.dtype().map_err(|e| format!("dtype: {e}"))?;
|
||||
ds.shape().map_err(|e| format!("shape: {e}"))?;
|
||||
ds.read_i32().map(|_| ()).map_err(|e| format!("read: {e}"))
|
||||
file.read_multi(&["d"])
|
||||
.map(|_| ())
|
||||
.map_err(|e| format!("read: {e}"))
|
||||
}
|
||||
|
||||
/// h5py's verdict for each file must be `expected`, and clawhdf5 must read
|
||||
@@ -198,3 +200,72 @@ for libver in ("earliest", "latest"):
|
||||
assert!(err.to_string().contains("truncated file"), "{name}: {err}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn header_and_datatype_damage_libhdf5_refuses_is_refused() {
|
||||
skip_if_no_python!();
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
// Each case damages one field of a valid file h5py wrote (libver
|
||||
// earliest, so version-1 object headers); all of these read before.
|
||||
let verdicts = h5py_verdicts(
|
||||
dir.path(),
|
||||
r#"
|
||||
def write(name, make):
|
||||
path = os.path.join(d, name + ".h5")
|
||||
with h5py.File(path, "w", libver="earliest") as f:
|
||||
make(f)
|
||||
return bytearray(open(path, "rb").read())
|
||||
|
||||
def save(name, data):
|
||||
open(os.path.join(d, name + ".h5"), "wb").write(data)
|
||||
|
||||
# A layout message (type 8, 24 bytes, v1 header) flagged shareable.
|
||||
data = write("layout_good", lambda f: f.create_dataset("d", data=np.arange(4, dtype="<i4")))
|
||||
at = data.find(bytes([8, 0, 24, 0]))
|
||||
assert at > 0
|
||||
bad = bytearray(data); bad[at + 4] |= 0x40
|
||||
save("layout_shareable", bad)
|
||||
# A message size that is not a multiple of 8 in a v1 header.
|
||||
bad = bytearray(data); bad[at + 2] = 23
|
||||
save("layout_unaligned", bad)
|
||||
|
||||
# A compound whose second field repeats the first's name, or overlaps it.
|
||||
dt = np.dtype([("aa", "<i4"), ("bb", "<i4")])
|
||||
data = write("compound_good", lambda f: f.create_dataset("d", data=np.zeros(2, dt)))
|
||||
at = data.find(b"bb\0")
|
||||
bad = bytearray(data); bad[at:at + 2] = b"aa"
|
||||
save("compound_duplicate", bad)
|
||||
bad = bytearray(data); bad[at + 8:at + 12] = struct.pack("<I", 2)
|
||||
save("compound_overlap", bad)
|
||||
|
||||
# An enum member with an empty name.
|
||||
et = h5py.enum_dtype({"RED": 0, "GREEN": 1}, basetype="i4")
|
||||
data = write("enum_good", lambda f: f.create_dataset("d", data=np.zeros(2, "<i4"), dtype=et))
|
||||
at = data.find(b"RED\0")
|
||||
bad = bytearray(data); bad[at:at + 3] = b"\0\0\0"
|
||||
save("enum_empty_name", bad)
|
||||
|
||||
# A float whose exponent overlaps its mantissa (exponent at bit 20).
|
||||
data = write("float_good", lambda f: f.create_dataset("d", data=np.zeros(3, "<f4")))
|
||||
at = data.find(bytes([0, 0, 32, 0, 23, 8, 0, 23]))
|
||||
bad = bytearray(data); bad[at + 4] = 20
|
||||
save("float_overlap", bad)
|
||||
"#,
|
||||
);
|
||||
assert_agrees_with_h5py(
|
||||
dir.path(),
|
||||
&verdicts,
|
||||
&[
|
||||
"compound_duplicate ERROR",
|
||||
"compound_good ok",
|
||||
"compound_overlap ERROR",
|
||||
"enum_empty_name ERROR",
|
||||
"enum_good ok",
|
||||
"float_good ok",
|
||||
"float_overlap ERROR",
|
||||
"layout_good ok",
|
||||
"layout_shareable ERROR",
|
||||
"layout_unaligned ERROR",
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user