use std::{ffi::CStr, sync::OnceLock};
use digest::Digest;
use memchr::arch::all::{is_equal, is_prefix, is_suffix};
use subtle::ConstantTimeEq;
use crate::hash::{get_at_random_hex, hex_encode_lower, SafeHash};
static SYD_ID: OnceLock<[u8; 64]> = OnceLock::new();
#[cfg(not(debug_assertions))]
static SYD_NAME: OnceLock<[u8; 16]> = OnceLock::new();
static SYD_MEMFD_BOX: OnceLock<[u8; 73]> = OnceLock::new(); static SYD_MEMFD_AES: OnceLock<[u8; 73]> = OnceLock::new(); static SYD_MEMFD_MID: OnceLock<[u8; 84]> = OnceLock::new(); static SYD_MEMFD_VER: OnceLock<[u8; 82]> = OnceLock::new(); static SYD_MEMFD_REL: OnceLock<[u8; 95]> = OnceLock::new(); static SYD_MEMFD_UPT: OnceLock<[u8; 81]> = OnceLock::new();
static SYD_MEMFD_PFX: OnceLock<[u8; 69]> = OnceLock::new();
pub struct SydId;
impl SydId {
pub fn get_name(base: &'static str) -> &'static str {
#[cfg(debug_assertions)]
{
base
}
#[cfg(not(debug_assertions))]
{
let name = crate::config::ENV_SKIP_NAME;
let skip = crate::confine::secure_getenv(name).is_some();
if skip {
return base;
}
let name = SYD_NAME.get_or_init(syd_name_init);
unsafe { std::str::from_utf8_unchecked(&name[..15]) }
}
}
pub fn get_cname(base: &'static CStr) -> &'static CStr {
#[cfg(debug_assertions)]
{
base
}
#[cfg(not(debug_assertions))]
{
let _ = base;
let name = SYD_NAME.get_or_init(syd_name_init);
unsafe { CStr::from_bytes_with_nul_unchecked(name) }
}
}
pub(crate) fn get() -> &'static [u8; 64] {
SYD_ID.get_or_init(syd_id_init)
}
pub(crate) fn get_str() -> &'static str {
let id = SYD_ID.get_or_init(syd_id_init);
unsafe { std::str::from_utf8_unchecked(id) }
}
pub(crate) fn get_prefix() -> &'static [u8] {
SYD_MEMFD_PFX.get_or_init(|| {
let id_bytes = SYD_ID.get_or_init(syd_id_init);
let mut name = [0u8; 69];
name[0..4].copy_from_slice(b"syd-");
name[4..68].copy_from_slice(id_bytes);
name[68..69].copy_from_slice(b"/");
name
})
}
pub(crate) fn get_memfd_box() -> &'static CStr {
let buf = SYD_MEMFD_BOX.get_or_init(|| {
let id_bytes = SYD_ID.get_or_init(syd_id_init);
let mut name = [0u8; 73];
name[0..4].copy_from_slice(b"syd-");
name[4..68].copy_from_slice(id_bytes);
name[68..72].copy_from_slice(b"/box");
name[72..73].copy_from_slice(b"\0");
name
});
unsafe { CStr::from_bytes_with_nul_unchecked(buf) }
}
pub(crate) fn get_memfd_aes() -> &'static CStr {
let buf = SYD_MEMFD_AES.get_or_init(|| {
let id_bytes = SYD_ID.get_or_init(syd_id_init);
let mut name = [0u8; 73];
name[0..4].copy_from_slice(b"syd-");
name[4..68].copy_from_slice(id_bytes);
name[68..72].copy_from_slice(b"/aes");
name[72..73].copy_from_slice(b"\0");
name
});
unsafe { CStr::from_bytes_with_nul_unchecked(buf) }
}
pub(crate) fn get_memfd_machine_id() -> &'static CStr {
let buf = SYD_MEMFD_MID.get_or_init(|| {
let id_bytes = SYD_ID.get_or_init(syd_id_init);
let mut name = [0u8; 84];
name[0..4].copy_from_slice(b"syd-");
name[4..68].copy_from_slice(id_bytes);
name[68..83].copy_from_slice(b"/etc/machine-id");
name[83..84].copy_from_slice(b"\0");
name
});
unsafe { CStr::from_bytes_with_nul_unchecked(buf) }
}
pub(crate) fn get_memfd_proc_version() -> &'static CStr {
let buf = SYD_MEMFD_VER.get_or_init(|| {
let id_bytes = SYD_ID.get_or_init(syd_id_init);
let mut name = [0u8; 82];
name[0..4].copy_from_slice(b"syd-");
name[4..68].copy_from_slice(id_bytes);
name[68..81].copy_from_slice(b"/proc/version");
name[81..82].copy_from_slice(b"\0");
name
});
unsafe { CStr::from_bytes_with_nul_unchecked(buf) }
}
pub(crate) fn get_memfd_osrelease() -> &'static CStr {
let buf = SYD_MEMFD_REL.get_or_init(|| {
let id_bytes = SYD_ID.get_or_init(syd_id_init);
let mut name = [0u8; 95];
name[0..4].copy_from_slice(b"syd-");
name[4..68].copy_from_slice(id_bytes);
name[68..94].copy_from_slice(b"/proc/sys/kernel/osrelease");
name[94..95].copy_from_slice(b"\0");
name
});
unsafe { CStr::from_bytes_with_nul_unchecked(buf) }
}
pub(crate) fn get_memfd_uptime() -> &'static CStr {
let buf = SYD_MEMFD_UPT.get_or_init(|| {
let id_bytes = SYD_ID.get_or_init(syd_id_init);
let mut name = [0u8; 81];
name[0..4].copy_from_slice(b"syd-");
name[4..68].copy_from_slice(id_bytes);
name[68..80].copy_from_slice(b"/proc/uptime");
name[80..81].copy_from_slice(b"\0");
name
});
unsafe { CStr::from_bytes_with_nul_unchecked(buf) }
}
pub(crate) fn is_syd_memfd_name(name: &[u8]) -> bool {
match_syd_id(name, b"syd-")
}
pub(crate) fn is_syd_memfd_path(path: &[u8]) -> bool {
match_syd_id(path, b"!memfd:syd-")
}
pub(crate) fn is_syd_memfd_real(path: &[u8], suffix: &[u8]) -> bool {
match_syd_id2(path, b"/memfd:syd-", suffix, true)
}
pub(crate) fn is_syd_memfd_box(path: &[u8]) -> bool {
match_syd_id2(path, b"!memfd:syd-", b"/box", false)
}
pub(crate) fn is_syd_memfd_aes(path: &[u8]) -> bool {
match_syd_id2(path, b"!memfd:syd-", b"/aes", false)
}
pub(crate) fn strip_syd_memfd(buf: &[u8]) -> Option<&[u8]> {
const PFX: &[u8] = b"/memfd:syd-";
const DEL: &[u8] = b" (deleted)";
const LEN: usize = PFX.len() + 64;
if buf.len() < LEN || !is_prefix(buf, PFX) || !is_suffix(buf, DEL) {
return None;
}
let this_id = SYD_ID.get_or_init(syd_id_init);
let that_id = &buf[PFX.len()..LEN];
#[expect(clippy::arithmetic_side_effects)]
if bool::from(this_id.ct_eq(that_id)) {
Some(&buf[LEN..(buf.len() - DEL.len())])
} else {
None
}
}
}
fn syd_id_init() -> [u8; 64] {
#[expect(clippy::disallowed_methods)]
let cookie = get_at_random_hex(false).unwrap();
assert_eq!(cookie.len(), 32);
let checksum = <SafeHash as Digest>::digest(cookie.as_bytes());
#[expect(clippy::disallowed_methods)]
let id_bytes = hex_encode_lower(checksum.as_slice()).unwrap();
let id_bytes = id_bytes.as_bytes();
assert_eq!(id_bytes.len(), 64);
#[expect(clippy::disallowed_methods)]
id_bytes
.try_into()
.expect("BUG: syd_id_init: invalid id_bytes")
}
#[cfg(not(debug_assertions))]
fn syd_name_init() -> [u8; 16] {
let id = SYD_ID.get_or_init(syd_id_init);
let mut name = [0u8; 16];
name[0..4].copy_from_slice(b"syd_");
name[4..15].copy_from_slice(&id[..11]);
name
}
fn match_syd_id(data: &[u8], prefix: &[u8]) -> bool {
#[expect(clippy::arithmetic_side_effects)]
if data.len() < prefix.len() + 64 {
return false;
}
#[expect(clippy::arithmetic_side_effects)]
{
is_prefix(data, prefix) && {
let this_id = SYD_ID.get_or_init(syd_id_init);
let that_id = &data[prefix.len()..prefix.len() + 64];
this_id.ct_eq(that_id).into()
}
}
}
fn match_syd_id2(data: &[u8], prefix: &[u8], suffix: &[u8], deleted: bool) -> bool {
const DEL: &[u8] = b" (deleted)";
#[expect(clippy::arithmetic_side_effects)]
let id_end = prefix.len() + 64;
let deleted_len = if deleted { DEL.len() } else { 0 };
#[expect(clippy::arithmetic_side_effects)]
if data.len() != id_end + suffix.len() + deleted_len {
return false;
}
is_prefix(data, prefix) && {
let after_id = &data[id_end..];
is_prefix(after_id, suffix)
&& if deleted {
!suffix.is_empty() && is_equal(&after_id[suffix.len()..], DEL)
} else {
after_id.len() == suffix.len()
}
&& {
let this_id = SYD_ID.get_or_init(syd_id_init);
let that_id = &data[prefix.len()..id_end];
this_id.ct_eq(that_id).into()
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_match_syd_id_1() {
assert!(!match_syd_id(b"", b"syd-"));
assert!(!match_syd_id(b"syd-", b"syd-"));
assert!(!match_syd_id(b"syd-abc", b"syd-"));
assert!(!match_syd_id(b"!memfd:syd-", b"!memfd:syd-"));
assert!(!match_syd_id(b"/memfd:syd-", b"/memfd:syd-"));
let short_id = b"0123456789abcdef";
assert!(!match_syd_id(
&[b"syd-".as_ref(), short_id].concat(),
b"syd-"
));
assert!(!match_syd_id(
b"syd-0000000000000000000000000000000000000000000000000000000000extra",
b"syd-"
));
assert!(!match_syd_id(
b"syd-0000000000000000000000000000000000000000000000000000000000",
b"syd-"
));
assert!(!match_syd_id(
b"syd-FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
b"syd-"
));
}
#[test]
fn test_match_syd_id_2() {
let id = SydId::get();
let wrong = b"foo-";
let mut buf = Vec::with_capacity(wrong.len() + id.len());
buf.extend_from_slice(wrong);
buf.extend_from_slice(id);
assert!(!match_syd_id(&buf, b"syd-"));
}
#[test]
fn test_match_syd_id_3() {
let id = SydId::get();
let mut buf = Vec::with_capacity(4 + id.len());
buf.extend_from_slice(b"syd-");
buf.extend_from_slice(id);
assert!(match_syd_id(&buf, b"syd-"));
}
#[test]
fn test_match_syd_id_4() {
let id = SydId::get();
let mut buf = Vec::with_capacity(4 + id.len() + 4);
buf.extend_from_slice(b"syd-");
buf.extend_from_slice(id);
buf.extend_from_slice(b"test");
assert!(match_syd_id(&buf, b"syd-"));
}
#[test]
fn test_match_syd_id_5() {
let id = SydId::get();
let mut buf = Vec::with_capacity(11 + id.len());
buf.extend_from_slice(b"!memfd:syd-");
buf.extend_from_slice(id);
assert!(match_syd_id(&buf, b"!memfd:syd-"));
}
#[test]
fn test_match_syd_id_6() {
let id = SydId::get();
let mut buf = Vec::with_capacity(11 + id.len());
buf.extend_from_slice(b"/memfd:syd-");
buf.extend_from_slice(id);
assert!(match_syd_id(&buf, b"/memfd:syd-"));
}
#[test]
fn test_match_syd_id_7() {
let id = SydId::get();
let mut lower_buf = Vec::with_capacity(4 + id.len());
lower_buf.extend_from_slice(b"syd-");
lower_buf.extend_from_slice(id);
assert!(match_syd_id(&lower_buf, b"syd-"));
let mut upper_buf = Vec::with_capacity(4 + id.len());
upper_buf.extend_from_slice(b"SYD-");
upper_buf.extend_from_slice(id);
assert!(!match_syd_id(&upper_buf, b"syd-"));
}
#[test]
fn test_match_syd_id2_1() {
assert!(!match_syd_id2(b"", b"syd-", b"/box", false));
assert!(!match_syd_id2(b"syd-", b"syd-", b"/box", false));
assert!(!match_syd_id2(b"syd-abc", b"syd-", b"/box", false));
assert!(!match_syd_id2(
b"syd-0000000000000000000000000000000000000000000000000000000000/box",
b"syd-",
b"/box",
false
));
}
#[test]
fn test_match_syd_id2_2() {
let id = SydId::get();
let mut buf = Vec::with_capacity(4 + id.len() + 4);
buf.extend_from_slice(b"syd-");
buf.extend_from_slice(id);
buf.extend_from_slice(b"/box");
buf.push(b'x');
assert!(!match_syd_id2(&buf, b"syd-", b"/box", false));
}
#[test]
fn test_match_syd_id2_3() {
let id = SydId::get();
let mut buf = Vec::with_capacity(4 + id.len() + 4);
buf.extend_from_slice(b"foo-");
buf.extend_from_slice(id);
buf.extend_from_slice(b"/box");
assert!(!match_syd_id2(&buf, b"syd-", b"/box", false));
}
#[test]
fn test_match_syd_id2_4() {
let id = SydId::get();
let mut buf = Vec::with_capacity(4 + id.len() + 4);
buf.extend_from_slice(b"syd-");
buf.extend_from_slice(id);
buf.extend_from_slice(b"/aes");
assert!(!match_syd_id2(&buf, b"syd-", b"/box", false));
}
#[test]
fn test_match_syd_id2_5() {
let id = SydId::get();
let mut buf = Vec::with_capacity(4 + id.len() + 4);
buf.extend_from_slice(b"syd-");
buf.extend_from_slice(id);
buf.extend_from_slice(b"/box");
assert!(match_syd_id2(&buf, b"syd-", b"/box", false));
}
#[test]
fn test_match_syd_id2_6() {
let id = SydId::get();
let mut buf = Vec::with_capacity(4 + id.len() + 4 + 10);
buf.extend_from_slice(b"syd-");
buf.extend_from_slice(id);
buf.extend_from_slice(b"/box");
buf.extend_from_slice(b" (deleted)");
assert!(match_syd_id2(&buf, b"syd-", b"/box", true));
}
#[test]
fn test_match_syd_id2_7() {
let id = SydId::get();
let mut buf = Vec::with_capacity(4 + id.len() + 4);
buf.extend_from_slice(b"syd-");
buf.extend_from_slice(id);
buf.extend_from_slice(b"/box");
assert!(!match_syd_id2(&buf, b"syd-", b"/box", true));
}
#[test]
fn test_match_syd_id2_8() {
let id = SydId::get();
let mut buf = Vec::with_capacity(4 + id.len() + 4 + 10);
buf.extend_from_slice(b"syd-");
buf.extend_from_slice(id);
buf.extend_from_slice(b"/box");
buf.extend_from_slice(b" (deleted)");
assert!(!match_syd_id2(&buf, b"syd-", b"/box", false));
}
#[test]
fn test_match_syd_id2_9() {
let id = SydId::get();
let mut buf = Vec::with_capacity(4 + id.len() + 4 + 8);
buf.extend_from_slice(b"syd-");
buf.extend_from_slice(id);
buf.extend_from_slice(b"/box");
buf.extend_from_slice(b" (wrong)");
assert!(!match_syd_id2(&buf, b"syd-", b"/box", true));
}
#[test]
fn test_match_syd_id2_10() {
let id = SydId::get();
let long_suffix = b"/etc/machine-id";
let mut buf = Vec::with_capacity(4 + id.len() + long_suffix.len());
buf.extend_from_slice(b"syd-");
buf.extend_from_slice(id);
buf.extend_from_slice(long_suffix);
assert!(match_syd_id2(&buf, b"syd-", long_suffix, false));
}
#[test]
fn test_match_syd_id2_11() {
let id = SydId::get();
let mut buf = Vec::with_capacity(11 + id.len() + 4);
buf.extend_from_slice(b"!memfd:syd-");
buf.extend_from_slice(id);
buf.extend_from_slice(b"/box");
assert!(match_syd_id2(&buf, b"!memfd:syd-", b"/box", false));
}
#[test]
fn test_match_syd_id2_12() {
let id = SydId::get();
let mut buf = Vec::with_capacity(11 + id.len() + 4 + 10);
buf.extend_from_slice(b"/memfd:syd-");
buf.extend_from_slice(id);
buf.extend_from_slice(b"/box");
buf.extend_from_slice(b" (deleted)");
assert!(match_syd_id2(&buf, b"/memfd:syd-", b"/box", true));
}
#[test]
fn test_match_syd_id2_13() {
let id = SydId::get();
let mut match_buf = Vec::with_capacity(4 + id.len() + 4);
match_buf.extend_from_slice(b"syd-");
match_buf.extend_from_slice(id);
match_buf.extend_from_slice(b"/box");
let mut mismatch_buf = Vec::with_capacity(4 + id.len() + 4);
mismatch_buf.extend_from_slice(b"syd-");
mismatch_buf.extend_from_slice(id);
mismatch_buf.extend_from_slice(b"/aes");
let match_result = match_syd_id2(&match_buf, b"syd-", b"/box", false);
let mismatch_result = match_syd_id2(&mismatch_buf, b"syd-", b"/box", false);
assert!(match_result);
assert!(!mismatch_result);
}
#[test]
fn test_match_syd_id2_14() {
let id = SydId::get();
let mut exact_buf = Vec::with_capacity(4 + id.len() + 4);
exact_buf.extend_from_slice(b"syd-");
exact_buf.extend_from_slice(id);
exact_buf.extend_from_slice(b"/box");
let mut long_buf = exact_buf.clone();
long_buf.push(b'x');
let mut short_buf = exact_buf.clone();
short_buf.pop();
assert!(match_syd_id2(&exact_buf, b"syd-", b"/box", false));
assert!(!match_syd_id2(&long_buf, b"syd-", b"/box", false));
assert!(!match_syd_id2(&short_buf, b"syd-", b"/box", false));
}
#[test]
fn test_match_syd_id2_15() {
let id = SydId::get();
let mut buf = Vec::with_capacity(4 + id.len() + 4);
buf.extend_from_slice(b"syd-");
buf.extend_from_slice(id);
buf.extend_from_slice(b"/box");
assert!(match_syd_id2(&buf, b"syd-", b"/box", false));
assert!(!match_syd_id2(&buf, b"syd", b"/box", false));
assert!(!match_syd_id2(&buf, b"syd--", b"/box", false));
assert!(!match_syd_id2(&buf, b"x", b"/box", false));
}
#[test]
fn test_match_syd_id2_16() {
let id = SydId::get();
let mut buf = Vec::with_capacity(4 + id.len());
buf.extend_from_slice(b"syd-");
buf.extend_from_slice(id);
assert!(match_syd_id2(&buf, b"syd-", b"", false));
}
#[test]
fn test_match_syd_id2_17() {
let id = SydId::get();
let mut buf = Vec::with_capacity(id.len() + 4);
buf.extend_from_slice(id);
buf.extend_from_slice(b"/box");
assert!(match_syd_id2(&buf, b"", b"/box", false));
}
#[test]
fn test_match_syd_id2_18() {
let id = SydId::get();
let mut buf = Vec::with_capacity(4 + id.len() + 10);
buf.extend_from_slice(b"syd-");
buf.extend_from_slice(id);
buf.extend_from_slice(b" (deleted)");
assert!(!match_syd_id2(&buf, b"syd-", b"", true));
}
#[test]
fn test_match_syd_id2_19() {
let id = SydId::get();
let mut buf = Vec::with_capacity(11 + id.len() + 4);
buf.extend_from_slice(b"!memfd:syd-");
buf.extend_from_slice(id);
buf.extend_from_slice(b"/box");
assert!(match_syd_id2(&buf, b"!memfd:syd-", b"/box", false));
assert!(!match_syd_id2(&buf, b"syd-", b"/box", false));
assert!(!match_syd_id2(&buf, b"prefix-", b"/box", false));
}
#[test]
fn test_match_syd_id2_20() {
let id = SydId::get();
let mut buf = Vec::with_capacity(4 + id.len() + 4);
buf.extend_from_slice(b"syd-");
buf.extend_from_slice(&id[..32]);
buf.extend_from_slice(b"00000000000000000000000000");
buf.extend_from_slice(b"/box");
assert!(!match_syd_id2(&buf, b"syd-", b"/box", false));
}
#[test]
fn test_match_syd_id2_21() {
let id = SydId::get();
let mut buf = Vec::with_capacity(4 + id.len() + 5);
buf.extend_from_slice(b"syd-");
buf.extend_from_slice(id);
buf.extend_from_slice(b"/box\x00");
assert!(!match_syd_id2(&buf, b"syd-", b"/box", false));
}
#[test]
fn test_match_syd_id2_22() {
let id = SydId::get();
let mut buf = Vec::with_capacity(4 + id.len() + 4 + 11);
buf.extend_from_slice(b"syd-");
buf.extend_from_slice(id);
buf.extend_from_slice(b"/box");
buf.extend_from_slice(b" (deleted)x");
assert!(!match_syd_id2(&buf, b"syd-", b"/box", true));
}
#[test]
fn test_match_syd_id2_23() {
let id = SydId::get();
let mut buf = Vec::with_capacity(4 + id.len() + 4 + 9);
buf.extend_from_slice(b"syd-");
buf.extend_from_slice(id);
buf.extend_from_slice(b"/box");
buf.extend_from_slice(b" (delete");
assert!(!match_syd_id2(&buf, b"syd-", b"/box", true));
}
#[test]
fn test_match_syd_id2_24() {
let id = SydId::get();
let suffix = b"/proc/sys/kernel/osrelease";
let mut buf = Vec::with_capacity(4 + id.len() + suffix.len());
buf.extend_from_slice(b"syd-");
buf.extend_from_slice(id);
buf.extend_from_slice(suffix);
assert!(match_syd_id2(&buf, b"syd-", suffix, false));
}
}