gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/test/syscalls/linux/chmod.cc (about)

     1  // Copyright 2018 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  #include <fcntl.h>
    16  #include <sys/stat.h>
    17  #include <sys/syscall.h>
    18  #include <sys/types.h>
    19  #include <unistd.h>
    20  
    21  #include <string>
    22  
    23  #include "gtest/gtest.h"
    24  #include "test/util/capability_util.h"
    25  #include "test/util/file_descriptor.h"
    26  #include "test/util/fs_util.h"
    27  #include "test/util/temp_path.h"
    28  #include "test/util/test_util.h"
    29  
    30  namespace gvisor {
    31  namespace testing {
    32  
    33  namespace {
    34  
    35  TEST(ChmodTest, ChmodFileSucceeds) {
    36    // Drop capabilities that allow us to override file permissions.
    37    AutoCapability cap(CAP_DAC_OVERRIDE, false);
    38  
    39    auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
    40  
    41    ASSERT_THAT(chmod(file.path().c_str(), 0466), SyscallSucceeds());
    42    EXPECT_THAT(open(file.path().c_str(), O_RDWR), SyscallFailsWithErrno(EACCES));
    43  }
    44  
    45  TEST(ChmodTest, ChmodDirSucceeds) {
    46    // Drop capabilities that allow us to override file and directory permissions.
    47    AutoCapability cap1(CAP_DAC_OVERRIDE, false);
    48    AutoCapability cap2(CAP_DAC_READ_SEARCH, false);
    49  
    50    auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
    51    const std::string fileInDir = NewTempAbsPathInDir(dir.path());
    52  
    53    ASSERT_THAT(chmod(dir.path().c_str(), 0466), SyscallSucceeds());
    54    EXPECT_THAT(open(fileInDir.c_str(), O_RDONLY), SyscallFailsWithErrno(EACCES));
    55  }
    56  
    57  TEST(ChmodTest, FchmodFileSucceeds) {
    58    // Drop capabilities that allow us to file directory permissions.
    59    AutoCapability cap(CAP_DAC_OVERRIDE, false);
    60  
    61    auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileMode(0666));
    62    int fd;
    63    ASSERT_THAT(fd = open(file.path().c_str(), O_RDWR), SyscallSucceeds());
    64  
    65    {
    66      const DisableSave ds;  // File permissions are reduced.
    67      ASSERT_THAT(fchmod(fd, 0444), SyscallSucceeds());
    68      EXPECT_THAT(close(fd), SyscallSucceeds());
    69    }
    70  
    71    EXPECT_THAT(open(file.path().c_str(), O_RDWR), SyscallFailsWithErrno(EACCES));
    72  }
    73  
    74  TEST(ChmodTest, FchmodDirSucceeds) {
    75    // Drop capabilities that allow us to override file and directory permissions.
    76    AutoCapability cap1(CAP_DAC_OVERRIDE, false);
    77    AutoCapability cap2(CAP_DAC_READ_SEARCH, false);
    78  
    79    auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
    80    int fd;
    81    ASSERT_THAT(fd = open(dir.path().c_str(), O_RDONLY | O_DIRECTORY),
    82                SyscallSucceeds());
    83  
    84    {
    85      const DisableSave ds;  // File permissions are reduced.
    86      ASSERT_THAT(fchmod(fd, 0), SyscallSucceeds());
    87      EXPECT_THAT(close(fd), SyscallSucceeds());
    88    }
    89  
    90    EXPECT_THAT(open(dir.path().c_str(), O_RDONLY),
    91                SyscallFailsWithErrno(EACCES));
    92  }
    93  
    94  TEST(ChmodTest, FchmodBadF) {
    95    ASSERT_THAT(fchmod(-1, 0444), SyscallFailsWithErrno(EBADF));
    96  }
    97  
    98  TEST(ChmodTest, FchmodatBadF) {
    99    ASSERT_THAT(fchmodat(-1, "foo", 0444, 0), SyscallFailsWithErrno(EBADF));
   100  }
   101  
   102  TEST(ChmodTest, FchmodFileWithOpath) {
   103    auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
   104    FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_PATH));
   105  
   106    // Bionic's implementation of fchmod() uses chmod() when O_PATH is set
   107    // to circumvent the behavior this is testing for.
   108    // Use syscall() here to avoid running through Bionic on Android.
   109    ASSERT_THAT(syscall(SYS_fchmod, fd.get(), 0444),
   110                SyscallFailsWithErrno(EBADF));
   111  }
   112  
   113  TEST(ChmodTest, FchmodDirWithOpath) {
   114    const auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
   115    const auto fd =
   116        ASSERT_NO_ERRNO_AND_VALUE(Open(dir.path(), O_DIRECTORY | O_PATH));
   117  
   118    ASSERT_THAT(syscall(SYS_fchmod, fd.get(), 0444),
   119                SyscallFailsWithErrno(EBADF));
   120  }
   121  
   122  TEST(ChmodTest, FchmodatWithOpath) {
   123    // Drop capabilities that allow us to override file permissions.
   124    AutoCapability cap(CAP_DAC_OVERRIDE, false);
   125  
   126    auto temp_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
   127  
   128    const auto parent_fd = ASSERT_NO_ERRNO_AND_VALUE(
   129        Open(GetAbsoluteTestTmpdir().c_str(), O_PATH | O_DIRECTORY));
   130  
   131    ASSERT_THAT(
   132        fchmodat(parent_fd.get(), std::string(Basename(temp_file.path())).c_str(),
   133                 0444, 0),
   134        SyscallSucceeds());
   135  
   136    EXPECT_THAT(open(temp_file.path().c_str(), O_RDWR),
   137                SyscallFailsWithErrno(EACCES));
   138  }
   139  
   140  TEST(ChmodTest, FchmodatNotDir) {
   141    ASSERT_THAT(fchmodat(-1, "", 0444, 0), SyscallFailsWithErrno(ENOENT));
   142  }
   143  
   144  TEST(ChmodTest, FchmodatFileAbsolutePath) {
   145    // Drop capabilities that allow us to override file permissions.
   146    AutoCapability cap(CAP_DAC_OVERRIDE, false);
   147  
   148    auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
   149  
   150    ASSERT_THAT(fchmodat(-1, file.path().c_str(), 0444, 0), SyscallSucceeds());
   151    EXPECT_THAT(open(file.path().c_str(), O_RDWR), SyscallFailsWithErrno(EACCES));
   152  }
   153  
   154  TEST(ChmodTest, FchmodatDirAbsolutePath) {
   155    // Drop capabilities that allow us to override file and directory permissions.
   156    AutoCapability cap1(CAP_DAC_OVERRIDE, false);
   157    AutoCapability cap2(CAP_DAC_READ_SEARCH, false);
   158  
   159    auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
   160  
   161    int fd;
   162    ASSERT_THAT(fd = open(dir.path().c_str(), O_RDONLY | O_DIRECTORY),
   163                SyscallSucceeds());
   164    EXPECT_THAT(close(fd), SyscallSucceeds());
   165  
   166    ASSERT_THAT(fchmodat(-1, dir.path().c_str(), 0, 0), SyscallSucceeds());
   167    EXPECT_THAT(open(dir.path().c_str(), O_RDONLY),
   168                SyscallFailsWithErrno(EACCES));
   169  }
   170  
   171  TEST(ChmodTest, FchmodatFile) {
   172    // Drop capabilities that allow us to override file permissions.
   173    AutoCapability cap(CAP_DAC_OVERRIDE, false);
   174  
   175    auto temp_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
   176  
   177    int parent_fd;
   178    ASSERT_THAT(
   179        parent_fd = open(GetAbsoluteTestTmpdir().c_str(), O_RDONLY | O_DIRECTORY),
   180        SyscallSucceeds());
   181  
   182    ASSERT_THAT(
   183        fchmodat(parent_fd, std::string(Basename(temp_file.path())).c_str(), 0444,
   184                 0),
   185        SyscallSucceeds());
   186    EXPECT_THAT(close(parent_fd), SyscallSucceeds());
   187  
   188    EXPECT_THAT(open(temp_file.path().c_str(), O_RDWR),
   189                SyscallFailsWithErrno(EACCES));
   190  }
   191  
   192  TEST(ChmodTest, FchmodatDir) {
   193    // Drop capabilities that allow us to override file and directory permissions.
   194    AutoCapability cap1(CAP_DAC_OVERRIDE, false);
   195    AutoCapability cap2(CAP_DAC_READ_SEARCH, false);
   196  
   197    auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
   198  
   199    int parent_fd;
   200    ASSERT_THAT(
   201        parent_fd = open(GetAbsoluteTestTmpdir().c_str(), O_RDONLY | O_DIRECTORY),
   202        SyscallSucceeds());
   203  
   204    int fd;
   205    ASSERT_THAT(fd = open(dir.path().c_str(), O_RDONLY | O_DIRECTORY),
   206                SyscallSucceeds());
   207    EXPECT_THAT(close(fd), SyscallSucceeds());
   208  
   209    ASSERT_THAT(
   210        fchmodat(parent_fd, std::string(Basename(dir.path())).c_str(), 0, 0),
   211        SyscallSucceeds());
   212    EXPECT_THAT(close(parent_fd), SyscallSucceeds());
   213  
   214    EXPECT_THAT(open(dir.path().c_str(), O_RDONLY | O_DIRECTORY),
   215                SyscallFailsWithErrno(EACCES));
   216  }
   217  
   218  TEST(ChmodTest, ChmodDowngradeWritability) {
   219    auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileMode(0666));
   220  
   221    int fd;
   222    ASSERT_THAT(fd = open(file.path().c_str(), O_RDWR), SyscallSucceeds());
   223  
   224    const DisableSave ds;  // Permissions are dropped.
   225    ASSERT_THAT(chmod(file.path().c_str(), 0444), SyscallSucceeds());
   226    EXPECT_THAT(write(fd, "hello", 5), SyscallSucceedsWithValue(5));
   227  
   228    EXPECT_THAT(close(fd), SyscallSucceeds());
   229  }
   230  
   231  TEST(ChmodTest, ChmodFileToNoPermissionsSucceeds) {
   232    // Drop capabilities that allow us to override file permissions.
   233    AutoCapability cap1(CAP_DAC_OVERRIDE, false);
   234    AutoCapability cap2(CAP_DAC_READ_SEARCH, false);
   235  
   236    auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileMode(0666));
   237  
   238    ASSERT_THAT(chmod(file.path().c_str(), 0), SyscallSucceeds());
   239  
   240    EXPECT_THAT(open(file.path().c_str(), O_RDONLY),
   241                SyscallFailsWithErrno(EACCES));
   242  }
   243  
   244  TEST(ChmodTest, FchmodDowngradeWritability) {
   245    auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
   246  
   247    int fd;
   248    ASSERT_THAT(fd = open(file.path().c_str(), O_RDWR | O_CREAT, 0666),
   249                SyscallSucceeds());
   250  
   251    const DisableSave ds;  // Permissions are dropped.
   252    ASSERT_THAT(fchmod(fd, 0444), SyscallSucceeds());
   253    EXPECT_THAT(write(fd, "hello", 5), SyscallSucceedsWithValue(5));
   254  
   255    EXPECT_THAT(close(fd), SyscallSucceeds());
   256  }
   257  
   258  TEST(ChmodTest, FchmodFileToNoPermissionsSucceeds) {
   259    // Drop capabilities that allow us to override file permissions.
   260    AutoCapability cap1(CAP_DAC_OVERRIDE, false);
   261    AutoCapability cap2(CAP_DAC_READ_SEARCH, false);
   262  
   263    auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileMode(0666));
   264  
   265    int fd;
   266    ASSERT_THAT(fd = open(file.path().c_str(), O_RDWR), SyscallSucceeds());
   267  
   268    {
   269      const DisableSave ds;  // Permissions are dropped.
   270      ASSERT_THAT(fchmod(fd, 0), SyscallSucceeds());
   271      EXPECT_THAT(close(fd), SyscallSucceeds());
   272    }
   273  
   274    EXPECT_THAT(open(file.path().c_str(), O_RDONLY),
   275                SyscallFailsWithErrno(EACCES));
   276  }
   277  
   278  // Verify that we can get a RW FD after chmod, even if a RO fd is left open.
   279  TEST(ChmodTest, ChmodWritableWithOpenFD) {
   280    // FIXME(b/72455313): broken on hostfs.
   281    if (IsRunningOnGvisor()) {
   282      return;
   283    }
   284  
   285    TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileMode(0444));
   286  
   287    FileDescriptor fd1 = ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDONLY));
   288  
   289    ASSERT_THAT(fchmod(fd1.get(), 0644), SyscallSucceeds());
   290  
   291    // This FD is writable, even though fd1 has a read-only reference to the file.
   292    FileDescriptor fd2 = ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR));
   293  
   294    // fd1 is not writable, but fd2 is.
   295    char c = 'a';
   296    EXPECT_THAT(WriteFd(fd1.get(), &c, 1), SyscallFailsWithErrno(EBADF));
   297    EXPECT_THAT(WriteFd(fd2.get(), &c, 1), SyscallSucceedsWithValue(1));
   298  }
   299  
   300  }  // namespace
   301  
   302  }  // namespace testing
   303  }  // namespace gvisor