Implement checks for get/setxattr at the syscall layer.

Add checks for input arguments, file type, permissions, etc. that match
the Linux implementation. A call to get/setxattr that passes all the
checks will still currently return EOPNOTSUPP. Actual support will be
added in following commits.

Only allow user.* extended attributes for the time being.

PiperOrigin-RevId: 285835159
This commit is contained in:
Dean Deng
2019-12-16 13:20:07 -08:00
committed by gVisor bot
parent 6b42453039
commit e6f4124afd
9 changed files with 253 additions and 45 deletions
+1
View File
@@ -57,6 +57,7 @@ go_library(
"uio.go",
"utsname.go",
"wait.go",
"xattr.go",
],
importpath = "gvisor.dev/gvisor/pkg/abi/linux",
visibility = ["//visibility:public"],
+27
View File
@@ -0,0 +1,27 @@
// Copyright 2019 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package linux
// Constants for extended attributes.
const (
XATTR_NAME_MAX = 255
XATTR_SIZE_MAX = 65536
XATTR_CREATE = 1
XATTR_REPLACE = 2
XATTR_USER_PREFIX = "user."
XATTR_USER_PREFIX_LEN = len(XATTR_USER_PREFIX)
)
+8
View File
@@ -270,6 +270,14 @@ func (i *Inode) Getxattr(name string) (string, error) {
return i.InodeOperations.Getxattr(i, name)
}
// Setxattr calls i.InodeOperations.Setxattr with i as the Inode.
func (i *Inode) Setxattr(name, value string) error {
if i.overlay != nil {
return overlaySetxattr(i.overlay, name, value)
}
return i.InodeOperations.Setxattr(i, name, value)
}
// Listxattr calls i.InodeOperations.Listxattr with i as the Inode.
func (i *Inode) Listxattr() (map[string]struct{}, error) {
if i.overlay != nil {
+5
View File
@@ -552,6 +552,11 @@ func overlayGetxattr(o *overlayEntry, name string) (string, error) {
return s, err
}
// TODO(b/146028302): Support setxattr for overlayfs.
func overlaySetxattr(o *overlayEntry, name, value string) error {
return syserror.EOPNOTSUPP
}
func overlayListxattr(o *overlayEntry) (map[string]struct{}, error) {
o.copyMu.RLock()
defer o.copyMu.RUnlock()
+1
View File
@@ -49,6 +49,7 @@ go_library(
"sys_tls.go",
"sys_utsname.go",
"sys_write.go",
"sys_xattr.go",
"timespec.go",
],
importpath = "gvisor.dev/gvisor/pkg/sentry/syscalls/linux",
+2 -2
View File
@@ -228,10 +228,10 @@ var AMD64 = &kernel.SyscallTable{
185: syscalls.Error("security", syserror.ENOSYS, "Not implemented in Linux.", nil),
186: syscalls.Supported("gettid", Gettid),
187: syscalls.Supported("readahead", Readahead),
188: syscalls.Error("setxattr", syserror.ENOTSUP, "Requires filesystem support.", nil),
188: syscalls.PartiallySupported("setxattr", Setxattr, "Only supported for tmpfs.", nil),
189: syscalls.Error("lsetxattr", syserror.ENOTSUP, "Requires filesystem support.", nil),
190: syscalls.Error("fsetxattr", syserror.ENOTSUP, "Requires filesystem support.", nil),
191: syscalls.ErrorWithEvent("getxattr", syserror.ENOTSUP, "Requires filesystem support.", nil),
191: syscalls.PartiallySupported("getxattr", Getxattr, "Only supported for tmpfs.", nil),
192: syscalls.ErrorWithEvent("lgetxattr", syserror.ENOTSUP, "Requires filesystem support.", nil),
193: syscalls.ErrorWithEvent("fgetxattr", syserror.ENOTSUP, "Requires filesystem support.", nil),
194: syscalls.ErrorWithEvent("listxattr", syserror.ENOTSUP, "Requires filesystem support.", nil),
+2 -2
View File
@@ -41,10 +41,10 @@ var ARM64 = &kernel.SyscallTable{
2: syscalls.PartiallySupported("io_submit", IoSubmit, "Generally supported with exceptions. User ring optimizations are not implemented.", []string{"gvisor.dev/issue/204"}),
3: syscalls.PartiallySupported("io_cancel", IoCancel, "Generally supported with exceptions. User ring optimizations are not implemented.", []string{"gvisor.dev/issue/204"}),
4: syscalls.PartiallySupported("io_getevents", IoGetevents, "Generally supported with exceptions. User ring optimizations are not implemented.", []string{"gvisor.dev/issue/204"}),
5: syscalls.Error("setxattr", syserror.ENOTSUP, "Requires filesystem support.", nil),
5: syscalls.PartiallySupported("setxattr", Setxattr, "Only supported for tmpfs.", nil),
6: syscalls.Error("lsetxattr", syserror.ENOTSUP, "Requires filesystem support.", nil),
7: syscalls.Error("fsetxattr", syserror.ENOTSUP, "Requires filesystem support.", nil),
8: syscalls.ErrorWithEvent("getxattr", syserror.ENOTSUP, "Requires filesystem support.", nil),
8: syscalls.PartiallySupported("getxattr", Getxattr, "Only supported for tmpfs.", nil),
9: syscalls.ErrorWithEvent("lgetxattr", syserror.ENOTSUP, "Requires filesystem support.", nil),
10: syscalls.ErrorWithEvent("fgetxattr", syserror.ENOTSUP, "Requires filesystem support.", nil),
11: syscalls.ErrorWithEvent("listxattr", syserror.ENOTSUP, "Requires filesystem support.", nil),
+169
View File
@@ -0,0 +1,169 @@
// Copyright 2019 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package linux
import (
"strings"
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/fs"
"gvisor.dev/gvisor/pkg/sentry/kernel"
"gvisor.dev/gvisor/pkg/sentry/usermem"
"gvisor.dev/gvisor/pkg/syserror"
)
// Getxattr implements linux syscall getxattr(2).
func Getxattr(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
pathAddr := args[0].Pointer()
nameAddr := args[1].Pointer()
valueAddr := args[2].Pointer()
size := args[3].SizeT()
path, dirPath, err := copyInPath(t, pathAddr, false /* allowEmpty */)
if err != nil {
return 0, nil, err
}
valueLen := 0
err = fileOpOn(t, linux.AT_FDCWD, path, true /* resolve */, func(root *fs.Dirent, d *fs.Dirent, _ uint) error {
value, err := getxattr(t, d, dirPath, nameAddr)
if err != nil {
return err
}
valueLen = len(value)
if size == 0 {
return nil
}
if size > linux.XATTR_SIZE_MAX {
size = linux.XATTR_SIZE_MAX
}
if valueLen > int(size) {
return syserror.ERANGE
}
_, err = t.CopyOutBytes(valueAddr, []byte(value))
return err
})
if err != nil {
return 0, nil, err
}
return uintptr(valueLen), nil, nil
}
// getxattr implements getxattr from the given *fs.Dirent.
func getxattr(t *kernel.Task, d *fs.Dirent, dirPath bool, nameAddr usermem.Addr) (string, error) {
if dirPath && !fs.IsDir(d.Inode.StableAttr) {
return "", syserror.ENOTDIR
}
if err := checkXattrPermissions(t, d.Inode, fs.PermMask{Read: true}); err != nil {
return "", err
}
name, err := copyInXattrName(t, nameAddr)
if err != nil {
return "", err
}
if !strings.HasPrefix(name, linux.XATTR_USER_PREFIX) {
return "", syserror.EOPNOTSUPP
}
return d.Inode.Getxattr(name)
}
// Setxattr implements linux syscall setxattr(2).
func Setxattr(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
pathAddr := args[0].Pointer()
nameAddr := args[1].Pointer()
valueAddr := args[2].Pointer()
size := args[3].SizeT()
flags := args[4].Uint()
path, dirPath, err := copyInPath(t, pathAddr, false /* allowEmpty */)
if err != nil {
return 0, nil, err
}
if flags&^(linux.XATTR_CREATE|linux.XATTR_REPLACE) != 0 {
return 0, nil, syserror.EINVAL
}
return 0, nil, fileOpOn(t, linux.AT_FDCWD, path, true /* resolve */, func(root *fs.Dirent, d *fs.Dirent, _ uint) error {
return setxattr(t, d, dirPath, nameAddr, valueAddr, size, flags)
})
}
// setxattr implements setxattr from the given *fs.Dirent.
func setxattr(t *kernel.Task, d *fs.Dirent, dirPath bool, nameAddr, valueAddr usermem.Addr, size uint, flags uint32) error {
if dirPath && !fs.IsDir(d.Inode.StableAttr) {
return syserror.ENOTDIR
}
if err := checkXattrPermissions(t, d.Inode, fs.PermMask{Write: true}); err != nil {
return err
}
name, err := copyInXattrName(t, nameAddr)
if err != nil {
return err
}
if size > linux.XATTR_SIZE_MAX {
return syserror.E2BIG
}
buf := make([]byte, size)
if _, err = t.CopyInBytes(valueAddr, buf); err != nil {
return err
}
value := string(buf)
if !strings.HasPrefix(name, linux.XATTR_USER_PREFIX) {
return syserror.EOPNOTSUPP
}
return d.Inode.Setxattr(name, value)
}
func copyInXattrName(t *kernel.Task, nameAddr usermem.Addr) (string, error) {
name, err := t.CopyInString(nameAddr, linux.XATTR_NAME_MAX+1)
if err != nil {
if err == syserror.ENAMETOOLONG {
return "", syserror.ERANGE
}
return "", err
}
if len(name) == 0 {
return "", syserror.ERANGE
}
return name, nil
}
func checkXattrPermissions(t *kernel.Task, i *fs.Inode, perms fs.PermMask) error {
// Restrict xattrs to regular files and directories.
//
// In Linux, this restriction technically only applies to xattrs in the
// "user.*" namespace, but we don't allow any other xattr prefixes anyway.
if !fs.IsRegular(i.StableAttr) && !fs.IsDir(i.StableAttr) {
if perms.Write {
return syserror.EPERM
}
return syserror.ENODATA
}
return i.CheckPermission(t, perms)
}
+38 -41
View File
@@ -28,6 +28,7 @@
#include "test/util/capability_util.h"
#include "test/util/posix_error.h"
#include "test/util/temp_path.h"
#include "test/util/test_util.h"
namespace gvisor {
namespace testing {
@@ -37,9 +38,6 @@ namespace {
class XattrTest : public FileTest {};
TEST_F(XattrTest, XattrNullName) {
// TODO(b/127675828): Support setxattr and getxattr.
SKIP_IF(IsRunningOnGvisor());
const char* path = test_file_name_.c_str();
EXPECT_THAT(setxattr(path, nullptr, nullptr, 0, /*flags=*/0),
@@ -49,9 +47,6 @@ TEST_F(XattrTest, XattrNullName) {
}
TEST_F(XattrTest, XattrEmptyName) {
// TODO(b/127675828): Support setxattr and getxattr.
SKIP_IF(IsRunningOnGvisor());
const char* path = test_file_name_.c_str();
EXPECT_THAT(setxattr(path, "", nullptr, 0, /*flags=*/0),
@@ -60,16 +55,17 @@ TEST_F(XattrTest, XattrEmptyName) {
}
TEST_F(XattrTest, XattrLargeName) {
// TODO(b/127675828): Support setxattr and getxattr.
SKIP_IF(IsRunningOnGvisor());
const char* path = test_file_name_.c_str();
std::string name = "user.";
name += std::string(XATTR_NAME_MAX - name.length(), 'a');
EXPECT_THAT(setxattr(path, name.c_str(), nullptr, 0, /*flags=*/0),
SyscallSucceeds());
EXPECT_THAT(getxattr(path, name.c_str(), nullptr, 0),
SyscallSucceedsWithValue(0));
// TODO(b/127675828): Support setxattr and getxattr.
if (!IsRunningOnGvisor()) {
EXPECT_THAT(setxattr(path, name.c_str(), nullptr, 0, /*flags=*/0),
SyscallSucceeds());
EXPECT_THAT(getxattr(path, name.c_str(), nullptr, 0),
SyscallSucceedsWithValue(0));
}
name += "a";
EXPECT_THAT(setxattr(path, name.c_str(), nullptr, 0, /*flags=*/0),
@@ -79,9 +75,6 @@ TEST_F(XattrTest, XattrLargeName) {
}
TEST_F(XattrTest, XattrInvalidPrefix) {
// TODO(b/127675828): Support setxattr and getxattr.
SKIP_IF(IsRunningOnGvisor());
const char* path = test_file_name_.c_str();
std::string name(XATTR_NAME_MAX, 'a');
EXPECT_THAT(setxattr(path, name.c_str(), nullptr, 0, /*flags=*/0),
@@ -91,9 +84,6 @@ TEST_F(XattrTest, XattrInvalidPrefix) {
}
TEST_F(XattrTest, XattrReadOnly) {
// TODO(b/127675828): Support setxattr and getxattr.
SKIP_IF(IsRunningOnGvisor());
// Drop capabilities that allow us to override file and directory permissions.
ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false));
ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false));
@@ -102,22 +92,28 @@ TEST_F(XattrTest, XattrReadOnly) {
char name[] = "user.abc";
char val = 'a';
size_t size = sizeof(val);
EXPECT_THAT(setxattr(path, name, &val, size, /*flags=*/0), SyscallSucceeds());
// TODO(b/127675828): Support setxattr and getxattr.
if (!IsRunningOnGvisor()) {
EXPECT_THAT(setxattr(path, name, &val, size, /*flags=*/0),
SyscallSucceeds());
}
ASSERT_NO_ERRNO(testing::Chmod(test_file_name_, S_IRUSR));
EXPECT_THAT(setxattr(path, name, &val, size, /*flags=*/0),
SyscallFailsWithErrno(EACCES));
char buf = '-';
EXPECT_THAT(getxattr(path, name, &buf, size), SyscallSucceedsWithValue(size));
EXPECT_EQ(buf, val);
// TODO(b/127675828): Support setxattr and getxattr.
if (!IsRunningOnGvisor()) {
char buf = '-';
EXPECT_THAT(getxattr(path, name, &buf, size),
SyscallSucceedsWithValue(size));
EXPECT_EQ(buf, val);
}
}
TEST_F(XattrTest, XattrWriteOnly) {
// TODO(b/127675828): Support setxattr and getxattr.
SKIP_IF(IsRunningOnGvisor());
// Drop capabilities that allow us to override file and directory permissions.
ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false));
ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false));
@@ -128,7 +124,12 @@ TEST_F(XattrTest, XattrWriteOnly) {
char name[] = "user.abc";
char val = 'a';
size_t size = sizeof(val);
EXPECT_THAT(setxattr(path, name, &val, size, /*flags=*/0), SyscallSucceeds());
// TODO(b/127675828): Support setxattr and getxattr.
if (!IsRunningOnGvisor()) {
EXPECT_THAT(setxattr(path, name, &val, size, /*flags=*/0),
SyscallSucceeds());
}
EXPECT_THAT(getxattr(path, name, nullptr, 0), SyscallFailsWithErrno(EACCES));
}
@@ -172,9 +173,6 @@ TEST_F(XattrTest, XattrOnSymlink) {
}
TEST_F(XattrTest, XattrOnInvalidFileTypes) {
// TODO(b/127675828): Support setxattr and getxattr.
SKIP_IF(IsRunningOnGvisor());
char name[] = "user.abc";
char char_device[] = "/dev/zero";
@@ -226,9 +224,6 @@ TEST_F(XattrTest, SetxattrZeroSize) {
}
TEST_F(XattrTest, SetxattrSizeTooLarge) {
// TODO(b/127675828): Support setxattr and getxattr.
SKIP_IF(IsRunningOnGvisor());
const char* path = test_file_name_.c_str();
char name[] = "user.abc";
@@ -240,19 +235,24 @@ TEST_F(XattrTest, SetxattrSizeTooLarge) {
EXPECT_THAT(setxattr(path, name, val.data(), size, /*flags=*/0),
SyscallFailsWithErrno(E2BIG));
EXPECT_THAT(getxattr(path, name, nullptr, 0), SyscallFailsWithErrno(ENODATA));
// TODO(b/127675828): Support setxattr and getxattr.
if (!IsRunningOnGvisor()) {
EXPECT_THAT(getxattr(path, name, nullptr, 0),
SyscallFailsWithErrno(ENODATA));
}
}
TEST_F(XattrTest, SetxattrNullValueAndNonzeroSize) {
// TODO(b/127675828): Support setxattr and getxattr.
SKIP_IF(IsRunningOnGvisor());
const char* path = test_file_name_.c_str();
char name[] = "user.abc";
EXPECT_THAT(setxattr(path, name, nullptr, 1, /*flags=*/0),
SyscallFailsWithErrno(EFAULT));
EXPECT_THAT(getxattr(path, name, nullptr, 0), SyscallFailsWithErrno(ENODATA));
// TODO(b/127675828): Support setxattr and getxattr.
if (!IsRunningOnGvisor()) {
EXPECT_THAT(getxattr(path, name, nullptr, 0),
SyscallFailsWithErrno(ENODATA));
}
}
TEST_F(XattrTest, SetxattrNullValueAndZeroSize) {
@@ -350,9 +350,6 @@ TEST_F(XattrTest, SetxattrReplaceFlag) {
}
TEST_F(XattrTest, SetxattrInvalidFlags) {
// TODO(b/127675828): Support setxattr and getxattr.
SKIP_IF(IsRunningOnGvisor());
const char* path = test_file_name_.c_str();
int invalid_flags = 0xff;
EXPECT_THAT(setxattr(path, nullptr, nullptr, 0, invalid_flags),