shadowsocks/shadowsocks-rust · error
username
Error message
username
What it means
This panic comes from `.expect("username")` on `CString::new(uname)` in `run_as_user` (src/sys.rs:92). `CString::new` fails only when the username string contains an interior NUL byte (`\0`), which cannot be represented in a C string for `libc::getpwnam`. The panic occurs while looking up the user's passwd entry after parsing the `--user` value as a UID succeeded but `getpwuid` returned null.
Source
Thrown at src/sys.rs:92
}
}
}
/// setuid(), setgid() for a specific user or uid
#[cfg(unix)]
pub fn run_as_user(uname: &str) -> std::io::Result<()> {
use log::error;
use std::{
ffi::{CStr, CString},
io::{Error, ErrorKind},
};
unsafe {
let pwd = match uname.parse::<libc::uid_t>() {
Ok(uid) => {
let mut pwd = libc::getpwuid(uid);
if pwd.is_null() {
let uname = CString::new(uname).expect("username");
pwd = libc::getpwnam(uname.as_ptr())
}
pwd
}
Err(..) => {
let uname = CString::new(uname).expect("username");
libc::getpwnam(uname.as_ptr())
}
};
if pwd.is_null() {
return Err(Error::new(ErrorKind::InvalidInput, format!("user {} not found", uname)));
}
let pwd = &*pwd;
// setgid first, because we may not allowed to do it anymore after setuid
if libc::setgid(pwd.pw_gid as libc::gid_t) != 0 {View on GitHub (pinned to 8eb0f0a65b)
Solutions
- Pass a normal NUL-free username to --user, e.g. `--user nobody`.
- If the user was given as a numeric UID, ensure a passwd entry exists or pass the username instead so getpwnam is used directly.
- Sanitize the user string in the invoking script (strip control characters) before launching.
- Patch the code to propagate `CString::new(...)` errors as `Error::new(ErrorKind::InvalidInput, ...)` instead of `.expect`.
Example fix
// before
let uname = CString::new(uname).expect("username");
// after
let uname = CString::new(uname).map_err(|e| {
Error::new(ErrorKind::InvalidInput, format!("invalid user name: {e}"))
})?; Defensive patterns
Strategy: validation
Validate before calling
fn is_safe_username(u: &str) -> bool {
!u.is_empty() && !u.contains('\0') && u.bytes().all(|b| b.is_ascii_graphic() || b == b'_')
}
assert!(is_safe_username(&user_arg), "--user contains invalid characters"); Type guard
fn nul_free(s: &str) -> Option<&str> {
if s.contains('\0') { None } else { Some(s) }
} Try / catch
// If patching run_as_user
let uname = CString::new(uname).map_err(|e| {
Error::new(ErrorKind::InvalidInput, format!("user name contains NUL byte: {e}"))
})?; Prevention
- Validate the --user value (alphanumeric/underscore only) in launcher scripts.
- Never build the user argument from binary or untrusted substituted input.
- Prefer passing a real username over a numeric UID when switching privileges.
When it happens
Trigger: Calling `run_as_user` with a username containing an embedded NUL byte — realistically only via a CLI argument or config value built from binary data, an over-long truncated buffer, or programmatic callers passing a String with `\0` inside.
Common situations: Rare: a `--user` argument sourced from corrupted input, environment substitution gone wrong, or a wrapper script inserting null bytes; also hit when a numeric-looking user string parsed as a UID but no matching passwd entry exists, sending execution into this fallback path.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- cannot get current working directory, {err:?}
- cannot get absolute path to working directory, {err:?}
- errno
- signal
- all plugins are exited. all connections may fail, check your
AI-assisted analysis of shadowsocks/shadowsocks-rust@8eb0f0a65b (2026-09-09).
Data as JSON: /api/errors/6507c828f8e9b94e.
Report an issue: GitHub.