Skip to content

[Code scan] Corrupted volume data is reported as a memory rejection #44

Description

@KaguraSayuki

Category

Runtime

Operating system

Linux, macOS, and Windows

Package or commit tested

PR #26 final head: cdfeaaf

Steps to reproduce

  1. Start a valid four-pipe MatterViz session.
  2. Create a valid v1 or v2 volume frame.
  3. Change one byte in the volume body without updating its CRC.
  4. Send the frame and read the acknowledgement.
  5. Keep the producer pipe open and observe whether the session stops.

Current behavior

Rust detects the invalid frame or CRC, sends a nonzero acknowledgement, and then continues reading the same transport:

fn receive_buffered_volume(
reader: &mut platform::PipeReader,
writer: &mut platform::PipeWriter,
store: &VolumeStore,
stop: &AtomicBool,
prelude: [u8; PRELUDE_BYTES],
) -> Result<(), ()> {
let frame_len = declared_volume_frame_len(&prelude).map_err(|_| ())?;
let mut frame = vec![0_u8; frame_len];
frame[..PRELUDE_BYTES].copy_from_slice(&prelude);
read_exact(reader, &mut frame[PRELUDE_BYTES..], stop)?;
let identity = frame_identity(&frame).ok_or(())?;
let status = if store.insert(frame).is_ok() { 0 } else { 1 };
let ack = encode_ack(identity.0, identity.1, status).map_err(|_| ())?;
writer.write_all(&ack).map_err(|_| ())
}
fn receive_stream_volume(
reader: &mut platform::PipeReader,
writer: &mut platform::PipeWriter,
broker: &VolumeStreamBroker,
stop: &AtomicBool,
prelude: [u8; PRELUDE_BYTES],
) -> Result<(), ()> {
let mut header = [0_u8; VOLUME_HEADER_BYTES];
header[..PRELUDE_BYTES].copy_from_slice(&prelude);
read_exact(reader, &mut header[PRELUDE_BYTES..], stop)?;
let metadata = decode_stream_volume_header(&header).map_err(|_| ())?;
let request_id = metadata.request_id;
let volume_id = metadata.volume_id;
let expected_crc = metadata.body_crc32c;
let mut sender = broker.sender(request_id);
let mut accepted = sender.as_ref().is_some_and(|channel| {
send_event(
channel,
StreamEvent::Begin(Box::new(metadata.clone()), Box::new(header)),
stop,
)
});
if !accepted {
sender = None;
}
let mut remaining = metadata.body_bytes;
let mut crc = Crc32c::new();
while remaining > 0 {
let length = usize::try_from(remaining.min(64 * 1024)).map_err(|_| ())?;
let mut chunk = vec![0_u8; length];
read_exact(reader, &mut chunk, stop)?;
crc.update(&chunk);
if let Some(channel) = sender.as_ref() {
if !send_event(channel, StreamEvent::Chunk(chunk), stop) {
sender = None;
accepted = false;
}
}
remaining -= length as u64;
}
let valid = crc.finish() == expected_crc;
if let Some(channel) = sender.as_ref() {
let event = if valid {
StreamEvent::End
} else {
StreamEvent::Error("MatterViz volume body CRC mismatch".to_owned())
};
if !send_event(channel, event, stop) {
accepted = false;
}
}
broker.finish(request_id);
let status = u32::from(!accepted || !valid);
let ack = encode_stream_ack(request_id, volume_id, status).map_err(|_| ())?;
writer.write_all(&ack).map_err(|_| ())

The C adapter converts any otherwise well-formed nonzero acknowledgement to MWFN_ERR_REJECTED:

{
uint8_t ack[MWFN_ACK_BYTES];
error_code = mwfn_read_exact_posix_deadline((int)ack_read, ack, sizeof(ack), deadline);
if (error_code == ETIMEDOUT) return MWFN_ERR_TIMEOUT;
if (error_code != 0) return error_code;
if (!mwfn_valid_ack(ack, request_id, volume_id)) {
return mwfn_valid_ack_fields(ack, request_id, volume_id, 0) ? MWFN_ERR_REJECTED
: MWFN_ERR_PROTOCOL;
}

Fortran treats that value as a recoverable memory or admission rejection, keeps the transport open, and may tell the user to reduce the grid size:

if (status_out/=0) then
write(*,"(a,i0,a)") " MatterViz binary volume publish failed (",status_out,")"
! A consumer-side memory/admission rejection is request-local. Protocol,
! pipe and timeout failures invalidate the transport for this session.
if (status_out/=-1005) call close_matterviz_transport()

This hides the real problem, which is corrupted or invalid data.

Expected behavior

A request that is no longer needed may be rejected without closing the whole session. Data that fails format or CRC validation should be reported as a protocol error and should close the formal session.

The user should not be told to reduce the grid size when the received bytes are corrupted.

Input file and reproducer

No scientific input file is required. Start from one of the existing valid protocol fixtures and flip one body byte.

Logs

Current output can report a rejected volume request or suggest reducing the grid instead of identifying a CRC or protocol failure.

Screenshots or comparisons

The regression test should keep the producer open after the bad frame and verify that Rust stops the transport without waiting for the producer to close it.

Before submitting

  • I searched for an existing issue describing the same problem.
  • I have included enough information to reproduce the issue.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions