fix(cubetrace): wrap Behavior::variant in behavior() helper in test call sites

Behavior is a newtype (Behavior(pub u16)), not an enum, so the 5 test
call sites that passed Behavior::PURE / Behavior::IO_HEAVY / etc. directly
to the ev() helper got type mismatch errors. Add a local behavior() wrapper
and use it at all 5 sites. Also clean up the unused CodeCell import and
extraneous parens that clippy flagged.

fix(cubesys): ls on directory paths no longer rejected by path_to_czyx

The ls handler used path_to_czyx() (which enforces "path must name a
record") to derive the ACL gate coordinate. Directory prefixes like
/c001/z001/y001 are valid read targets but path_to_czyx rejects them as
"names a directory, not a record", so cube-bench's ls assertion panicked.

Fix: use cubefs::path::parse_path (accepts any well-formed prefix) and
derive the directory prefix coordinate (trailing axes zeroed) for the ACL
gate, matching what NullSpace acl_bucket uses for directory ACLs.

Verification: cube-bench runs to completion (ls section printed OK), all
3 cubetrace tests pass, workspace compiles clean.

Co-Authored-By: Hermes Agent
This commit is contained in:
CUBELinux-2
2026-08-20 17:25:48 -04:00
co-authored by Hermes Agent
parent b97efa2a16
commit ddca08ea73
2 changed files with 25 additions and 8 deletions
+14 -1
View File
@@ -1184,7 +1184,20 @@ impl Session {
let dir = it.next().ok_or_else(|| "ls needs <dir>".to_string())?;
// R5: directory reads are metadata reads — honor the read gate
// so an attacker can't enumerate a victim's records by name.
let dir_coord = crate::path_to_czyx(dir).map_err(|e| e.to_string())?;
// Use parse_path (not path_to_czyx) because ls targets a
// directory prefix, which path_to_czyx rejects as "not a record".
let parsed = cubefs::path::parse_path(dir)
.map_err(|e| format!("ls: bad path {dir}: {e}"))?;
// The ACL gate wants the directory's prefix coordinate
// (trailing axes zeroed) — same key the NullSpace acl_bucket
// uses for directory ACLs.
let dir_coord = match parsed.axes.len() {
0 => Czyx::new(0, 0, 0, 0), // root
1 => Czyx::new(parsed.axes[0], 0, 0, 0),
2 => Czyx::new(parsed.axes[0], parsed.axes[1], 0, 0),
3 => Czyx::new(parsed.axes[0], parsed.axes[1], parsed.axes[2], 0),
_ => return Err("ls: path too deep".to_string()),
};
if let Some(msg) = self.admit_read(dir_coord) {
self.audit_now(OP_READ, dir_coord, false);
return Err(msg);
+11 -7
View File
@@ -19,7 +19,7 @@
//! * `replay_all` deterministically replays the stored stream in timestamp
//! order (§977/§1118: "deterministic replay first").
use cubecode::{Behavior, CodeCell, Kind};
use cubecode::{Behavior, Kind};
use cubecoords::{CubeHeader, Czyx};
use cubestore::{CubeBackend, CubeStore, HashBackend};
@@ -169,7 +169,7 @@ impl<B: CubeBackend> Tracer<B> {
/// Store one event under a fresh CZYX coordinate in the `c240` band.
pub fn store_event(&mut self, ev: &TraceEvent) -> Czyx {
let label = Czyx::new(C_TRACE, 1, (ev.kind as u8), self.next_x);
let label = Czyx::new(C_TRACE, 1, ev.kind as u8, self.next_x);
self.next_x = self.next_x.wrapping_add(1).max(1);
let mut h = CubeHeader::new();
h.title = Some(format!("{:?}:{:?}", ev.kind, ev.coord));
@@ -220,13 +220,17 @@ mod tests {
}
}
fn behavior(flag: u16) -> Behavior {
Behavior(flag)
}
#[test]
fn event_round_trips_through_bytes() {
let e = ev(
EventKind::Syscall,
42,
Czyx::new(1, 2, 3, 4),
Behavior::HOT_PATH,
behavior(Behavior::HOT_PATH),
);
let back = TraceEvent::from_bytes(&e.to_bytes()).expect("decodes");
assert_eq!(e, back);
@@ -239,13 +243,13 @@ mod tests {
EventKind::BasicBlock,
10,
Czyx::new(1, 0, 0, 1),
Behavior::PURE,
behavior(Behavior::PURE),
),
ev(
EventKind::Syscall,
20,
Czyx::new(1, 0, 0, 2),
Behavior::IO_HEAVY,
behavior(Behavior::IO_HEAVY),
),
ev(
EventKind::Meta,
@@ -273,13 +277,13 @@ mod tests {
EventKind::BasicBlock,
30,
Czyx::new(1, 0, 0, 1),
Behavior::PURE,
behavior(Behavior::PURE),
),
ev(
EventKind::Syscall,
10,
Czyx::new(1, 0, 0, 2),
Behavior::IO_HEAVY,
behavior(Behavior::IO_HEAVY),
),
ev(
EventKind::Meta,