Skip to content

Commit 2427424

Browse files
nixprimegvisor-bot
authored andcommitted
embeddedbinary: replace Options.TempDir with automatic memfd fallback
PiperOrigin-RevId: 821738453
1 parent df97ff9 commit 2427424

File tree

1 file changed

+63
-38
lines changed

1 file changed

+63
-38
lines changed

tools/embeddedbinary/embeddedbinary_template.go

Lines changed: 63 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,6 @@ var compressedBinary []byte
3838

3939
// Options is the set of options to execute the embedded binary.
4040
type 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

Comments
 (0)