@@ -161,9 +161,6 @@ fn exit_info_from_resp(resp: Option<Resp>) -> Option<crate::state::ExitInfo> {
161161 }
162162}
163163
164- /// Filename of the supervisor control socket inside the sandbox state dir.
165- pub const SUPERVISOR_SOCKET : & str = "supervisor.sock" ;
166-
167164/// Commands the CLI sends to the Supervisor over the Unix socket.
168165#[ derive( Debug , serde:: Deserialize , serde:: Serialize ) ]
169166#[ serde( tag = "cmd" , rename_all = "lowercase" ) ]
@@ -204,11 +201,29 @@ pub enum SupervisorReply {
204201 Exit { code : Option < i32 > , signal : Option < i32 > } ,
205202}
206203
204+ /// Deterministic 64-bit FNV-1a hash of `id` as 16 lowercase hex chars.
205+ ///
206+ /// Keeps the supervisor socket path short enough for `sockaddr_un.sun_path`
207+ /// (108 bytes incl. NUL) even when the runtime root and container id are long
208+ /// (containerd passes a 64-char id under /run/containerd/runc/<ns>). Only needs
209+ /// to be stable within a single binary: bind and connect run the same build.
210+ fn fnv1a_hex ( id : & str ) -> String {
211+ const OFFSET : u64 = 0xcbf29ce484222325 ;
212+ const PRIME : u64 = 0x0000_0100_0000_01b3 ;
213+ let mut h = OFFSET ;
214+ for b in id. as_bytes ( ) {
215+ h ^= * b as u64 ;
216+ h = h. wrapping_mul ( PRIME ) ;
217+ }
218+ format ! ( "{:016x}" , h)
219+ }
220+
207221/// Returns the path to the supervisor socket for the given sandbox ID.
222+ ///
223+ /// Lives directly under the state dir as `<fnv16(id)>.sock` (not under the
224+ /// per-id state subdir) so the path stays well under the `sun_path` limit.
208225pub fn socket_path ( id : & str ) -> PathBuf {
209- PathBuf :: from ( crate :: state:: state_dir ( ) )
210- . join ( id)
211- . join ( SUPERVISOR_SOCKET )
226+ PathBuf :: from ( crate :: state:: state_dir ( ) ) . join ( format ! ( "{}.sock" , fnv1a_hex( id) ) )
212227}
213228
214229/// Send a command to a running supervisor and return its reply (blocking).
@@ -315,6 +330,13 @@ async fn supervisor_main(
315330 }
316331 } ;
317332
333+ // Restrict connects to the owner (root, same as the runtime). Best-effort:
334+ // the path-length fix is what matters; a chmod failure must not abort create.
335+ {
336+ use std:: os:: unix:: fs:: PermissionsExt ;
337+ let _ = std:: fs:: set_permissions ( & sock_path, std:: fs:: Permissions :: from_mode ( 0o700 ) ) ;
338+ }
339+
318340 // Set up the control channel. The child end is mapped onto CONTROL_FD inside
319341 // the confined process; the daemon keeps the other end to drive
320342 // RunMain/RunExec/Shutdown.
@@ -976,6 +998,13 @@ async fn supervisor_restore_main(
976998 }
977999 } ;
9781000
1001+ // Restrict connects to the owner (root, same as the runtime). Best-effort:
1002+ // the path-length fix is what matters; a chmod failure must not abort restore.
1003+ {
1004+ use std:: os:: unix:: fs:: PermissionsExt ;
1005+ let _ = std:: fs:: set_permissions ( & sock_path, std:: fs:: Permissions :: from_mode ( 0o700 ) ) ;
1006+ }
1007+
9791008 // Restore: forks the child under the saved policy, injects the checkpoint,
9801009 // and RESUMES it. The child is already running on return — there is no
9811010 // separate start step.
@@ -1026,10 +1055,31 @@ mod tests {
10261055 use super :: * ;
10271056
10281057 #[ test]
1029- fn socket_path_is_under_state_dir ( ) {
1058+ fn socket_path_uses_short_hashed_name_under_state_dir ( ) {
10301059 let p = socket_path ( "my-sandbox" ) ;
1031- assert ! ( p. to_str( ) . unwrap( ) . contains( "my-sandbox" ) ) ;
1032- assert ! ( p. to_str( ) . unwrap( ) . contains( "supervisor.sock" ) ) ;
1060+ let s = p. to_str ( ) . unwrap ( ) ;
1061+ assert ! ( s. starts_with( & crate :: state:: state_dir( ) ) ) ;
1062+ assert ! ( s. ends_with( ".sock" ) ) ;
1063+ // file name is 16 hex chars + ".sock" = 21 bytes, never the raw id.
1064+ assert_eq ! ( p. file_name( ) . unwrap( ) . to_str( ) . unwrap( ) . len( ) , 21 ) ;
1065+ assert ! ( !s. contains( "my-sandbox" ) ) ;
1066+ }
1067+
1068+ #[ test]
1069+ fn socket_filename_keeps_path_under_sun_len_for_cri_root ( ) {
1070+ // containerd's runc-v2 shim passes this root plus a 64-char id.
1071+ let cri_root = "/run/containerd/runc/k8s.io" ;
1072+ let id = "a" . repeat ( 64 ) ;
1073+ let full = format ! ( "{}/{}.sock" , cri_root, fnv1a_hex( & id) ) ;
1074+ assert ! ( full. len( ) < 108 , "socket path too long: {} bytes" , full. len( ) ) ;
1075+ }
1076+
1077+ #[ test]
1078+ fn fnv1a_hex_is_deterministic_and_distinct ( ) {
1079+ assert_eq ! ( fnv1a_hex( "abc" ) , fnv1a_hex( "abc" ) ) ;
1080+ assert_ne ! ( fnv1a_hex( "abc" ) , fnv1a_hex( "abd" ) ) ;
1081+ assert_eq ! ( fnv1a_hex( "abc" ) . len( ) , 16 ) ;
1082+ assert ! ( fnv1a_hex( "abc" ) . chars( ) . all( |c| c. is_ascii_hexdigit( ) ) ) ;
10331083 }
10341084
10351085 #[ test]
0 commit comments