github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/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 SKIP_IF(IsRunningWithVFS1()); 225 TempPath tmpfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); 226 FileDescriptor f = 227 ASSERT_NO_ERRNO_AND_VALUE(Open(tmpfile.path().c_str(), O_PATH)); 228 int fd = f.get(); 229 230 EXPECT_THAT(WriteBytes(fd, 1024), SyscallFailsWithErrno(EBADF)); 231 } 232 233 TEST_F(WriteTest, WritevWithOpath) { 234 SKIP_IF(IsRunningWithVFS1()); 235 TempPath tmpfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); 236 FileDescriptor f = 237 ASSERT_NO_ERRNO_AND_VALUE(Open(tmpfile.path().c_str(), O_PATH)); 238 int fd = f.get(); 239 240 char buf[16]; 241 struct iovec iov; 242 iov.iov_base = buf; 243 iov.iov_len = sizeof(buf); 244 245 EXPECT_THAT(writev(fd, &iov, /*__count=*/1), SyscallFailsWithErrno(EBADF)); 246 } 247 248 TEST_F(WriteTest, PwriteWithOpath) { 249 SKIP_IF(IsRunningWithVFS1()); 250 TempPath tmpfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); 251 FileDescriptor f = 252 ASSERT_NO_ERRNO_AND_VALUE(Open(tmpfile.path().c_str(), O_PATH)); 253 int fd = f.get(); 254 255 const std::string data = "hello world\n"; 256 257 EXPECT_THAT(pwrite(fd, data.data(), data.size(), 0), 258 SyscallFailsWithErrno(EBADF)); 259 } 260 261 // Test that partial writes that hit SIGSEGV are correctly handled and return 262 // partial write. 263 TEST_F(WriteTest, PartialWriteSIGSEGV) { 264 // Allocate 2 pages and remove permission from the second. 265 const size_t size = 2 * kPageSize; 266 void* addr = mmap(0, size, PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0); 267 ASSERT_NE(addr, MAP_FAILED); 268 auto cleanup = Cleanup( 269 [addr, size] { EXPECT_THAT(munmap(addr, size), SyscallSucceeds()); }); 270 271 void* badAddr = reinterpret_cast<char*>(addr) + kPageSize; 272 ASSERT_THAT(mprotect(badAddr, kPageSize, PROT_NONE), SyscallSucceeds()); 273 274 TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); 275 FileDescriptor fd = 276 ASSERT_NO_ERRNO_AND_VALUE(Open(file.path().c_str(), O_WRONLY)); 277 278 // Attempt to write both pages to the file. Create a non-contiguous iovec pair 279 // to ensure operation is done in 2 steps. 280 struct iovec iov[] = { 281 { 282 .iov_base = addr, 283 .iov_len = kPageSize, 284 }, 285 { 286 .iov_base = addr, 287 .iov_len = size, 288 }, 289 }; 290 // Write should succeed for the first iovec and half of the second (=2 pages). 291 EXPECT_THAT(pwritev(fd.get(), iov, ABSL_ARRAYSIZE(iov), 0), 292 SyscallSucceedsWithValue(2 * kPageSize)); 293 } 294 295 // Test that partial writes that hit SIGBUS are correctly handled and return 296 // partial write. 297 TEST_F(WriteTest, PartialWriteSIGBUS) { 298 SKIP_IF(getenv("GVISOR_GOFER_UNCACHED")); // Can't mmap from uncached files. 299 300 TempPath mapfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); 301 FileDescriptor fd_map = 302 ASSERT_NO_ERRNO_AND_VALUE(Open(mapfile.path().c_str(), O_RDWR)); 303 304 // Let the first page be read to force a partial write. 305 ASSERT_THAT(ftruncate(fd_map.get(), kPageSize), SyscallSucceeds()); 306 307 // Map 2 pages, one of which is not allocated in the backing file. Reading 308 // from it will trigger a SIGBUS. 309 const size_t size = 2 * kPageSize; 310 void* addr = 311 mmap(NULL, size, PROT_READ, MAP_FILE | MAP_PRIVATE, fd_map.get(), 0); 312 ASSERT_NE(addr, MAP_FAILED); 313 auto cleanup = Cleanup( 314 [addr, size] { EXPECT_THAT(munmap(addr, size), SyscallSucceeds()); }); 315 316 TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); 317 FileDescriptor fd = 318 ASSERT_NO_ERRNO_AND_VALUE(Open(file.path().c_str(), O_WRONLY)); 319 320 // Attempt to write both pages to the file. Create a non-contiguous iovec pair 321 // to ensure operation is done in 2 steps. 322 struct iovec iov[] = { 323 { 324 .iov_base = addr, 325 .iov_len = kPageSize, 326 }, 327 { 328 .iov_base = addr, 329 .iov_len = size, 330 }, 331 }; 332 // Write should succeed for the first iovec and half of the second (=2 pages). 333 ASSERT_THAT(pwritev(fd.get(), iov, ABSL_ARRAYSIZE(iov), 0), 334 SyscallSucceedsWithValue(2 * kPageSize)); 335 } 336 337 } // namespace 338 339 } // namespace testing 340 } // namespace gvisor