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:
Andrei Vagin
2019-04-04 17:43:53 -07:00
committed by Shentubot
parent 75a5ccf5d9
commit 88409e983c
8 changed files with 39 additions and 4 deletions
+5
View File
@@ -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
+4
View File
@@ -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
+3
View File
@@ -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]".
+4 -1
View File
@@ -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 {
+2
View File
@@ -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)
}
+2 -3
View File
@@ -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 {
+1
View File
@@ -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",
+18
View File
@@ -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)));