1818//! namespace. So with the checking here, they may fail, but they won't be able
1919//! to succeed with bogus results.
2020
21- use crate :: backend:: pid:: syscalls:: getpid;
2221use crate :: fd:: { AsFd , BorrowedFd , OwnedFd } ;
22+ use crate :: ffi:: CStr ;
2323use crate :: fs:: {
24- fstat, fstatfs, major, openat, renameat, FileType , FsWord , Mode , OFlags , Stat , CWD ,
24+ fstat, fstatfs, major, openat, renameat, FileType , FsWord , Mode , OFlags , RawDir , Stat , CWD ,
2525 PROC_SUPER_MAGIC ,
2626} ;
2727use crate :: io;
2828use crate :: path:: DecInt ;
2929#[ cfg( feature = "rustc-dep-of-std" ) ]
3030use core:: lazy:: OnceCell ;
31+ use core:: mem:: MaybeUninit ;
3132#[ cfg( not( feature = "rustc-dep-of-std" ) ) ]
3233use once_cell:: sync:: OnceCell ;
33- #[ cfg( feature = "alloc" ) ]
34- use { crate :: ffi:: CStr , crate :: fs:: Dir } ;
3534
3635/// Linux's procfs always uses inode 1 for its root directory.
3736const PROC_ROOT_INO : u64 = 1 ;
@@ -42,8 +41,8 @@ enum Kind {
4241 Proc ,
4342 Pid ,
4443 Fd ,
45- #[ cfg( feature = "alloc" ) ]
4644 File ,
45+ Symlink ,
4746}
4847
4948/// Check a subdirectory of "/proc" for anomalies.
@@ -69,16 +68,23 @@ fn check_proc_entry_with_stat(
6968 match kind {
7069 Kind :: Proc => check_proc_root ( entry, & entry_stat) ?,
7170 Kind :: Pid | Kind :: Fd => check_proc_subdir ( entry, & entry_stat, proc_stat) ?,
72- #[ cfg( feature = "alloc" ) ]
7371 Kind :: File => check_proc_file ( & entry_stat, proc_stat) ?,
72+ Kind :: Symlink => check_proc_symlink ( & entry_stat, proc_stat) ?,
7473 }
7574
7675 // "/proc" directories are typically mounted r-xr-xr-x.
7776 // "/proc/self/fd" is r-x------. Allow them to have fewer permissions, but
7877 // not more.
79- let expected_mode = if let Kind :: Fd = kind { 0o500 } else { 0o555 } ;
80- if entry_stat. st_mode & 0o777 & !expected_mode != 0 {
81- return Err ( io:: Errno :: NOTSUP ) ;
78+ match kind {
79+ Kind :: Symlink => {
80+ // On Linux, symlinks don't have their own permissions.
81+ }
82+ _ => {
83+ let expected_mode = if let Kind :: Fd = kind { 0o500 } else { 0o555 } ;
84+ if entry_stat. st_mode & 0o777 & !expected_mode != 0 {
85+ return Err ( io:: Errno :: NOTSUP ) ;
86+ }
87+ }
8288 }
8389
8490 match kind {
@@ -97,14 +103,20 @@ fn check_proc_entry_with_stat(
97103 return Err ( io:: Errno :: NOTSUP ) ;
98104 }
99105 }
100- #[ cfg( feature = "alloc" ) ]
101106 Kind :: File => {
102107 // Check that files in procfs don't have extraneous hard links to
103108 // them (which might indicate hard links to other things).
104109 if entry_stat. st_nlink != 1 {
105110 return Err ( io:: Errno :: NOTSUP ) ;
106111 }
107112 }
113+ Kind :: Symlink => {
114+ // Check that symlinks in procfs don't have extraneous hard links
115+ // to them (which might indicate hard links to other things).
116+ if entry_stat. st_nlink != 1 {
117+ return Err ( io:: Errno :: NOTSUP ) ;
118+ }
119+ }
108120 }
109121
110122 Ok ( entry_stat)
@@ -153,7 +165,6 @@ fn check_proc_subdir(
153165 Ok ( ( ) )
154166}
155167
156- #[ cfg( feature = "alloc" ) ]
157168fn check_proc_file ( stat : & Stat , proc_stat : Option < & Stat > ) -> io:: Result < ( ) > {
158169 // Check that we have a regular file.
159170 if FileType :: from_raw_mode ( stat. st_mode ) != FileType :: RegularFile {
@@ -165,6 +176,17 @@ fn check_proc_file(stat: &Stat, proc_stat: Option<&Stat>) -> io::Result<()> {
165176 Ok ( ( ) )
166177}
167178
179+ fn check_proc_symlink ( stat : & Stat , proc_stat : Option < & Stat > ) -> io:: Result < ( ) > {
180+ // Check that we have a symbolic link.
181+ if FileType :: from_raw_mode ( stat. st_mode ) != FileType :: Symlink {
182+ return Err ( io:: Errno :: NOTSUP ) ;
183+ }
184+
185+ check_proc_nonroot ( stat, proc_stat) ?;
186+
187+ Ok ( ( ) )
188+ }
189+
168190fn check_proc_nonroot ( stat : & Stat , proc_stat : Option < & Stat > ) -> io:: Result < ( ) > {
169191 // Check that we haven't been linked back to the root of "/proc".
170192 if stat. st_ino == PROC_ROOT_INO {
@@ -245,6 +267,7 @@ fn proc() -> io::Result<(BorrowedFd<'static>, &'static Stat)> {
245267/// - [Linux]
246268///
247269/// [Linux]: https://man7.org/linux/man-pages/man5/proc.5.html
270+ #[ allow( unsafe_code) ]
248271fn proc_self ( ) -> io:: Result < ( BorrowedFd < ' static > , & ' static Stat ) > {
249272 static PROC_SELF : StaticFd = StaticFd :: new ( ) ;
250273
@@ -253,11 +276,21 @@ fn proc_self() -> io::Result<(BorrowedFd<'static>, &'static Stat)> {
253276 . get_or_try_init ( || {
254277 let ( proc, proc_stat) = proc ( ) ?;
255278
256- let pid = getpid ( ) ;
279+ // `getpid` would return our pid in our own pid namespace, so
280+ // instead use `readlink` on the `self` symlink to learn our pid in
281+ // the procfs namespace.
282+ let self_symlink = open_and_check_file ( proc, proc_stat, cstr ! ( "self" ) , Kind :: Symlink ) ?;
283+ let mut buf = [ MaybeUninit :: < u8 > :: uninit ( ) ; 20 ] ;
284+ let len = crate :: backend:: fs:: syscalls:: readlinkat (
285+ self_symlink. as_fd ( ) ,
286+ cstr ! ( "" ) ,
287+ & mut buf,
288+ ) ?;
289+ let pid: & [ u8 ] = unsafe { core:: mem:: transmute ( & buf[ ..len] ) } ;
257290
258291 // Open "/proc/self". Use our pid to compute the name rather than
259292 // literally using "self", as "self" is a symlink.
260- let proc_self = proc_opendirat ( proc, DecInt :: new ( pid. as_raw_nonzero ( ) . get ( ) ) ) ?;
293+ let proc_self = proc_opendirat ( proc, pid) ?;
261294 let proc_self_stat = check_proc_entry ( Kind :: Pid , proc_self. as_fd ( ) , Some ( proc_stat) )
262295 . map_err ( |_err| io:: Errno :: NOTSUP ) ?;
263296
@@ -314,7 +347,6 @@ fn new_static_fd(fd: OwnedFd, stat: Stat) -> (OwnedFd, Stat) {
314347/// - [Linux]
315348///
316349/// [Linux]: https://man7.org/linux/man-pages/man5/proc.5.html
317- #[ cfg( feature = "alloc" ) ]
318350fn proc_self_fdinfo ( ) -> io:: Result < ( BorrowedFd < ' static > , & ' static Stat ) > {
319351 static PROC_SELF_FDINFO : StaticFd = StaticFd :: new ( ) ;
320352
@@ -344,18 +376,21 @@ fn proc_self_fdinfo() -> io::Result<(BorrowedFd<'static>, &'static Stat)> {
344376/// - [Linux]
345377///
346378/// [Linux]: https://man7.org/linux/man-pages/man5/proc.5.html
347- #[ cfg( feature = "alloc" ) ]
348379#[ inline]
349380#[ cfg_attr( doc_cfg, doc( cfg( feature = "procfs" ) ) ) ]
350381pub fn proc_self_fdinfo_fd < Fd : AsFd > ( fd : Fd ) -> io:: Result < OwnedFd > {
351382 _proc_self_fdinfo ( fd. as_fd ( ) )
352383}
353384
354- #[ cfg( feature = "alloc" ) ]
355385fn _proc_self_fdinfo ( fd : BorrowedFd < ' _ > ) -> io:: Result < OwnedFd > {
356386 let ( proc_self_fdinfo, proc_self_fdinfo_stat) = proc_self_fdinfo ( ) ?;
357387 let fd_str = DecInt :: from_fd ( fd) ;
358- open_and_check_file ( proc_self_fdinfo, proc_self_fdinfo_stat, fd_str. as_c_str ( ) )
388+ open_and_check_file (
389+ proc_self_fdinfo,
390+ proc_self_fdinfo_stat,
391+ fd_str. as_c_str ( ) ,
392+ Kind :: File ,
393+ )
359394}
360395
361396/// Returns a handle to a Linux `/proc/self/pagemap` file.
@@ -369,7 +404,6 @@ fn _proc_self_fdinfo(fd: BorrowedFd<'_>) -> io::Result<OwnedFd> {
369404///
370405/// [Linux]: https://man7.org/linux/man-pages/man5/proc.5.html
371406/// [Linux pagemap]: https://www.kernel.org/doc/Documentation/vm/pagemap.txt
372- #[ cfg( feature = "alloc" ) ]
373407#[ inline]
374408#[ cfg_attr( doc_cfg, doc( cfg( feature = "procfs" ) ) ) ]
375409pub fn proc_self_pagemap ( ) -> io:: Result < OwnedFd > {
@@ -385,7 +419,6 @@ pub fn proc_self_pagemap() -> io::Result<OwnedFd> {
385419/// - [Linux]
386420///
387421/// [Linux]: https://man7.org/linux/man-pages/man5/proc.5.html
388- #[ cfg( feature = "alloc" ) ]
389422#[ inline]
390423#[ cfg_attr( doc_cfg, doc( cfg( feature = "procfs" ) ) ) ]
391424pub fn proc_self_maps ( ) -> io:: Result < OwnedFd > {
@@ -401,23 +434,25 @@ pub fn proc_self_maps() -> io::Result<OwnedFd> {
401434/// - [Linux]
402435///
403436/// [Linux]: https://man7.org/linux/man-pages/man5/proc.5.html
404- #[ cfg( feature = "alloc" ) ]
405437#[ inline]
406438#[ cfg_attr( doc_cfg, doc( cfg( feature = "procfs" ) ) ) ]
407439pub fn proc_self_status ( ) -> io:: Result < OwnedFd > {
408440 proc_self_file ( cstr ! ( "status" ) )
409441}
410442
411443/// Open a file under `/proc/self`.
412- #[ cfg( feature = "alloc" ) ]
413444fn proc_self_file ( name : & CStr ) -> io:: Result < OwnedFd > {
414445 let ( proc_self, proc_self_stat) = proc_self ( ) ?;
415- open_and_check_file ( proc_self, proc_self_stat, name)
446+ open_and_check_file ( proc_self, proc_self_stat, name, Kind :: File )
416447}
417448
418449/// Open a procfs file within in `dir` and check it for bind mounts.
419- #[ cfg( feature = "alloc" ) ]
420- fn open_and_check_file ( dir : BorrowedFd < ' _ > , dir_stat : & Stat , name : & CStr ) -> io:: Result < OwnedFd > {
450+ fn open_and_check_file (
451+ dir : BorrowedFd < ' _ > ,
452+ dir_stat : & Stat ,
453+ name : & CStr ,
454+ kind : Kind ,
455+ ) -> io:: Result < OwnedFd > {
421456 let ( _, proc_stat) = proc ( ) ?;
422457
423458 // Don't use `NOATIME`, because it [requires us to own the file], and when
@@ -426,7 +461,11 @@ fn open_and_check_file(dir: BorrowedFd<'_>, dir_stat: &Stat, name: &CStr) -> io:
426461 //
427462 // [requires us to own the file]: https://man7.org/linux/man-pages/man2/openat.2.html
428463 // [to root:root]: https://man7.org/linux/man-pages/man5/proc.5.html
429- let oflags = OFlags :: RDONLY | OFlags :: CLOEXEC | OFlags :: NOFOLLOW | OFlags :: NOCTTY ;
464+ let mut oflags = OFlags :: RDONLY | OFlags :: CLOEXEC | OFlags :: NOFOLLOW | OFlags :: NOCTTY ;
465+ if let Kind :: Symlink = kind {
466+ // Open symlinks with `O_PATH`.
467+ oflags |= OFlags :: PATH ;
468+ }
430469 let file = openat ( dir, name, oflags, Mode :: empty ( ) ) . map_err ( |_err| io:: Errno :: NOTSUP ) ?;
431470 let file_stat = fstat ( & file) ?;
432471
@@ -436,32 +475,29 @@ fn open_and_check_file(dir: BorrowedFd<'_>, dir_stat: &Stat, name: &CStr) -> io:
436475 // we just opened. If we can't find it, there could be a file bind mount on
437476 // top of the file we want.
438477 //
439- // As we scan, we also check for ".", to make sure it's the same directory
440- // as our original directory, to detect mount points, since
441- // `Dir::read_from` reopens ".".
442- //
443478 // TODO: With Linux 5.8 we might be able to use `statx` and
444479 // `STATX_ATTR_MOUNT_ROOT` to detect mountpoints directly instead of doing
445480 // this scanning.
446- let dir = Dir :: read_from ( dir) . map_err ( |_err| io:: Errno :: NOTSUP ) ?;
447481
448- // Confirm that we got the same inode.
449- let dot_stat = dir . stat ( ) . map_err ( |_err| io :: Errno :: NOTSUP ) ? ;
450- if ( dot_stat . st_dev , dot_stat . st_ino ) != ( dir_stat . st_dev , dir_stat . st_ino ) {
451- return Err ( io :: Errno :: NOTSUP ) ;
452- }
482+ let expected_type = match kind {
483+ Kind :: File => FileType :: RegularFile ,
484+ Kind :: Symlink => FileType :: Symlink ,
485+ _ => unreachable ! ( ) ,
486+ } ;
453487
454488 let mut found_file = false ;
455489 let mut found_dot = false ;
456- for entry in dir {
490+
491+ let mut buf = [ MaybeUninit :: uninit ( ) ; 2048 ] ;
492+ let mut iter = RawDir :: new ( dir, & mut buf) ;
493+ while let Some ( entry) = iter. next ( ) {
457494 let entry = entry. map_err ( |_err| io:: Errno :: NOTSUP ) ?;
458495 if entry. ino ( ) == file_stat. st_ino
459- && entry. file_type ( ) == FileType :: RegularFile
496+ && entry. file_type ( ) == expected_type
460497 && entry. file_name ( ) == name
461498 {
462499 // We found the file. Proceed to check the file handle.
463- let _ =
464- check_proc_entry_with_stat ( Kind :: File , file. as_fd ( ) , file_stat, Some ( proc_stat) ) ?;
500+ let _ = check_proc_entry_with_stat ( kind, file. as_fd ( ) , file_stat, Some ( proc_stat) ) ?;
465501
466502 found_file = true ;
467503 } else if entry. ino ( ) == dir_stat. st_ino
0 commit comments