@@ -38,11 +38,6 @@ var compressedBinary []byte
3838
3939// Options is the set of options to execute the embedded binary.
4040type Options struct {
41- // If TempDir is non-empty, the embedded binary will be extracted to a
42- // subdirectory of TempDir. Otherwise, the embedded binary will be
43- // extracted to a subdirectory of os.TempDir().
44- TempDir string
45-
4641 // Argv is the set of arguments to exec with.
4742 // `Argv[0]` is the name of the binary as invoked.
4843 // If Argv is empty, it will default to a single-element slice, with
@@ -80,40 +75,70 @@ func run(options *Options, fork bool) (int, error) {
8075 defer runtime .UnlockOSThread ()
8176 oldMask := unix .Umask (0077 )
8277 defer unix .Umask (oldMask )
83- tmpDir , err := os .MkdirTemp (options .TempDir , "gvisor.*.tmp" )
84- if err != nil {
85- return 0 , fmt .Errorf ("cannot create temp directory: %w" , err )
86- }
87- tmpDirHandle , err := os .Open (tmpDir )
88- if err != nil {
89- return 0 , fmt .Errorf ("cannot open temp directory: %w" , err )
90- }
91- defer tmpDirHandle .Close ()
92- binPath := path .Join (tmpDir , BinaryName )
93- tmpFile , err := os .OpenFile (binPath , os .O_RDWR | os .O_CREATE | os .O_EXCL , 0700 )
94- if err != nil {
95- return 0 , fmt .Errorf ("cannot open temp file: %w" , err )
96- }
97- if err := os .RemoveAll (tmpDir ); err != nil {
98- return 0 , fmt .Errorf ("cannot remove temp directory: %w" , err )
99- }
100- unix .Umask (oldMask )
101- if _ , err := io .Copy (tmpFile , binaryReader ); err != nil {
102- tmpFile .Close ()
103- return 0 , fmt .Errorf ("cannot decompress embedded binary or write it to temporary file: %w" , err )
104- }
105- // Reopen the file for reading.
106- tmpFileReadOnly , err := os .OpenFile (fmt .Sprintf ("/proc/self/fd/%d" , tmpFile .Fd ()), os .O_RDONLY , 0700 )
107- if err != nil {
108- tmpFile .Close ()
109- return 0 , fmt .Errorf ("cannot re-open temp file for reading: %w" , err )
110- }
111- if err := tmpFile .Close (); err != nil {
112- return 0 , fmt .Errorf ("cannot close temp file: %w" , err )
78+
79+ tmpFD := - 1
80+ // /tmp is sometimes mounted noexec for "security" reasons. Handle this by
81+ // falling back to executing from a memfd.
82+ parentDir := os .TempDir ()
83+ var parentDirStatfs unix.Statfs_t
84+ if err := unix .Statfs (parentDir , & parentDirStatfs ); err == nil && parentDirStatfs .Flags & unix .ST_NOEXEC == 0 {
85+ tmpDir , err := os .MkdirTemp (parentDir , "gvisor.*.tmp" )
86+ if err != nil {
87+ return 0 , fmt .Errorf ("cannot create temp directory: %w" , err )
88+ }
89+ tmpDirHandle , err := os .Open (tmpDir )
90+ if err != nil {
91+ return 0 , fmt .Errorf ("cannot open temp directory: %w" , err )
92+ }
93+ defer tmpDirHandle .Close ()
94+ binPath := path .Join (tmpDir , BinaryName )
95+ tmpFile , err := os .OpenFile (binPath , os .O_RDWR | os .O_CREATE | os .O_EXCL , 0700 )
96+ if err != nil {
97+ return 0 , fmt .Errorf ("cannot open temp file: %w" , err )
98+ }
99+ if err := os .RemoveAll (tmpDir ); err != nil {
100+ return 0 , fmt .Errorf ("cannot remove temp directory: %w" , err )
101+ }
102+ unix .Umask (oldMask )
103+ if _ , err := io .Copy (tmpFile , binaryReader ); err != nil {
104+ tmpFile .Close ()
105+ return 0 , fmt .Errorf ("cannot decompress embedded binary or write it to temporary file: %w" , err )
106+ }
107+ // Reopen the file for reading.
108+ tmpFileReadOnly , err := os .OpenFile (fmt .Sprintf ("/proc/self/fd/%d" , tmpFile .Fd ()), os .O_RDONLY , 0700 )
109+ if err != nil {
110+ tmpFile .Close ()
111+ return 0 , fmt .Errorf ("cannot re-open temp file for reading: %w" , err )
112+ }
113+ if err := tmpFile .Close (); err != nil {
114+ return 0 , fmt .Errorf ("cannot close temp file: %w" , err )
115+ }
116+ defer tmpFileReadOnly .Close ()
117+ tmpFD = int (tmpFileReadOnly .Fd ())
118+ } else {
119+ var err error
120+ tmpFD , err = unix .MemfdCreate (BinaryName , unix .MFD_ALLOW_SEALING | unix .MFD_EXEC )
121+ if err == unix .EINVAL {
122+ // Assume that the kernel precedes 105ff5339f498 ("mm/memfd: add
123+ // MFD_NOEXEC_SEAL and MFD_EXEC"), Linux 6.3+.
124+ tmpFD , err = unix .MemfdCreate (BinaryName , unix .MFD_ALLOW_SEALING )
125+ }
126+ if err != nil {
127+ return 0 , fmt .Errorf ("cannot create memfd: %w" , err )
128+ }
129+ tmpFile := os .NewFile (uintptr (tmpFD ), BinaryName )
130+ defer tmpFile .Close ()
131+ unix .Umask (oldMask )
132+ if _ , err := io .Copy (tmpFile , binaryReader ); err != nil {
133+ return 0 , fmt .Errorf ("cannot decompress embedded binary or write it to temporary memfd: %w" , err )
134+ }
135+ // Prevent future writes to the memfd.
136+ if _ , err := unix .FcntlInt (uintptr (tmpFD ), unix .F_ADD_SEALS , unix .F_SEAL_SEAL | unix .F_SEAL_SHRINK | unix .F_SEAL_GROW | unix .F_SEAL_WRITE ); err != nil {
137+ return 0 , fmt .Errorf ("cannot seal memfd: %w" , err )
138+ }
113139 }
114- defer tmpFileReadOnly .Close ()
115- tmpFD := tmpFileReadOnly .Fd ()
116- if _ , err := unix .Seek (int (tmpFD ), 0 , unix .SEEK_SET ); err != nil {
140+
141+ if _ , err := unix .Seek (tmpFD , 0 , unix .SEEK_SET ); err != nil {
117142 return 0 , fmt .Errorf ("cannot seek temp file back to 0: %w" , err )
118143 }
119144 fdPath := fmt .Sprintf ("/proc/self/fd/%d" , tmpFD )
0 commit comments