mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
gvisor: Add support for the MS_NOEXEC mount option
https://github.com/google/gvisor/issues/145 PiperOrigin-RevId: 242044115 Change-Id: I8f140fe05e32ecd438b6be218e224e4b7fe05878
This commit is contained in:
@@ -46,6 +46,11 @@ func ContextCanAccessFile(ctx context.Context, inode *Inode, reqPerms PermMask)
|
||||
p = uattr.Perms.Group
|
||||
}
|
||||
|
||||
// Do not allow programs to be executed if MS_NOEXEC is set.
|
||||
if IsFile(inode.StableAttr) && reqPerms.Execute && inode.MountSource.Flags.NoExec {
|
||||
return false
|
||||
}
|
||||
|
||||
// Are permissions satisfied without capability checks?
|
||||
if p.SupersetOf(reqPerms) {
|
||||
return true
|
||||
|
||||
@@ -140,6 +140,10 @@ type MountSourceFlags struct {
|
||||
// cache, even when the platform supports direct mapped I/O. This
|
||||
// doesn't correspond to any Linux mount options.
|
||||
ForcePageCache bool
|
||||
|
||||
// NoExec corresponds to mount(2)'s "MS_NOEXEC" and indicates that
|
||||
// binaries from this file system can't be executed.
|
||||
NoExec bool
|
||||
}
|
||||
|
||||
// GenericMountSourceOptions splits a string containing comma separated tokens of the
|
||||
|
||||
@@ -129,6 +129,9 @@ func (mif *mountInfoFile) ReadSeqFileData(ctx context.Context, handle seqfile.Se
|
||||
if m.Flags.NoAtime {
|
||||
opts += ",noatime"
|
||||
}
|
||||
if m.Flags.NoExec {
|
||||
opts += ",noexec"
|
||||
}
|
||||
fmt.Fprintf(&buf, "%s ", opts)
|
||||
|
||||
// (7) Optional fields: zero or more fields of the form "tag[:value]".
|
||||
|
||||
@@ -75,7 +75,7 @@ func Mount(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
|
||||
|
||||
// Silently allow MS_NOSUID, since we don't implement set-id bits
|
||||
// anyway.
|
||||
const unsupportedFlags = linux.MS_NODEV | linux.MS_NOEXEC |
|
||||
const unsupportedFlags = linux.MS_NODEV |
|
||||
linux.MS_NODIRATIME | linux.MS_STRICTATIME
|
||||
|
||||
// Linux just allows passing any flags to mount(2) - it won't fail when
|
||||
@@ -100,6 +100,9 @@ func Mount(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
|
||||
if flags&linux.MS_RDONLY == linux.MS_RDONLY {
|
||||
superFlags.ReadOnly = true
|
||||
}
|
||||
if flags&linux.MS_NOEXEC == linux.MS_NOEXEC {
|
||||
superFlags.NoExec = true
|
||||
}
|
||||
|
||||
rootInode, err := rsys.Mount(t, sourcePath, superFlags, data, nil)
|
||||
if err != nil {
|
||||
|
||||
@@ -482,6 +482,8 @@ func mountFlags(opts []string) fs.MountSourceFlags {
|
||||
mf.ReadOnly = true
|
||||
case "noatime":
|
||||
mf.NoAtime = true
|
||||
case "noexec":
|
||||
mf.NoExec = true
|
||||
default:
|
||||
log.Warningf("ignoring unknown mount option %q", o)
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ var optionsMap = map[string]mapping{
|
||||
"diratime": {set: false, val: syscall.MS_NODIRATIME},
|
||||
"dirsync": {set: true, val: syscall.MS_DIRSYNC},
|
||||
"exec": {set: false, val: syscall.MS_NOEXEC},
|
||||
"noexec": {set: true, val: syscall.MS_NOEXEC},
|
||||
"iversion": {set: true, val: syscall.MS_I_VERSION},
|
||||
"loud": {set: false, val: syscall.MS_SILENT},
|
||||
"mand": {set: true, val: syscall.MS_MANDLOCK},
|
||||
@@ -76,9 +77,7 @@ var propOptionsMap = map[string]mapping{
|
||||
// invalidOptions list options not allowed.
|
||||
// - shared: sandbox must be isolated from the host. Propagating mount changes
|
||||
// from the sandbox to the host breaks the isolation.
|
||||
// - noexec: not yet supported. Don't ignore it since it could break
|
||||
// in-sandbox security.
|
||||
var invalidOptions = []string{"shared", "rshared", "noexec"}
|
||||
var invalidOptions = []string{"shared", "rshared"}
|
||||
|
||||
// OptionsToFlags converts mount options to syscall flags.
|
||||
func OptionsToFlags(opts []string) uint32 {
|
||||
|
||||
@@ -1080,6 +1080,7 @@ cc_binary(
|
||||
"//test/util:file_descriptor",
|
||||
"//test/util:fs_util",
|
||||
"//test/util:mount_util",
|
||||
"//test/util:multiprocess_util",
|
||||
"//test/util:posix_error",
|
||||
"//test/util:temp_path",
|
||||
"//test/util:test_main",
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include "test/util/file_descriptor.h"
|
||||
#include "test/util/fs_util.h"
|
||||
#include "test/util/mount_util.h"
|
||||
#include "test/util/multiprocess_util.h"
|
||||
#include "test/util/posix_error.h"
|
||||
#include "test/util/temp_path.h"
|
||||
#include "test/util/test_util.h"
|
||||
@@ -277,6 +278,23 @@ TEST(MountTest, MountNoAtime) {
|
||||
EXPECT_EQ(before, after);
|
||||
}
|
||||
|
||||
TEST(MountTest, MountNoExec) {
|
||||
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN)));
|
||||
|
||||
auto const dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
|
||||
auto const mount = ASSERT_NO_ERRNO_AND_VALUE(
|
||||
Mount("", dir.path(), "tmpfs", MS_NOEXEC, "mode=0777", 0));
|
||||
|
||||
std::string const contents = "No no no, don't follow the instructions!";
|
||||
auto const file = ASSERT_NO_ERRNO_AND_VALUE(
|
||||
TempPath::CreateFileWith(dir.path(), contents, 0777));
|
||||
|
||||
int execve_errno;
|
||||
ASSERT_NO_ERRNO_AND_VALUE(
|
||||
ForkAndExec(file.path(), {}, {}, nullptr, &execve_errno));
|
||||
EXPECT_EQ(execve_errno, EACCES);
|
||||
}
|
||||
|
||||
TEST(MountTest, RenameRemoveMountPoint) {
|
||||
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN)));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user