@@ -470,21 +470,48 @@ fn deny_open_verdict(
470470 if allowed { None } else { Some ( libc:: EACCES ) }
471471}
472472
473- /// openat/open argument layout, normalized across the two spellings.
473+ /// open/ openat/openat2 argument layout, normalized across the spellings.
474474struct OpenArgs {
475475 dirfd : i64 ,
476476 path_ptr : u64 ,
477477 flags : u64 ,
478478 mode : u64 ,
479+ /// `openat2` `resolve` flags (`RESOLVE_*`); 0 for `open`/`openat`.
480+ resolve : u64 ,
479481}
480482
481- fn decode_open_args ( notif : & SeccompNotif ) -> OpenArgs {
483+ /// Decode the open arguments. `openat2` carries flags/mode/resolve inside a
484+ /// `struct open_how` in child memory, so its decode reads child memory and
485+ /// can fail; `None` means "could not decode" and the caller soft-falls-through
486+ /// (the kernel's own re-read fails the same way).
487+ fn decode_open_args ( notif : & SeccompNotif , notif_fd : RawFd ) -> Option < OpenArgs > {
482488 let a = & notif. data . args ;
483- if notif. data . nr as i64 == libc:: SYS_openat {
484- OpenArgs { dirfd : a[ 0 ] as i64 , path_ptr : a[ 1 ] , flags : a[ 2 ] , mode : a[ 3 ] }
489+ let nr = notif. data . nr as i64 ;
490+ if nr == libc:: SYS_openat {
491+ Some ( OpenArgs { dirfd : a[ 0 ] as i64 , path_ptr : a[ 1 ] , flags : a[ 2 ] , mode : a[ 3 ] , resolve : 0 } )
492+ } else if nr == arch:: SYS_OPENAT2 {
493+ // openat2(dirfd, pathname, struct open_how *how, size_t size),
494+ // open_how = { u64 flags, u64 mode, u64 resolve }.
495+ let how_ptr = a[ 2 ] ;
496+ let want = ( a[ 3 ] as usize ) . min ( std:: mem:: size_of :: < OpenHow > ( ) ) ;
497+ if how_ptr == 0 || want < 16 {
498+ return None ; // need at least flags + mode
499+ }
500+ let bytes = read_child_mem ( notif_fd, notif. id , notif. pid , how_ptr, want) . ok ( ) ?;
501+ if bytes. len ( ) < 16 {
502+ return None ;
503+ }
504+ let flags = u64:: from_ne_bytes ( bytes[ 0 ..8 ] . try_into ( ) . ok ( ) ?) ;
505+ let mode = u64:: from_ne_bytes ( bytes[ 8 ..16 ] . try_into ( ) . ok ( ) ?) ;
506+ let resolve = if bytes. len ( ) >= 24 {
507+ u64:: from_ne_bytes ( bytes[ 16 ..24 ] . try_into ( ) . ok ( ) ?)
508+ } else {
509+ 0
510+ } ;
511+ Some ( OpenArgs { dirfd : a[ 0 ] as i64 , path_ptr : a[ 1 ] , flags, mode, resolve } )
485512 } else {
486513 // legacy open(path, flags, mode) — AT_FDCWD implied.
487- OpenArgs { dirfd : libc:: AT_FDCWD as i64 , path_ptr : a[ 0 ] , flags : a[ 1 ] , mode : a[ 2 ] }
514+ Some ( OpenArgs { dirfd : libc:: AT_FDCWD as i64 , path_ptr : a[ 0 ] , flags : a[ 1 ] , mode : a[ 2 ] , resolve : 0 } )
488515 }
489516}
490517
@@ -543,6 +570,7 @@ fn create_new_on_behalf(
543570 path : & str ,
544571 flags : u64 ,
545572 mode : u64 ,
573+ resolve : u64 ,
546574 policy : & NotifPolicy ,
547575 pfs : & super :: state:: PolicyFnState ,
548576) -> NotifAction {
@@ -565,7 +593,7 @@ fn create_new_on_behalf(
565593 & c_parent,
566594 ( libc:: O_PATH | libc:: O_DIRECTORY | libc:: O_CLOEXEC ) as u64 ,
567595 0 ,
568- RESOLVE_NO_MAGICLINKS ,
596+ RESOLVE_NO_MAGICLINKS | resolve ,
569597 ) {
570598 Ok ( f) => f,
571599 Err ( e) => return NotifAction :: Errno ( e) ,
@@ -605,7 +633,11 @@ fn on_behalf_open_for_deny(
605633 return NotifAction :: Continue ;
606634 }
607635
608- let OpenArgs { dirfd, path_ptr, flags, mode } = decode_open_args ( notif) ;
636+ let OpenArgs { dirfd, path_ptr, flags, mode, resolve } =
637+ match decode_open_args ( notif, notif_fd) {
638+ Some ( a) => a,
639+ None => return NotifAction :: Continue , // kernel's re-read fails the same way
640+ } ;
609641
610642 let path = match read_child_cstr ( notif_fd, notif. id , notif. pid , path_ptr, 4096 ) {
611643 Some ( p) => p,
@@ -621,12 +653,12 @@ fn on_behalf_open_for_deny(
621653 } ;
622654
623655 // Side-effect-free probe; mirror the child's no-follow intent for the
624- // final component.
656+ // final component and any `openat2` RESOLVE_* flags it requested .
625657 let probe_flags = ( libc:: O_PATH | libc:: O_CLOEXEC ) as u64 | ( flags & libc:: O_NOFOLLOW as u64 ) ;
626- match openat2_at ( base. as_raw_fd ( ) , & c_path, probe_flags, 0 , RESOLVE_NO_MAGICLINKS ) {
658+ match openat2_at ( base. as_raw_fd ( ) , & c_path, probe_flags, 0 , RESOLVE_NO_MAGICLINKS | resolve ) {
627659 Ok ( probe) => reopen_existing_on_behalf ( probe, flags, policy, pfs) ,
628660 Err ( errno) if errno == libc:: ENOENT && ( flags & libc:: O_CREAT as u64 ) != 0 => {
629- create_new_on_behalf ( & base, & path, flags, mode, policy, pfs)
661+ create_new_on_behalf ( & base, & path, flags, mode, resolve , policy, pfs)
630662 }
631663 Err ( errno) => NotifAction :: Errno ( errno) ,
632664 }
@@ -1255,8 +1287,9 @@ fn resolve_at_path_for_event(notif: &SeccompNotif, dirfd: i64, path: &str) -> Op
12551287fn resolve_path_for_notif ( notif : & SeccompNotif , notif_fd : RawFd ) -> Option < String > {
12561288 let nr = notif. data . nr as i64 ;
12571289 match nr {
1258- n if n == libc:: SYS_openat => {
1259- // openat(dirfd, pathname, flags, mode)
1290+ n if n == libc:: SYS_openat || n == arch:: SYS_OPENAT2 => {
1291+ // openat(dirfd, pathname, flags, mode) and
1292+ // openat2(dirfd, pathname, how, size) share (dirfd, pathname).
12601293 let path = read_path_for_event ( notif, notif. data . args [ 1 ] , notif_fd) ?;
12611294 resolve_at_path_for_event ( notif, notif. data . args [ 0 ] as i64 , & path)
12621295 }
@@ -1588,7 +1621,7 @@ async fn handle_notification(
15881621 let mut action = {
15891622 let nr = notif. data . nr as i64 ;
15901623 let mut path_check_nrs = vec ! [
1591- libc:: SYS_openat , libc:: SYS_execve , libc:: SYS_execveat ,
1624+ libc:: SYS_openat , arch :: SYS_OPENAT2 , libc:: SYS_execve , libc:: SYS_execveat ,
15921625 libc:: SYS_linkat , libc:: SYS_renameat2 , libc:: SYS_symlinkat ,
15931626 ] ;
15941627 path_check_nrs. extend ( [
@@ -1614,8 +1647,9 @@ async fn handle_notification(
16141647 // inode and inject the fd so the kernel never re-resolves.
16151648 // Other path syscalls keep the best-effort precheck above
16161649 // (documented follow-up — they return no fd to inject).
1617- let is_openat_family =
1618- nr == libc:: SYS_openat || Some ( nr) == arch:: sys_open ( ) ;
1650+ let is_openat_family = nr == libc:: SYS_openat
1651+ || nr == arch:: SYS_OPENAT2
1652+ || Some ( nr) == arch:: sys_open ( ) ;
16191653 if matches ! ( action, NotifAction :: Continue ) && is_openat_family && has_denied {
16201654 let pfs = ctx. policy_fn . lock ( ) . await ;
16211655 on_behalf_open_for_deny ( & notif, policy, & pfs, fd)
0 commit comments