gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/test/syscalls/linux/dup.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/resource.h> 17 #include <unistd.h> 18 19 #include <cerrno> 20 #include <memory> 21 22 #include "gtest/gtest.h" 23 #include "absl/memory/memory.h" 24 #include "absl/strings/str_cat.h" 25 #include "test/util/eventfd_util.h" 26 #include "test/util/file_descriptor.h" 27 #include "test/util/fs_util.h" 28 #include "test/util/linux_capability_util.h" 29 #include "test/util/posix_error.h" 30 #include "test/util/temp_path.h" 31 #include "test/util/test_util.h" 32 33 namespace gvisor { 34 namespace testing { 35 36 namespace { 37 38 PosixErrorOr<FileDescriptor> Dup2(const FileDescriptor& fd, int target_fd) { 39 int new_fd = dup2(fd.get(), target_fd); 40 if (new_fd < 0) { 41 return PosixError(errno, "Dup2"); 42 } 43 return FileDescriptor(new_fd); 44 } 45 46 PosixErrorOr<FileDescriptor> Dup3(const FileDescriptor& fd, int target_fd, 47 int flags) { 48 int new_fd = dup3(fd.get(), target_fd, flags); 49 if (new_fd < 0) { 50 return PosixError(errno, "Dup2"); 51 } 52 return FileDescriptor(new_fd); 53 } 54 55 TEST(DupTest, Dup) { 56 auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); 57 FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(f.path(), O_RDONLY)); 58 59 // Dup the descriptor and make sure it's the same file. 60 FileDescriptor nfd = ASSERT_NO_ERRNO_AND_VALUE(fd.Dup()); 61 ASSERT_NE(fd.get(), nfd.get()); 62 ASSERT_NO_ERRNO(CheckSameFile(fd, nfd)); 63 } 64 65 TEST(DupTest, DupClearsCloExec) { 66 // Open an eventfd file descriptor with FD_CLOEXEC descriptor flag set. 67 FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(NewEventFD(0, EFD_CLOEXEC)); 68 EXPECT_THAT(fcntl(fd.get(), F_GETFD), SyscallSucceedsWithValue(FD_CLOEXEC)); 69 70 // Duplicate the descriptor. Ensure that it doesn't have FD_CLOEXEC set. 71 FileDescriptor nfd = ASSERT_NO_ERRNO_AND_VALUE(fd.Dup()); 72 ASSERT_NE(fd.get(), nfd.get()); 73 ASSERT_NO_ERRNO(CheckSameFile(fd, nfd)); 74 EXPECT_THAT(fcntl(nfd.get(), F_GETFD), SyscallSucceedsWithValue(0)); 75 } 76 77 TEST(DupTest, DupWithOpath) { 78 auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); 79 FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(f.path(), O_PATH)); 80 int flags; 81 ASSERT_THAT(flags = fcntl(fd.get(), F_GETFL), SyscallSucceeds()); 82 83 // Dup the descriptor and make sure it's the same file. 84 FileDescriptor nfd = ASSERT_NO_ERRNO_AND_VALUE(fd.Dup()); 85 ASSERT_NE(fd.get(), nfd.get()); 86 ASSERT_NO_ERRNO(CheckSameFile(fd, nfd)); 87 EXPECT_THAT(fcntl(nfd.get(), F_GETFL), SyscallSucceedsWithValue(flags)); 88 } 89 90 TEST(DupTest, Dup2) { 91 auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); 92 FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(f.path(), O_RDONLY)); 93 94 // Regular dup once. 95 FileDescriptor nfd = ASSERT_NO_ERRNO_AND_VALUE(fd.Dup()); 96 97 ASSERT_NE(fd.get(), nfd.get()); 98 ASSERT_NO_ERRNO(CheckSameFile(fd, nfd)); 99 100 // Dup over the file above. 101 int target_fd = nfd.release(); 102 FileDescriptor nfd2 = ASSERT_NO_ERRNO_AND_VALUE(Dup2(fd, target_fd)); 103 EXPECT_EQ(target_fd, nfd2.get()); 104 ASSERT_NO_ERRNO(CheckSameFile(fd, nfd2)); 105 } 106 107 TEST(DupTest, Rlimit) { 108 auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); 109 FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(f.path(), O_RDONLY)); 110 111 struct rlimit rl = {}; 112 EXPECT_THAT(getrlimit(RLIMIT_NOFILE, &rl), SyscallSucceeds()); 113 114 ASSERT_THAT(setrlimit(RLIMIT_NOFILE, &rl), SyscallSucceeds()); 115 116 constexpr int kFDLimit = 101; 117 // Create a file descriptor that will be above the limit. 118 FileDescriptor aboveLimitFD = 119 ASSERT_NO_ERRNO_AND_VALUE(Dup2(fd, kFDLimit * 2 - 1)); 120 121 rl.rlim_cur = kFDLimit; 122 ASSERT_THAT(setrlimit(RLIMIT_NOFILE, &rl), SyscallSucceeds()); 123 ASSERT_THAT(dup3(fd.get(), kFDLimit, 0), SyscallFails()); 124 125 std::vector<std::unique_ptr<FileDescriptor>> fds; 126 int prev_fd = fd.get(); 127 int used_fds = 0; 128 for (int i = 0; i < kFDLimit; ++i) { 129 int new_fd = dup(fd.get()); 130 if (new_fd == -1) { 131 break; 132 } 133 auto f = std::make_unique<FileDescriptor>(new_fd); 134 EXPECT_LT(new_fd, kFDLimit); 135 EXPECT_GT(new_fd, prev_fd); 136 // Check that all fds in (prev_fd, new_fd) are used. 137 for (int j = prev_fd + 1; j < new_fd; ++j) { 138 if (fcntl(j, F_GETFD) != -1) used_fds++; 139 } 140 prev_fd = new_fd; 141 fds.push_back(std::move(f)); 142 } 143 EXPECT_EQ(fds.size() + used_fds, kFDLimit - fd.get() - 1); 144 } 145 146 TEST(RlimitTest, DupLimitedByNROpenSysctl) { 147 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_RESOURCE))); 148 149 const int kNROpen = 1 << 22; 150 FileDescriptor fd = 151 ASSERT_NO_ERRNO_AND_VALUE(Open("/proc/sys/fs/nr_open", O_WRONLY)); 152 auto data = absl::StrCat(kNROpen); 153 EXPECT_THAT(write(fd.get(), data.c_str(), data.size()), 154 SyscallSucceedsWithValue(data.size())); 155 156 struct rlimit rl = { 157 .rlim_cur = kNROpen + 1, 158 .rlim_max = kNROpen + 1, 159 }; 160 EXPECT_THAT(setrlimit(RLIMIT_NOFILE, &rl), SyscallFailsWithErrno(EPERM)); 161 rl.rlim_cur = kNROpen; 162 rl.rlim_max = kNROpen; 163 ASSERT_THAT(setrlimit(RLIMIT_NOFILE, &rl), SyscallSucceeds()); 164 165 ASSERT_THAT(dup3(fd.get(), kNROpen, 0), SyscallFailsWithErrno(EBADF)); 166 ASSERT_THAT(dup3(fd.get(), kNROpen - 1, 0), SyscallSucceeds()); 167 } 168 169 TEST(DupTest, Dup2SameFD) { 170 auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); 171 FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(f.path(), O_RDONLY)); 172 173 // Should succeed. 174 ASSERT_THAT(dup2(fd.get(), fd.get()), SyscallSucceedsWithValue(fd.get())); 175 } 176 177 TEST(DupTest, Dup2WithOpath) { 178 auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); 179 FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(f.path(), O_PATH)); 180 int flags; 181 ASSERT_THAT(flags = fcntl(fd.get(), F_GETFL), SyscallSucceeds()); 182 183 // Regular dup once. 184 FileDescriptor nfd = ASSERT_NO_ERRNO_AND_VALUE(fd.Dup()); 185 186 ASSERT_NE(fd.get(), nfd.get()); 187 ASSERT_NO_ERRNO(CheckSameFile(fd, nfd)); 188 EXPECT_THAT(fcntl(nfd.get(), F_GETFL), SyscallSucceedsWithValue(flags)); 189 190 // Dup over the file above. 191 int target_fd = nfd.release(); 192 FileDescriptor nfd2 = ASSERT_NO_ERRNO_AND_VALUE(Dup2(fd, target_fd)); 193 EXPECT_EQ(target_fd, nfd2.get()); 194 ASSERT_NO_ERRNO(CheckSameFile(fd, nfd2)); 195 EXPECT_THAT(fcntl(nfd2.get(), F_GETFL), SyscallSucceedsWithValue(flags)); 196 } 197 198 TEST(DupTest, Dup3) { 199 auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); 200 FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(f.path(), O_RDONLY)); 201 202 // Regular dup once. 203 FileDescriptor nfd = ASSERT_NO_ERRNO_AND_VALUE(fd.Dup()); 204 ASSERT_NE(fd.get(), nfd.get()); 205 ASSERT_NO_ERRNO(CheckSameFile(fd, nfd)); 206 207 // Dup over the file above, check that it has no CLOEXEC. 208 nfd = ASSERT_NO_ERRNO_AND_VALUE(Dup3(fd, nfd.release(), 0)); 209 ASSERT_NO_ERRNO(CheckSameFile(fd, nfd)); 210 EXPECT_THAT(fcntl(nfd.get(), F_GETFD), SyscallSucceedsWithValue(0)); 211 212 // Dup over the file again, check that it does not CLOEXEC. 213 nfd = ASSERT_NO_ERRNO_AND_VALUE(Dup3(fd, nfd.release(), O_CLOEXEC)); 214 ASSERT_NO_ERRNO(CheckSameFile(fd, nfd)); 215 EXPECT_THAT(fcntl(nfd.get(), F_GETFD), SyscallSucceedsWithValue(FD_CLOEXEC)); 216 } 217 218 TEST(DupTest, Dup3FailsSameFD) { 219 auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); 220 FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(f.path(), O_RDONLY)); 221 222 // Only dup3 fails if the new and old fd are the same. 223 ASSERT_THAT(dup3(fd.get(), fd.get(), 0), SyscallFailsWithErrno(EINVAL)); 224 } 225 226 TEST(DupTest, Dup3WithOpath) { 227 auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); 228 FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(f.path(), O_PATH)); 229 EXPECT_THAT(fcntl(fd.get(), F_GETFD), SyscallSucceedsWithValue(0)); 230 int flags; 231 ASSERT_THAT(flags = fcntl(fd.get(), F_GETFL), SyscallSucceeds()); 232 233 // Regular dup once. 234 FileDescriptor nfd = ASSERT_NO_ERRNO_AND_VALUE(fd.Dup()); 235 ASSERT_NE(fd.get(), nfd.get()); 236 ASSERT_NO_ERRNO(CheckSameFile(fd, nfd)); 237 238 // Dup over the file above, check that it has no CLOEXEC. 239 nfd = ASSERT_NO_ERRNO_AND_VALUE(Dup3(fd, nfd.release(), 0)); 240 ASSERT_NO_ERRNO(CheckSameFile(fd, nfd)); 241 EXPECT_THAT(fcntl(nfd.get(), F_GETFD), SyscallSucceedsWithValue(0)); 242 EXPECT_THAT(fcntl(nfd.get(), F_GETFL), SyscallSucceedsWithValue(flags)); 243 244 // Dup over the file again, check that it does not CLOEXEC. 245 nfd = ASSERT_NO_ERRNO_AND_VALUE(Dup3(fd, nfd.release(), O_CLOEXEC)); 246 ASSERT_NO_ERRNO(CheckSameFile(fd, nfd)); 247 EXPECT_THAT(fcntl(nfd.get(), F_GETFD), SyscallSucceedsWithValue(FD_CLOEXEC)); 248 EXPECT_THAT(fcntl(nfd.get(), F_GETFL), SyscallSucceedsWithValue(flags)); 249 } 250 251 } // namespace 252 253 } // namespace testing 254 } // namespace gvisor