#![forbid(unsafe_code)]
use std::{mem::take, os::fd::AsFd};
use btoi::btoi;
use memchr::{memchr, memmem};
use nix::{errno::Errno, sys::socket::UnixAddr};
use crate::{
cookie::safe_read, io::write_all, path::XPath, retry::retry_on_eintr, unix::unix_path_bytes,
};
struct Patch {
needle: &'static [u8],
repl: &'static [u8],
}
type PatchMask = u8; type PatchStep = Option<(usize, PatchMask)>;
const TRACERPID: &[u8] = b"TracerPid:";
const NONEWPRIVS: &[u8] = b"NoNewPrivs:";
const SECCOMP: &[u8] = b"Seccomp:";
const SECCOMP_FILTERS: &[u8] = b"Seccomp_filters:";
const SPEC_SSB: &[u8] = b"Speculation_Store_Bypass:";
const SPEC_SIB: &[u8] = b"SpeculationIndirectBranch:";
const SPEC_SSB_PATCHES: &[Patch] = &[
Patch {
needle: b"unknown",
repl: b"vulnerable",
},
Patch {
needle: b"unsupported",
repl: b"vulnerable",
},
Patch {
needle: b"thread ",
repl: b"",
},
Patch {
needle: b"force ",
repl: b"",
},
Patch {
needle: b"mitigated",
repl: b"vulnerable",
},
];
const SPEC_SIB_PATCHES: &[Patch] = &[
Patch {
needle: b"unknown",
repl: b"always enabled",
},
Patch {
needle: b"unsupported",
repl: b"always enabled",
},
Patch {
needle: b"conditional",
repl: b"always",
},
Patch {
needle: b"force ",
repl: b"",
},
Patch {
needle: b"disabled",
repl: b"enabled",
},
];
const PF_TRACERPID: u8 = 1 << 0;
const PF_NONEWPRIVS: u8 = 1 << 1;
const PF_SECCOMP: u8 = 1 << 2;
const PF_SECCOMP_FILTERS: u8 = 1 << 3;
const PF_SPEC_SSB: u8 = 1 << 4;
const PF_SPEC_SIB: u8 = 1 << 5;
struct ProcPidStatusMasker {
inbuf: Vec<u8>, outbuf: Vec<u8>, prefix_mask: u8, }
impl ProcPidStatusMasker {
const INBUF_CAP: usize = 2048;
const OUTBUF_CAP: usize = 2048;
const GROW_STEP: usize = 128;
fn new() -> Result<Self, Errno> {
let mut inbuf = Vec::new();
inbuf
.try_reserve(Self::INBUF_CAP)
.map_err(|_| Errno::ENOMEM)?;
let mut outbuf = Vec::new();
outbuf
.try_reserve(Self::OUTBUF_CAP)
.map_err(|_| Errno::ENOMEM)?;
Ok(Self {
inbuf,
outbuf,
prefix_mask: 0,
})
}
fn obuf_write(&mut self, data: &[u8]) -> Result<(), Errno> {
if data.is_empty() {
return Ok(());
}
self.outbuf
.try_reserve(data.len())
.map_err(|_| Errno::ENOMEM)?;
self.outbuf.extend_from_slice(data);
Ok(())
}
#[inline]
fn flush_all<Fd: AsFd>(&mut self, out: Fd) -> Result<(), Errno> {
write_all(&out, &self.outbuf)
}
fn try_emit_zero_field(&mut self, line: &[u8]) -> Result<bool, Errno> {
if (self.prefix_mask & PF_TRACERPID) == 0 && line.starts_with(TRACERPID) {
self.prefix_mask |= PF_TRACERPID;
self.emit_zero_field(line, TRACERPID)?;
return Ok(true);
}
if (self.prefix_mask & PF_NONEWPRIVS) == 0 && line.starts_with(NONEWPRIVS) {
self.prefix_mask |= PF_NONEWPRIVS;
self.emit_zero_field(line, NONEWPRIVS)?;
return Ok(true);
}
if (self.prefix_mask & PF_SECCOMP_FILTERS) == 0 && line.starts_with(SECCOMP_FILTERS) {
self.prefix_mask |= PF_SECCOMP_FILTERS;
self.emit_zero_field(line, SECCOMP_FILTERS)?;
return Ok(true);
}
if (self.prefix_mask & PF_SECCOMP) == 0 && line.starts_with(SECCOMP) {
self.prefix_mask |= PF_SECCOMP;
self.emit_zero_field(line, SECCOMP)?;
return Ok(true);
}
Ok(false)
}
fn try_emit_patch_group(&mut self, line: &[u8]) -> Result<bool, Errno> {
if (self.prefix_mask & PF_SPEC_SSB) == 0 && line.starts_with(SPEC_SSB) {
self.prefix_mask |= PF_SPEC_SSB;
let (head, value) = line.split_at(SPEC_SSB.len());
self.obuf_write(head)?;
return self.emit_patch_group_value(value, SPEC_SSB_PATCHES);
}
if (self.prefix_mask & PF_SPEC_SIB) == 0 && line.starts_with(SPEC_SIB) {
self.prefix_mask |= PF_SPEC_SIB;
let (head, value) = line.split_at(SPEC_SIB.len());
self.obuf_write(head)?;
return self.emit_patch_group_value(value, SPEC_SIB_PATCHES);
}
Ok(false)
}
fn emit_zero_field(&mut self, line: &[u8], field: &[u8]) -> Result<(), Errno> {
let mut i = field.len();
while i < line.len() {
let b = line[i];
if b == b' ' || b == b'\t' {
i = i.checked_add(1).ok_or(Errno::EOVERFLOW)?;
} else {
break;
}
}
let start = i;
while i < line.len() && line[i].is_ascii_digit() {
i = i.checked_add(1).ok_or(Errno::EOVERFLOW)?;
}
let end = i;
let digits_len = end.checked_sub(start).ok_or(Errno::EOVERFLOW)?;
if digits_len == 0 || (digits_len == 1 && line[start] == b'0') {
self.obuf_write(line)?;
return Ok(());
}
self.obuf_write(&line[..start])?;
self.obuf_write(b"0\n")?;
Ok(())
}
fn apply_patch_step(
&mut self,
value: &[u8],
patches: &[Patch],
applied: PatchMask,
) -> Result<PatchStep, Errno> {
if value.is_empty() || patches.is_empty() {
return Ok(None);
}
let mut best_pos: Option<usize> = None;
let mut best_idx: usize = 0;
#[expect(clippy::cast_possible_truncation)]
for (idx, p) in patches.iter().enumerate() {
if ((applied >> (idx as u32)) & 1) != 0 {
continue;
} if p.needle.is_empty() {
continue;
}
if let Some(pos) = memmem::find(value, p.needle) {
match best_pos {
None => {
best_pos = Some(pos);
best_idx = idx;
}
Some(cur) if pos < cur => {
best_pos = Some(pos);
best_idx = idx;
}
_ => {}
}
}
}
let Some(pos) = best_pos else {
return Ok(None);
};
let (left, after_left) = value.split_at(pos);
let needle_len = patches[best_idx].needle.len();
let (_, rest) = after_left.split_at(needle_len);
self.obuf_write(left)?;
self.obuf_write(patches[best_idx].repl)?;
let consumed = value
.len()
.checked_sub(rest.len())
.ok_or(Errno::EOVERFLOW)?;
if best_idx >= (u8::BITS as usize) {
return Err(Errno::EOVERFLOW);
}
#[expect(clippy::cast_possible_truncation)]
let bit: PatchMask = 1u8 << (best_idx as u32);
let new_mask: PatchMask = applied | bit;
Ok(Some((consumed, new_mask)))
}
fn emit_patch_group_value(
&mut self,
mut value: &[u8],
patches: &[Patch],
) -> Result<bool, Errno> {
let mut applied: PatchMask = 0;
let mut any = false;
loop {
match self.apply_patch_step(value, patches, applied)? {
None => {
self.obuf_write(value)?;
return Ok(any || !value.is_empty());
}
Some((consumed, new_mask)) => {
any = true;
if consumed > value.len() {
return Err(Errno::EOVERFLOW);
}
let (_, rest) = value.split_at(consumed);
value = rest;
applied = new_mask;
}
}
}
}
fn emit_line<Fd: AsFd>(&mut self, _out: Fd, line: &[u8]) -> Result<(), Errno> {
if self.try_emit_zero_field(line)? {
return Ok(());
}
if self.try_emit_patch_group(line)? {
return Ok(());
}
self.obuf_write(line)
}
fn run<S: AsFd, D: AsFd>(&mut self, src: S, dst: D) -> Result<(), Errno> {
loop {
let cap = self.inbuf.capacity();
let len = self.inbuf.len();
let free = cap.checked_sub(len).ok_or(Errno::EOVERFLOW)?;
if free == 0 {
self.inbuf
.try_reserve(Self::GROW_STEP)
.map_err(|_| Errno::ENOMEM)?;
continue;
}
let cur_len = len;
let new_len = cur_len.checked_add(free).ok_or(Errno::EOVERFLOW)?;
self.inbuf.resize(new_len, 0);
let tail = &mut self.inbuf[cur_len..new_len];
let n = retry_on_eintr(|| safe_read(&src, tail))?;
if n == 0 {
self.inbuf.truncate(cur_len);
break;
}
let keep_len = cur_len.checked_add(n).ok_or(Errno::EOVERFLOW)?;
self.inbuf.truncate(keep_len);
}
let inbuf = take(&mut self.inbuf);
let mut start: usize = 0;
loop {
let slice = if start <= inbuf.len() {
&inbuf[start..]
} else {
return Err(Errno::EOVERFLOW);
};
if let Some(nl_rel) = memchr(b'\n', slice) {
let end_incl = start.checked_add(nl_rel).ok_or(Errno::EOVERFLOW)?;
let line_end = end_incl.checked_add(1).ok_or(Errno::EOVERFLOW)?;
if line_end > inbuf.len() {
return Err(Errno::EOVERFLOW);
}
let line = &inbuf[start..line_end];
self.emit_line(&dst, line)?;
start = line_end;
continue;
}
if start < inbuf.len() {
let line = &inbuf[start..];
self.emit_line(&dst, line)?;
}
break;
}
self.flush_all(dst)
}
}
struct ProcNetUnixMasker {
inbuf: Vec<u8>,
outbuf: Vec<u8>,
}
impl ProcNetUnixMasker {
const INBUF_CAP: usize = 4096;
const OUTBUF_CAP: usize = 4096;
const GROW_STEP: usize = 256;
fn new() -> Result<Self, Errno> {
let mut inbuf = Vec::new();
inbuf
.try_reserve(Self::INBUF_CAP)
.map_err(|_| Errno::ENOMEM)?;
let mut outbuf = Vec::new();
outbuf
.try_reserve(Self::OUTBUF_CAP)
.map_err(|_| Errno::ENOMEM)?;
Ok(Self { inbuf, outbuf })
}
fn obuf_write(&mut self, data: &[u8]) -> Result<(), Errno> {
if data.is_empty() {
return Ok(());
}
self.outbuf
.try_reserve(data.len())
.map_err(|_| Errno::ENOMEM)?;
self.outbuf.extend_from_slice(data);
Ok(())
}
fn emit_line<L>(&mut self, line: &[u8], lookup: &mut L) -> Result<(), Errno>
where
L: FnMut(u64) -> Option<UnixAddr>,
{
let has_nl = line.last() == Some(&b'\n');
let content = if has_nl {
&line[..line.len().saturating_sub(1)]
} else {
line
};
if let Some((inode, off)) = parse_proc_net_unix(content) {
if let Some(path) = content.get(off..) {
if let Some(addr) = lookup(inode) {
if let Some(addr) = unix_path_bytes(&addr) {
if path == XPath::from_bytes(addr).split().1.as_bytes() {
self.obuf_write(&content[..off])?;
self.obuf_write(addr)?;
if has_nl {
self.obuf_write(b"\n")?;
}
return Ok(());
}
}
}
}
}
self.obuf_write(line)
}
fn run<S, D, L>(&mut self, src: S, dst: D, mut lookup: L) -> Result<(), Errno>
where
S: AsFd,
D: AsFd,
L: FnMut(u64) -> Option<UnixAddr>,
{
loop {
let cap = self.inbuf.capacity();
let len = self.inbuf.len();
let free = cap.checked_sub(len).ok_or(Errno::EOVERFLOW)?;
if free == 0 {
self.inbuf
.try_reserve(Self::GROW_STEP)
.map_err(|_| Errno::ENOMEM)?;
continue;
}
let new_len = len.checked_add(free).ok_or(Errno::EOVERFLOW)?;
self.inbuf.resize(new_len, 0);
let tail = &mut self.inbuf[len..new_len];
let n = retry_on_eintr(|| safe_read(&src, tail))?;
if n == 0 {
self.inbuf.truncate(len);
break;
}
let keep = len.checked_add(n).ok_or(Errno::EOVERFLOW)?;
self.inbuf.truncate(keep);
}
let inbuf = take(&mut self.inbuf);
let mut start: usize = 0;
loop {
let slice = inbuf.get(start..).ok_or(Errno::EOVERFLOW)?;
if let Some(nl_rel) = memchr(b'\n', slice) {
let line_end = start
.checked_add(nl_rel)
.and_then(|i| i.checked_add(1))
.ok_or(Errno::EOVERFLOW)?;
let line = inbuf.get(start..line_end).ok_or(Errno::EOVERFLOW)?;
self.emit_line(line, &mut lookup)?;
start = line_end;
continue;
}
if start < inbuf.len() {
let line = &inbuf[start..];
self.emit_line(line, &mut lookup)?;
}
break;
}
write_all(&dst, &self.outbuf)
}
}
fn parse_proc_net_unix(line: &[u8]) -> Option<(u64, usize)> {
let mut idx = 0usize;
let mut inode = 0u64;
for field in 0..7 {
while idx < line.len() && line[idx] == b' ' {
idx = idx.checked_add(1)?;
}
let start = idx;
while idx < line.len() && line[idx] != b' ' {
idx = idx.checked_add(1)?;
}
if start == idx {
return None; }
if field == 6 {
inode = btoi::<u64>(line.get(start..idx)?).ok()?;
}
}
if line.get(idx) == Some(&b' ') {
let off = idx.checked_add(1)?;
if off < line.len() {
return Some((inode, off));
}
}
None
}
pub(crate) fn mask_proc_pid_status<S: AsFd, D: AsFd>(src: S, dst: D) -> Result<(), Errno> {
ProcPidStatusMasker::new()?.run(src, dst)
}
pub(crate) fn mask_proc_net_unix<S, D, L>(src: S, dst: D, lookup: L) -> Result<(), Errno>
where
S: AsFd,
D: AsFd,
L: FnMut(u64) -> Option<UnixAddr>,
{
ProcNetUnixMasker::new()?.run(src, dst, lookup)
}
#[cfg(test)]
mod tests {
use nix::{
fcntl::OFlag,
unistd::{pipe2, read, write},
};
use super::*;
const PROC_NET_UNIX_HDR: &[u8] = b"Num RefCount Protocol Flags Type St Inode Path\n";
fn run_mask(input: &[u8]) -> Result<Vec<u8>, Errno> {
let (in_rd, in_wr) = pipe2(OFlag::O_CLOEXEC)?;
let (out_rd, out_wr) = pipe2(OFlag::O_CLOEXEC)?;
let mut off = 0usize;
while off < input.len() {
match write(&in_wr, &input[off..]) {
Ok(0) => break,
Ok(n) => {
off = off.checked_add(n).ok_or(Errno::EOVERFLOW)?;
}
Err(e) => return Err(e),
}
}
drop(in_wr);
mask_proc_pid_status(&in_rd, &out_wr)?;
drop(out_wr);
let mut out = Vec::new();
let mut buf = [0u8; 1024];
loop {
match retry_on_eintr(|| read(&out_rd, &mut buf)) {
Ok(0) => break,
Ok(n) => out.extend_from_slice(&buf[..n]),
Err(e) => return Err(e),
}
}
Ok(out)
}
fn run_unix_mask(input: &[u8], map: &[(u64, &[u8])]) -> Result<Vec<u8>, Errno> {
let (in_rd, in_wr) = pipe2(OFlag::O_CLOEXEC)?;
let (out_rd, out_wr) = pipe2(OFlag::O_CLOEXEC)?;
let mut off = 0usize;
while off < input.len() {
match write(&in_wr, &input[off..]) {
Ok(0) => break,
Ok(n) => off = off.checked_add(n).ok_or(Errno::EOVERFLOW)?,
Err(e) => return Err(e),
}
}
drop(in_wr);
mask_proc_net_unix(&in_rd, &out_wr, |inode| {
map.iter()
.find(|(i, _)| *i == inode)
.map(|(_, p)| UnixAddr::new(std::path::Path::new(std::str::from_utf8(p).unwrap())))
.transpose()
.unwrap()
})?;
drop(out_wr);
let mut out = Vec::new();
let mut buf = [0u8; 1024];
loop {
match retry_on_eintr(|| read(&out_rd, &mut buf)) {
Ok(0) => break,
Ok(n) => out.extend_from_slice(&buf[..n]),
Err(e) => return Err(e),
}
}
Ok(out)
}
#[test]
fn test_proc_pid_status_1() {
let input = b"TracerPid:\t123\nNoNewPrivs:\t1\nSeccomp:\t2\nSeccomp_filters:\t7\n";
let out = run_mask(input).unwrap();
let expected = b"TracerPid:\t0\nNoNewPrivs:\t0\nSeccomp:\t0\nSeccomp_filters:\t0\n";
assert_eq!(&out, expected);
}
#[test]
fn test_proc_pid_status_2() {
let input = b"TracerPid:\t 456\nSeccomp:\t\t 2\n";
let out = run_mask(input).unwrap();
let expected = b"TracerPid:\t 0\nSeccomp:\t\t 0\n";
assert_eq!(&out, expected);
}
#[test]
fn test_proc_pid_status_3() {
let input = b"TracerPid:\t0\nNoNewPrivs:\t0\n";
let out = run_mask(input).unwrap();
assert_eq!(&out, input);
}
#[test]
fn test_proc_pid_status_4() {
let input = b"TracerPid:\t123 extra_garbage\n";
let out = run_mask(input).unwrap();
assert_eq!(&out, b"TracerPid:\t0\n");
}
#[test]
fn test_proc_pid_status_5() {
let input = b"Speculation_Store_Bypass: \t\tthread mitigated\n";
let out = run_mask(input).unwrap();
assert_eq!(&out, b"Speculation_Store_Bypass: \t\tvulnerable\n");
}
#[test]
fn test_proc_pid_status_6() {
let input = b"Speculation_Store_Bypass:\t force mitigated\n";
let out = run_mask(input).unwrap();
assert_eq!(&out, b"Speculation_Store_Bypass:\t vulnerable\n");
}
#[test]
fn test_proc_pid_status_7() {
let input = b"Speculation_Store_Bypass:\tthread force mitigated\n";
let out = run_mask(input).unwrap();
assert_eq!(&out, b"Speculation_Store_Bypass:\tvulnerable\n");
}
#[test]
fn test_proc_pid_status_8() {
let input = b"SpeculationIndirectBranch:\t conditional force disabled\n";
let out = run_mask(input).unwrap();
assert_eq!(&out, b"SpeculationIndirectBranch:\t always enabled\n");
}
#[test]
fn test_proc_pid_status_9() {
let input = b"SpeculationIndirectBranch: \talways force disabled\n";
let out = run_mask(input).unwrap();
assert_eq!(&out, b"SpeculationIndirectBranch: \talways enabled\n");
}
#[test]
fn test_proc_pid_status_10() {
let input = b"SpeculationIndirectBranch:\t \tconditional enabled\n";
let out = run_mask(input).unwrap();
assert_eq!(&out, b"SpeculationIndirectBranch:\t \talways enabled\n");
}
#[test]
fn test_proc_pid_status_11() {
let input = concat!(
"NoNewPrivs:\t1\n",
"TracerPid:\t42\n",
"Seccomp:\t2\n",
"Seccomp_filters:\t3\n",
)
.as_bytes();
let expected = concat!(
"NoNewPrivs:\t0\n",
"TracerPid:\t0\n",
"Seccomp:\t0\n",
"Seccomp_filters:\t0\n",
)
.as_bytes();
let out = run_mask(input).unwrap();
assert_eq!(&out, expected);
}
#[test]
fn test_proc_pid_status_12() {
let input = concat!(
"SpeculationIndirectBranch: \t conditional enabled\n",
"NoNewPrivs:\t1\n",
"Speculation_Store_Bypass: \t\t thread force mitigated\n",
"Seccomp:\t 2\n",
"TracerPid: \t42\n",
"Seccomp_filters: \t\t 3\n",
)
.as_bytes();
let expected = concat!(
"SpeculationIndirectBranch: \t always enabled\n",
"NoNewPrivs:\t0\n",
"Speculation_Store_Bypass: \t\t vulnerable\n",
"Seccomp:\t 0\n",
"TracerPid: \t0\n",
"Seccomp_filters: \t\t 0\n",
)
.as_bytes();
let out = run_mask(input).unwrap();
assert_eq!(&out, expected);
}
#[test]
fn test_proc_pid_status_13() {
let input = b"TracerPid:\t \t \t 999\nNoNewPrivs:\t\t\t3\n";
let out = run_mask(input).unwrap();
assert_eq!(&out, b"TracerPid:\t \t \t 0\nNoNewPrivs:\t\t\t0\n");
}
#[test]
fn test_proc_pid_status_14() {
let input = b"Seccomp:\t\t\n";
let out = run_mask(input).unwrap();
assert_eq!(&out, input);
}
#[test]
fn test_proc_pid_status_15() {
let input = b"Name:\tcat\nState:\tS (sleeping)\nThreads:\t4\n";
let out = run_mask(input).unwrap();
assert_eq!(&out, input);
}
#[test]
fn test_proc_pid_status_16() {
let input = b"Name:\tSeccomp:\t2 (not a header)\n";
let out = run_mask(input).unwrap();
assert_eq!(&out, input);
}
#[test]
fn test_proc_pid_status_17() {
let mut line = b"TracerPid:\t".to_vec();
line.extend(std::iter::repeat(b'9').take(9000));
line.push(b'\n');
let out = run_mask(&line).unwrap();
assert_eq!(out, b"TracerPid:\t0\n".to_vec());
}
#[test]
fn test_proc_pid_status_18() {
let mut line = vec![b'A'; 10000];
line.push(b'\n');
let out = run_mask(&line).unwrap();
assert_eq!(&out, &line);
}
#[test]
fn test_proc_pid_status_19() {
let input = concat!(
"Name:\tmyproc\n",
"TracerPid:\t42\n",
"Speculation_Store_Bypass:\tthread force mitigated\n",
"NoNewPrivs:\t1\n",
"SpeculationIndirectBranch:\t conditional force disabled\n",
"Seccomp:\t2\n",
"Threads:\t5\n",
"Seccomp_filters:\t3\n",
)
.as_bytes();
let expected = concat!(
"Name:\tmyproc\n",
"TracerPid:\t0\n",
"Speculation_Store_Bypass:\tvulnerable\n",
"NoNewPrivs:\t0\n",
"SpeculationIndirectBranch:\t always enabled\n",
"Seccomp:\t0\n",
"Threads:\t5\n",
"Seccomp_filters:\t0\n",
)
.as_bytes();
let out = run_mask(input).unwrap();
assert_eq!(&out, expected);
}
#[test]
fn test_proc_pid_status_20() {
let input = b"Name:\tno_nl_at_end";
let out = run_mask(input).unwrap();
assert_eq!(&out, input);
}
#[test]
fn test_proc_pid_status_21() {
let mut long = vec![b'X'; 4096];
long.push(b'\n');
let input = [
b"Name:\tmyproc\n".as_ref(),
b"State:\tS (sleeping)\n".as_ref(),
b"TracerPid:\t 456\n".as_ref(),
b"NoNewPrivs:\t1\n".as_ref(),
b"Speculation_Store_Bypass: \tthread force mitigated\n".as_ref(),
b"SpeculationIndirectBranch:\t conditional force disabled\n".as_ref(),
b"Seccomp:\t2\n".as_ref(),
b"Threads:\t5\n".as_ref(),
b"Seccomp_filters:\t3\n".as_ref(),
b"Note:\tSeccomp:\t2 (not a header)\n".as_ref(),
&long,
b"Name:\tno_nl_at_end".as_ref(),
]
.concat();
let expected = [
b"Name:\tmyproc\n".as_ref(),
b"State:\tS (sleeping)\n".as_ref(),
b"TracerPid:\t 0\n".as_ref(),
b"NoNewPrivs:\t0\n".as_ref(),
b"Speculation_Store_Bypass: \tvulnerable\n".as_ref(),
b"SpeculationIndirectBranch:\t always enabled\n".as_ref(),
b"Seccomp:\t0\n".as_ref(),
b"Threads:\t5\n".as_ref(),
b"Seccomp_filters:\t0\n".as_ref(),
b"Note:\tSeccomp:\t2 (not a header)\n".as_ref(),
&long,
b"Name:\tno_nl_at_end".as_ref(),
]
.concat();
let out = run_mask(&input).unwrap();
assert_eq!(&out, &expected);
}
#[test]
fn test_proc_net_unix_1() {
let out = run_unix_mask(PROC_NET_UNIX_HDR, &[]).unwrap();
assert_eq!(&out, PROC_NET_UNIX_HDR);
}
#[test]
fn test_proc_net_unix_2() {
let input = b"0000000000000000: 00000002 00000000 00000000 0001 01 16993800 foo.sock\n";
let out = run_unix_mask(input, &[(16993800, b"/tmp/dir/foo.sock")]).unwrap();
assert_eq!(
&out,
b"0000000000000000: 00000002 00000000 00000000 0001 01 16993800 /tmp/dir/foo.sock\n"
);
}
#[test]
fn test_proc_net_unix_3() {
let input = b"0000000000000000: 00000002 00000000 00000000 0001 01 4242 foo.sock\n";
let out = run_unix_mask(input, &[]).unwrap();
assert_eq!(&out, input);
}
#[test]
fn test_proc_net_unix_4() {
let input = b"0000000000000000: 00000002 00000000 00000000 0001 01 7 foo.sock\n";
let out = run_unix_mask(input, &[(7, b"/tmp/bar.sock")]).unwrap();
assert_eq!(&out, input);
}
#[test]
fn test_proc_net_unix_5() {
let input = b"0000000000000000: 00000002 00000000 00010000 0001 01 9 @abstract\n";
let out = run_unix_mask(input, &[]).unwrap();
assert_eq!(&out, input);
}
#[test]
fn test_proc_net_unix_6() {
let input = b"0000000000000000: 00000002 00000000 00000000 0001 01 55 @foo\n";
let out = run_unix_mask(input, &[(55, b"/tmp/@foo")]).unwrap();
assert_eq!(
&out,
b"0000000000000000: 00000002 00000000 00000000 0001 01 55 /tmp/@foo\n"
);
}
#[test]
fn test_proc_net_unix_7() {
let input = b"0000000000000000: 00000002 00000000 00000000 0001 01 12345\n";
let out = run_unix_mask(input, &[(12345, b"/tmp/x.sock")]).unwrap();
assert_eq!(&out, input);
}
#[test]
fn test_proc_net_unix_8() {
let input = b"0000000000000000: 00000002 00000000 00000000 0001 01 42 foo.sock\n";
let out = run_unix_mask(input, &[(42, b"/run/foo.sock")]).unwrap();
assert_eq!(
&out,
b"0000000000000000: 00000002 00000000 00000000 0001 01 42 /run/foo.sock\n"
);
}
#[test]
fn test_proc_net_unix_9() {
let input = [
PROC_NET_UNIX_HDR,
b"0000000000000000: 00000002 00000000 00000000 0001 01 100 a.sock\n".as_ref(),
b"0000000000000000: 00000002 00000000 00010000 0001 01 101 @abs\n".as_ref(),
b"0000000000000000: 00000003 00000000 00000000 0001 03 102 b.sock".as_ref(),
]
.concat();
let expected = [
PROC_NET_UNIX_HDR,
b"0000000000000000: 00000002 00000000 00000000 0001 01 100 /x/a.sock\n".as_ref(),
b"0000000000000000: 00000002 00000000 00010000 0001 01 101 @abs\n".as_ref(),
b"0000000000000000: 00000003 00000000 00000000 0001 03 102 /y/b.sock".as_ref(),
]
.concat();
let out = run_unix_mask(&input, &[(100, b"/x/a.sock"), (102, b"/y/b.sock")]).unwrap();
assert_eq!(&out, &expected);
}
#[test]
fn test_proc_net_unix_10() {
let line = b"0000000000000000: 00000002 00000000 00000000 0001 01 555 p.sock";
let (inode, off) = parse_proc_net_unix(line).unwrap();
assert_eq!(inode, 555);
assert_eq!(&line[off..], b"p.sock");
assert!(
parse_proc_net_unix(b"Num RefCount Protocol Flags Type St Inode Path")
.is_none()
);
assert!(
parse_proc_net_unix(b"0000000000000000: 00000002 00000000 00000000 0001 01 12345")
.is_none()
);
}
}