@@ -20,8 +20,10 @@ import (
20
20
"io/ioutil"
21
21
"os"
22
22
"os/exec"
23
+ "path/filepath"
23
24
"runtime"
24
25
"runtime/debug"
26
+ "strconv"
25
27
"strings"
26
28
27
29
"github.com/google/subcommands"
@@ -419,6 +421,8 @@ func (b *Boot) Execute(_ context.Context, f *flag.FlagSet, args ...any) subcomma
419
421
420
422
if b .procMountSyncFD != - 1 {
421
423
l .PreSeccompCallback = func () {
424
+ // Call validateOpenFDs() before umounting /proc.
425
+ validateOpenFDs (bootArgs .PassFDs )
422
426
// Umount /proc right before installing seccomp filters.
423
427
umountProc (b .procMountSyncFD )
424
428
}
@@ -525,3 +529,50 @@ func umountProc(syncFD int) {
525
529
util .Fatalf ("/proc is still accessible" )
526
530
}
527
531
}
532
+
533
+ // validateOpenFDs checks that the sandbox process does not have any open
534
+ // directory FDs.
535
+ func validateOpenFDs (passFDs []boot.FDMapping ) {
536
+ passHostFDs := make (map [int ]struct {})
537
+ for _ , passFD := range passFDs {
538
+ passHostFDs [passFD .Host ] = struct {}{}
539
+ }
540
+ const selfFDDir = "/proc/self/fd"
541
+ if err := filepath .WalkDir (selfFDDir , func (path string , d os.DirEntry , err error ) error {
542
+ if err != nil {
543
+ return err
544
+ }
545
+ if d .Type () != os .ModeSymlink {
546
+ // All entries are symlinks. Ignore the callback for fd directory itself.
547
+ return nil
548
+ }
549
+ if fdInfo , err := os .Stat (path ); err != nil {
550
+ if os .IsNotExist (err ) {
551
+ // Ignore FDs that are now closed. For example, the FD to selfFDDir that
552
+ // was opened by filepath.WalkDir() to read dirents.
553
+ return nil
554
+ }
555
+ return fmt .Errorf ("os.Stat(%s) failed: %v" , path , err )
556
+ } else if ! fdInfo .IsDir () {
557
+ return nil
558
+ }
559
+ // Uh-oh. This is a directory FD.
560
+ fdNo , err := strconv .Atoi (d .Name ())
561
+ if err != nil {
562
+ return fmt .Errorf ("strconv.Atoi(%s) failed: %v" , d .Name (), err )
563
+ }
564
+ dirLink , err := os .Readlink (path )
565
+ if err != nil {
566
+ return fmt .Errorf ("os.Readlink(%s) failed: %v" , path , err )
567
+ }
568
+ if _ , ok := passHostFDs [fdNo ]; ok {
569
+ // Passed FDs are allowed to be directories. The user must be knowing
570
+ // what they are doing. Log a warning regardless.
571
+ log .Warningf ("Sandbox has access to FD %d, which is a directory for %s" , fdNo , dirLink )
572
+ return nil
573
+ }
574
+ return fmt .Errorf ("FD %d is a directory for %s" , fdNo , dirLink )
575
+ }); err != nil {
576
+ util .Fatalf ("WalkDir(%s) failed: %v" , selfFDDir , err )
577
+ }
578
+ }
0 commit comments