gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/test/syscalls/linux/write.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 <errno.h> 16 #include <fcntl.h> 17 #include <signal.h> 18 #include <sys/mman.h> 19 #include <sys/resource.h> 20 #include <sys/stat.h> 21 #include <sys/types.h> 22 #include <time.h> 23 #include <unistd.h> 24 25 #include "gmock/gmock.h" 26 #include "gtest/gtest.h" 27 #include "absl/base/macros.h" 28 #include "test/util/cleanup.h" 29 #include "test/util/temp_path.h" 30 #include "test/util/test_util.h" 31 32 namespace gvisor { 33 namespace testing { 34 35 namespace { 36 37 // TODO(gvisor.dev/issue/2370): This test is currently very rudimentary. 38 class WriteTest : public ::testing::Test { 39 public: 40 ssize_t WriteBytes(int fd, int bytes) { 41 std::vector<char> buf(bytes); 42 std::fill(buf.begin(), buf.end(), 'a'); 43 return WriteFd(fd, buf.data(), buf.size()); 44 } 45 }; 46 47 TEST_F(WriteTest, WriteNoExceedsRLimit) { 48 // Get the current rlimit and restore after test run. 49 struct rlimit initial_lim; 50 ASSERT_THAT(getrlimit(RLIMIT_FSIZE, &initial_lim), SyscallSucceeds()); 51 auto cleanup = Cleanup([&initial_lim] { 52 EXPECT_THAT(setrlimit(RLIMIT_FSIZE, &initial_lim), SyscallSucceeds()); 53 }); 54 55 int fd; 56 struct rlimit setlim; 57 const int target_lim = 1024; 58 setlim.rlim_cur = target_lim; 59 setlim.rlim_max = RLIM_INFINITY; 60 const std::string pathname = NewTempAbsPath(); 61 ASSERT_THAT(fd = open(pathname.c_str(), O_WRONLY | O_CREAT, S_IRWXU), 62 SyscallSucceeds()); 63 ASSERT_THAT(setrlimit(RLIMIT_FSIZE, &setlim), SyscallSucceeds()); 64 65 EXPECT_THAT(WriteBytes(fd, target_lim), SyscallSucceedsWithValue(target_lim)); 66 67 std::vector<char> buf(target_lim + 1); 68 std::fill(buf.begin(), buf.end(), 'a'); 69 EXPECT_THAT(pwrite(fd, buf.data(), target_lim, 1), SyscallSucceeds()); 70 EXPECT_THAT(pwrite64(fd, buf.data(), target_lim, 1), SyscallSucceeds()); 71 72 EXPECT_THAT(close(fd), SyscallSucceeds()); 73 } 74 75 TEST_F(WriteTest, WriteExceedsRLimit) { 76 // Get the current rlimit and restore after test run. 77 struct rlimit initial_lim; 78 ASSERT_THAT(getrlimit(RLIMIT_FSIZE, &initial_lim), SyscallSucceeds()); 79 auto cleanup = Cleanup([&initial_lim] { 80 EXPECT_THAT(setrlimit(RLIMIT_FSIZE, &initial_lim), SyscallSucceeds()); 81 }); 82 83 int fd; 84 sigset_t filesize_mask; 85 sigemptyset(&filesize_mask); 86 sigaddset(&filesize_mask, SIGXFSZ); 87 88 struct rlimit setlim; 89 const int target_lim = 1024; 90 setlim.rlim_cur = target_lim; 91 setlim.rlim_max = RLIM_INFINITY; 92 93 const std::string pathname = NewTempAbsPath(); 94 ASSERT_THAT(fd = open(pathname.c_str(), O_WRONLY | O_CREAT, S_IRWXU), 95 SyscallSucceeds()); 96 ASSERT_THAT(setrlimit(RLIMIT_FSIZE, &setlim), SyscallSucceeds()); 97 ASSERT_THAT(sigprocmask(SIG_BLOCK, &filesize_mask, nullptr), 98 SyscallSucceeds()); 99 std::vector<char> buf(target_lim + 2); 100 std::fill(buf.begin(), buf.end(), 'a'); 101 102 EXPECT_THAT(write(fd, buf.data(), target_lim + 1), 103 SyscallSucceedsWithValue(target_lim)); 104 EXPECT_THAT(write(fd, buf.data(), 1), SyscallFailsWithErrno(EFBIG)); 105 siginfo_t info; 106 struct timespec timelimit = {0, 0}; 107 ASSERT_THAT(RetryEINTR(sigtimedwait)(&filesize_mask, &info, &timelimit), 108 SyscallSucceedsWithValue(SIGXFSZ)); 109 EXPECT_EQ(info.si_code, SI_USER); 110 EXPECT_EQ(info.si_pid, getpid()); 111 EXPECT_EQ(info.si_uid, getuid()); 112 113 EXPECT_THAT(pwrite(fd, buf.data(), target_lim + 1, 1), 114 SyscallSucceedsWithValue(target_lim - 1)); 115 EXPECT_THAT(pwrite(fd, buf.data(), 1, target_lim), 116 SyscallFailsWithErrno(EFBIG)); 117 ASSERT_THAT(RetryEINTR(sigtimedwait)(&filesize_mask, &info, &timelimit), 118 SyscallSucceedsWithValue(SIGXFSZ)); 119 EXPECT_EQ(info.si_code, SI_USER); 120 EXPECT_EQ(info.si_pid, getpid()); 121 EXPECT_EQ(info.si_uid, getuid()); 122 123 EXPECT_THAT(pwrite64(fd, buf.data(), target_lim + 1, 1), 124 SyscallSucceedsWithValue(target_lim - 1)); 125 EXPECT_THAT(pwrite64(fd, buf.data(), 1, target_lim), 126 SyscallFailsWithErrno(EFBIG)); 127 ASSERT_THAT(RetryEINTR(sigtimedwait)(&filesize_mask, &info, &timelimit), 128 SyscallSucceedsWithValue(SIGXFSZ)); 129 EXPECT_EQ(info.si_code, SI_USER); 130 EXPECT_EQ(info.si_pid, getpid()); 131 EXPECT_EQ(info.si_uid, getuid()); 132 133 ASSERT_THAT(sigprocmask(SIG_UNBLOCK, &filesize_mask, nullptr), 134 SyscallSucceeds()); 135 EXPECT_THAT(close(fd), SyscallSucceeds()); 136 } 137 138 TEST_F(WriteTest, WriteIncrementOffset) { 139 TempPath tmpfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); 140 FileDescriptor f = 141 ASSERT_NO_ERRNO_AND_VALUE(Open(tmpfile.path().c_str(), O_WRONLY)); 142 int fd = f.get(); 143 144 EXPECT_THAT(WriteBytes(fd, 0), SyscallSucceedsWithValue(0)); 145 EXPECT_THAT(lseek(fd, 0, SEEK_CUR), SyscallSucceedsWithValue(0)); 146 147 const int bytes_total = 1024; 148 149 EXPECT_THAT(WriteBytes(fd, bytes_total), 150 SyscallSucceedsWithValue(bytes_total)); 151 EXPECT_THAT(lseek(fd, 0, SEEK_CUR), SyscallSucceedsWithValue(bytes_total)); 152 } 153 154 TEST_F(WriteTest, WriteIncrementOffsetSeek) { 155 const std::string data = "hello world\n"; 156 TempPath tmpfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( 157 GetAbsoluteTestTmpdir(), data, TempPath::kDefaultFileMode)); 158 FileDescriptor f = 159 ASSERT_NO_ERRNO_AND_VALUE(Open(tmpfile.path().c_str(), O_WRONLY)); 160 int fd = f.get(); 161 162 const int seek_offset = data.size() / 2; 163 ASSERT_THAT(lseek(fd, seek_offset, SEEK_SET), 164 SyscallSucceedsWithValue(seek_offset)); 165 166 const int write_bytes = 512; 167 EXPECT_THAT(WriteBytes(fd, write_bytes), 168 SyscallSucceedsWithValue(write_bytes)); 169 EXPECT_THAT(lseek(fd, 0, SEEK_CUR), 170 SyscallSucceedsWithValue(seek_offset + write_bytes)); 171 } 172 173 TEST_F(WriteTest, WriteIncrementOffsetAppend) { 174 const std::string data = "hello world\n"; 175 TempPath tmpfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( 176 GetAbsoluteTestTmpdir(), data, TempPath::kDefaultFileMode)); 177 FileDescriptor f = ASSERT_NO_ERRNO_AND_VALUE( 178 Open(tmpfile.path().c_str(), O_WRONLY | O_APPEND)); 179 int fd = f.get(); 180 181 EXPECT_THAT(WriteBytes(fd, 1024), SyscallSucceedsWithValue(1024)); 182 EXPECT_THAT(lseek(fd, 0, SEEK_CUR), 183 SyscallSucceedsWithValue(data.size() + 1024)); 184 } 185 186 TEST_F(WriteTest, WriteIncrementOffsetEOF) { 187 const std::string data = "hello world\n"; 188 const TempPath tmpfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( 189 GetAbsoluteTestTmpdir(), data, TempPath::kDefaultFileMode)); 190 FileDescriptor f = 191 ASSERT_NO_ERRNO_AND_VALUE(Open(tmpfile.path().c_str(), O_WRONLY)); 192 int fd = f.get(); 193 194 EXPECT_THAT(lseek(fd, 0, SEEK_END), SyscallSucceedsWithValue(data.size())); 195 196 EXPECT_THAT(WriteBytes(fd, 1024), SyscallSucceedsWithValue(1024)); 197 EXPECT_THAT(lseek(fd, 0, SEEK_END), 198 SyscallSucceedsWithValue(data.size() + 1024)); 199 } 200 201 TEST_F(WriteTest, PwriteNoChangeOffset) { 202 TempPath tmpfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); 203 FileDescriptor f = 204 ASSERT_NO_ERRNO_AND_VALUE(Open(tmpfile.path().c_str(), O_WRONLY)); 205 int fd = f.get(); 206 207 const std::string data = "hello world\n"; 208 209 EXPECT_THAT(pwrite(fd, data.data(), data.size(), 0), 210 SyscallSucceedsWithValue(data.size())); 211 EXPECT_THAT(lseek(fd, 0, SEEK_CUR), SyscallSucceedsWithValue(0)); 212 213 const int bytes_total = 1024; 214 ASSERT_THAT(WriteBytes(fd, bytes_total), 215 SyscallSucceedsWithValue(bytes_total)); 216 ASSERT_THAT(lseek(fd, 0, SEEK_CUR), SyscallSucceedsWithValue(bytes_total)); 217 218 EXPECT_THAT(pwrite(fd, data.data(), data.size(), bytes_total), 219 SyscallSucceedsWithValue(data.size())); 220 EXPECT_THAT(lseek(fd, 0, SEEK_CUR), SyscallSucceedsWithValue(bytes_total)); 221 } 222 223 TEST_F(WriteTest, WriteWithOpath) { 224 TempPath tmpfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); 225 FileDescriptor f = 226 ASSERT_NO_ERRNO_AND_VALUE(Open(tmpfile.path().c_str(), O_PATH)); 227 int fd = f.get(); 228 229 EXPECT_THAT(WriteBytes(fd, 1024), SyscallFailsWithErrno(EBADF)); 230 } 231 232 TEST_F(WriteTest, WritevWithOpath) { 233 TempPath tmpfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); 234 FileDescriptor f = 235 ASSERT_NO_ERRNO_AND_VALUE(Open(tmpfile.path().c_str(), O_PATH)); 236 int fd = f.get(); 237 238 char buf[16]; 239 struct iovec iov; 240 iov.iov_base = buf; 241 iov.iov_len = sizeof(buf); 242 243 EXPECT_THAT(writev(fd, &iov, /*__count=*/1), SyscallFailsWithErrno(EBADF)); 244 } 245 246 TEST_F(WriteTest, PwriteWithOpath) { 247 TempPath tmpfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); 248 FileDescriptor f = 249 ASSERT_NO_ERRNO_AND_VALUE(Open(tmpfile.path().c_str(), O_PATH)); 250 int fd = f.get(); 251 252 const std::string data = "hello world\n"; 253 254 EXPECT_THAT(pwrite(fd, data.data(), data.size(), 0), 255 SyscallFailsWithErrno(EBADF)); 256 } 257 258 // Test that partial writes that hit SIGSEGV are correctly handled and return 259 // partial write. 260 TEST_F(WriteTest, PartialWriteSIGSEGV) { 261 // Allocate 2 pages and remove permission from the second. 262 const size_t size = 2 * kPageSize; 263 void* addr = mmap(0, size, PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0); 264 ASSERT_NE(addr, MAP_FAILED); 265 auto cleanup = Cleanup( 266 [addr, size] { EXPECT_THAT(munmap(addr, size), SyscallSucceeds()); }); 267 268 void* badAddr = reinterpret_cast<char*>(addr) + kPageSize; 269 ASSERT_THAT(mprotect(badAddr, kPageSize, PROT_NONE), SyscallSucceeds()); 270 271 TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); 272 FileDescriptor fd = 273 ASSERT_NO_ERRNO_AND_VALUE(Open(file.path().c_str(), O_WRONLY)); 274 275 // Attempt to write both pages to the file. Create a non-contiguous iovec pair 276 // to ensure operation is done in 2 steps. 277 struct iovec iov[] = { 278 { 279 .iov_base = addr, 280 .iov_len = kPageSize, 281 }, 282 { 283 .iov_base = addr, 284 .iov_len = size, 285 }, 286 }; 287 // Write should succeed for the first iovec and half of the second (=2 pages). 288 EXPECT_THAT(pwritev(fd.get(), iov, ABSL_ARRAYSIZE(iov), 0), 289 SyscallSucceedsWithValue(2 * kPageSize)); 290 } 291 292 // Test that partial writes that hit SIGBUS are correctly handled and return 293 // partial write. 294 TEST_F(WriteTest, PartialWriteSIGBUS) { 295 SKIP_IF(getenv("GVISOR_GOFER_UNCACHED")); // Can't mmap from uncached files. 296 // TODO(b/264306751): Remove once FUSE implements mmap. 297 SKIP_IF(getenv("GVISOR_FUSE_TEST")); 298 299 TempPath mapfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); 300 FileDescriptor fd_map = 301 ASSERT_NO_ERRNO_AND_VALUE(Open(mapfile.path().c_str(), O_RDWR)); 302 303 // Let the first page be read to force a partial write. 304 ASSERT_THAT(ftruncate(fd_map.get(), kPageSize), SyscallSucceeds()); 305 306 // Map 2 pages, one of which is not allocated in the backing file. Reading 307 // from it will trigger a SIGBUS. 308 const size_t size = 2 * kPageSize; 309 void* addr = 310 mmap(NULL, size, PROT_READ, MAP_FILE | MAP_PRIVATE, fd_map.get(), 0); 311 ASSERT_NE(addr, MAP_FAILED); 312 auto cleanup = Cleanup( 313 [addr, size] { EXPECT_THAT(munmap(addr, size), SyscallSucceeds()); }); 314 315 TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); 316 FileDescriptor fd = 317 ASSERT_NO_ERRNO_AND_VALUE(Open(file.path().c_str(), O_WRONLY)); 318 319 // Attempt to write both pages to the file. Create a non-contiguous iovec pair 320 // to ensure operation is done in 2 steps. 321 struct iovec iov[] = { 322 { 323 .iov_base = addr, 324 .iov_len = kPageSize, 325 }, 326 { 327 .iov_base = addr, 328 .iov_len = size, 329 }, 330 }; 331 // Write should succeed for the first iovec and half of the second (=2 pages). 332 ASSERT_THAT(pwritev(fd.get(), iov, ABSL_ARRAYSIZE(iov), 0), 333 SyscallSucceedsWithValue(2 * kPageSize)); 334 } 335 336 } // namespace 337 338 } // namespace testing 339 } // namespace gvisor