can1357/oh-my-pi · error · io::Error

path contains NUL

Error message

path contains NUL

What it means

pi-walker's directory-descriptor helper converts a Path's bytes to a CString before calling libc::open, and conversion fails if the path embeds a NUL byte (0x00). Rust Paths can contain interior NULs; libc APIs are NUL-terminated C strings, so such a path can never be opened. The walker maps the failure to io::ErrorKind::InvalidInput with this message.

Source

Thrown at crates/pi-walker/src/lib.rs:3567

					},
					Err(err) if is_skippable_entry_error(&err) => continue,
					Err(err) => return Err(err.into()),
				}
			}

			let raw_entry =
				RawDirEntry { name: Cow::Owned(entry.file_name()), file_type, mtime, size };

			if emit(raw_entry).map_err(ReadDirError::Walk)? == ReadDirControl::Stop {
				return Ok(ReadDirControl::Stop);
			}
		}
		Ok(ReadDirControl::Continue)
	}

	fn open_dir(path: &Path) -> io::Result<FdGuard> {
		let path = CString::new(path.as_os_str().as_bytes())
			.map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "path contains NUL"))?;
		// SAFETY: `path` is a NUL-terminated C string; flags open the directory for
		// metadata traversal only and do not transfer ownership of the string.
		let fd =
			unsafe { libc::open(path.as_ptr(), libc::O_RDONLY | libc::O_DIRECTORY | libc::O_CLOEXEC) };
		if fd < 0 {
			Err(io::Error::last_os_error())
		} else {
			Ok(FdGuard(fd))
		}
	}

	fn parse_record(record: &[u8], detail: WalkDetail) -> io::Result<Option<RawDirEntry<'_>>> {
		let mut cursor = size_of::<u32>();
		let name_ref_start = cursor;
		let name_ref = read_value::<libc::attrreference_t>(record, &mut cursor)?;
		let obj_type = read_value::<u32>(record, &mut cursor)?;
		let (mtime, data_length) = if detail == WalkDetail::Full {
			let modified = read_value::<libc::timespec>(record, &mut cursor)?;

View on GitHub (pinned to 9690622007)

Solutions

  1. Sanitize or reject paths containing interior NUL bytes before passing them to the walker.
  2. Find the source of the malformed path (decoded buffer, binary format, database) and fix the producer.
  3. Catch io::ErrorKind::InvalidInput with this message and surface a user-facing validation error instead of walking.

Example fix

// before
walker.walk(untrustedPath);
// after
if (untrustedPath.as_os_str().as_bytes().contains(&0)) {
  return Err("path contains NUL byte");
}
walker.walk(untrustedPath);
Defensive patterns

Strategy: validation

Validate before calling

if (path.as_os_str().as_bytes().contains(&0)) {
  return Err(io::Error::new(io::ErrorKind::InvalidInput, "rejecting path with NUL"));
}

Try / catch

match walker.walk(path) {
  Err(e) if e.kind() == io::ErrorKind::InvalidInput => eprintln!("invalid path: {e}"),
  Err(e) => return Err(e),
  Ok(()) => {},
}

Prevention

When it happens

Trigger: Calling open_dir with a Path whose OS bytes contain an interior 0x00 byte; typically reached via walk/read_dir APIs when the filesystem tree (or a crafted path input) contains NUL in a component.

Common situations: Paths sourced from binary data, corrupted filesystem entries, or user/network input that was not sanitized; rare on normal trees since most filesystems forbid NUL in names.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/c756e06213270262. Report an issue: GitHub.