1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Regression test for the fuse write-extend partial-EOF-page zeroing bug. 4 * 5 * A buffered write that extends i_size past a non-page-aligned EOF must zero 6 * the tail of the old last page. If an application has mmap'd that page and 7 * stored into the post-EOF region (undefined until the file grows), the 8 * now-in-bounds tail must read back as zero, not as the stale stored bytes. 9 * 10 * The bug is exposed on a non-writeback_cache server that keeps the page cache 11 * across the write (FOPEN_KEEP_CACHE without FOPEN_DIRECT_IO). This test is a 12 * raw /dev/fuse server in that mode; the backing data is always zero in the 13 * hole, so any non-zero byte a read sees is stale page-cache data. 14 * 15 * Requires root to mount fuse. 16 */ 17 #define _GNU_SOURCE 18 #include <errno.h> 19 #include <fcntl.h> 20 #include <linux/falloc.h> 21 #include <pthread.h> 22 #include <stdint.h> 23 #include <string.h> 24 #include <unistd.h> 25 #include <sys/mman.h> 26 #include <sys/mount.h> 27 #include <sys/stat.h> 28 #include <sys/uio.h> 29 #include <linux/fuse.h> 30 31 #include "../../kselftest_harness.h" 32 33 #define FUSE_ROOT_ID 1 34 #define FILE_INO 2 35 #define MAX_WRITE (128 * 1024) 36 #define BACKING_SIZE (4 * 1024 * 1024) 37 #define POLLUTE 0xee 38 39 /* Server-side state, shared with the responder thread. */ 40 struct server { 41 int fd; 42 unsigned char backing[BACKING_SIZE]; /* authoritative bytes */ 43 uint64_t size; 44 }; 45 46 static void reply(int fd, uint64_t unique, int error, void *data, size_t len) 47 { 48 struct fuse_out_header oh = { 49 .len = sizeof(oh) + (data ? len : 0), 50 .error = error, 51 .unique = unique, 52 }; 53 struct iovec iov[2] = { { &oh, sizeof(oh) }, { data, len } }; 54 55 /* Errors here are teardown races (device closed on unmount); ignore. */ 56 if (writev(fd, iov, data ? 2 : 1) < 0) 57 return; 58 } 59 60 static void fill_attr(struct fuse_attr *a, uint64_t ino, uint32_t mode, 61 uint64_t size) 62 { 63 memset(a, 0, sizeof(*a)); 64 a->ino = ino; 65 a->mode = mode; 66 a->nlink = 1; 67 a->size = size; 68 a->blksize = sysconf(_SC_PAGESIZE); 69 } 70 71 static void *server_thread(void *arg) 72 { 73 struct server *s = arg; 74 static char buf[MAX_WRITE + 4096]; 75 76 for (;;) { 77 ssize_t n = read(s->fd, buf, sizeof(buf)); 78 struct fuse_in_header *ih = (void *)buf; 79 80 if (n < 0) { 81 if (errno == EINTR || errno == EAGAIN) 82 continue; 83 return NULL; /* device closed on unmount */ 84 } 85 if (n < (ssize_t)sizeof(*ih)) 86 continue; 87 88 switch (ih->opcode) { 89 case FUSE_INIT: { 90 struct fuse_init_in *in = (void *)(ih + 1); 91 struct fuse_init_out out = {0}; 92 93 /* No FUSE_WRITEBACK_CACHE: the exposed configuration. */ 94 out.major = FUSE_KERNEL_VERSION; 95 out.minor = FUSE_KERNEL_MINOR_VERSION; 96 out.max_readahead = in->max_readahead; 97 out.max_write = MAX_WRITE; 98 out.max_background = 16; 99 out.congestion_threshold = 12; 100 out.flags = FUSE_MAX_PAGES; 101 out.max_pages = MAX_WRITE / sysconf(_SC_PAGESIZE); 102 reply(s->fd, ih->unique, 0, &out, sizeof(out)); 103 break; 104 } 105 case FUSE_GETATTR: { 106 struct fuse_attr_out out = {0}; 107 int root = ih->nodeid == FUSE_ROOT_ID; 108 109 out.attr_valid = 3600; 110 fill_attr(&out.attr, ih->nodeid, 111 root ? (S_IFDIR | 0755) : (S_IFREG | 0644), 112 root ? 0 : s->size); 113 reply(s->fd, ih->unique, 0, &out, sizeof(out)); 114 break; 115 } 116 case FUSE_LOOKUP: { 117 struct fuse_entry_out out = {0}; 118 119 out.nodeid = FILE_INO; 120 out.attr_valid = 3600; 121 out.entry_valid = 3600; 122 fill_attr(&out.attr, FILE_INO, S_IFREG | 0644, s->size); 123 reply(s->fd, ih->unique, 0, &out, sizeof(out)); 124 break; 125 } 126 case FUSE_OPEN: 127 case FUSE_OPENDIR: { 128 struct fuse_open_out out = {0}; 129 130 /* Keep the cache across the write, but not direct I/O. */ 131 out.open_flags = FOPEN_KEEP_CACHE; 132 reply(s->fd, ih->unique, 0, &out, sizeof(out)); 133 break; 134 } 135 case FUSE_READ: { 136 struct fuse_read_in *in = (void *)(ih + 1); 137 uint64_t off = in->offset; 138 uint32_t size = in->size; 139 140 if (off >= BACKING_SIZE) 141 size = 0; 142 else if (off + size > BACKING_SIZE) 143 size = BACKING_SIZE - off; 144 reply(s->fd, ih->unique, 0, s->backing + off, size); 145 break; 146 } 147 case FUSE_WRITE: { 148 struct fuse_write_in *in = (void *)(ih + 1); 149 struct fuse_write_out out = {0}; 150 uint64_t off = in->offset; 151 uint32_t size = in->size; 152 153 if (off < BACKING_SIZE) { 154 uint32_t c = size; 155 156 if (off + c > BACKING_SIZE) 157 c = BACKING_SIZE - off; 158 memcpy(s->backing + off, in + 1, c); 159 if (off + c > s->size) 160 s->size = off + c; 161 } 162 out.size = size; 163 reply(s->fd, ih->unique, 0, &out, sizeof(out)); 164 break; 165 } 166 case FUSE_SETATTR: { 167 struct fuse_setattr_in *in = (void *)(ih + 1); 168 struct fuse_attr_out out = {0}; 169 170 if ((in->valid & FATTR_SIZE) && in->size <= BACKING_SIZE) { 171 if (in->size > s->size) 172 memset(s->backing + s->size, 0, 173 in->size - s->size); 174 s->size = in->size; 175 } 176 out.attr_valid = 3600; 177 fill_attr(&out.attr, ih->nodeid, S_IFREG | 0644, s->size); 178 reply(s->fd, ih->unique, 0, &out, sizeof(out)); 179 break; 180 } 181 case FUSE_FALLOCATE: { 182 struct fuse_fallocate_in *in = (void *)(ih + 1); 183 uint64_t end = in->offset + in->length; 184 185 /* Only plain (size-extending) fallocate is used here. */ 186 if (!(in->mode & FALLOC_FL_KEEP_SIZE) && 187 end <= BACKING_SIZE && end > s->size) { 188 memset(s->backing + s->size, 0, end - s->size); 189 s->size = end; 190 } 191 reply(s->fd, ih->unique, 0, NULL, 0); 192 break; 193 } 194 case FUSE_FLUSH: 195 case FUSE_RELEASE: 196 case FUSE_RELEASEDIR: 197 case FUSE_FSYNC: 198 case FUSE_ACCESS: 199 reply(s->fd, ih->unique, 0, NULL, 0); 200 break; 201 case FUSE_FORGET: 202 break; 203 default: 204 reply(s->fd, ih->unique, -EOPNOTSUPP, NULL, 0); 205 break; 206 } 207 } 208 } 209 210 FIXTURE(fuse) 211 { 212 struct server *srv; 213 pthread_t thread; 214 char dir[64]; 215 long page; /* runtime page size */ 216 off_t eof; /* mid-page EOF, page-relative */ 217 int fd; /* open test file */ 218 char *map; /* mmap of the EOF page */ 219 int mounted; 220 }; 221 222 FIXTURE_SETUP(fuse) 223 { 224 char opts[128]; 225 pthread_t t; 226 227 if (geteuid() != 0) 228 SKIP(return, "need root to mount fuse"); 229 230 self->page = sysconf(_SC_PAGESIZE); 231 self->fd = -1; 232 self->map = MAP_FAILED; 233 234 self->srv = mmap(NULL, sizeof(*self->srv), PROT_READ | PROT_WRITE, 235 MAP_SHARED | MAP_ANONYMOUS, -1, 0); 236 ASSERT_NE(MAP_FAILED, self->srv); 237 238 self->srv->fd = open("/dev/fuse", O_RDWR); 239 ASSERT_GE(self->srv->fd, 0); 240 241 strcpy(self->dir, "/tmp/fuse_weof_XXXXXX"); 242 ASSERT_NE(NULL, mkdtemp(self->dir)); 243 244 snprintf(opts, sizeof(opts), 245 "fd=%d,rootmode=40000,user_id=0,group_id=0", 246 self->srv->fd); 247 ASSERT_EQ(0, mount("fuse", self->dir, "fuse", 0, opts)); 248 self->mounted = 1; 249 250 ASSERT_EQ(0, pthread_create(&t, NULL, server_thread, self->srv)); 251 self->thread = t; 252 } 253 254 FIXTURE_TEARDOWN(fuse) 255 { 256 if (self->map != MAP_FAILED) 257 munmap(self->map, self->page); 258 if (self->fd >= 0) 259 close(self->fd); 260 if (self->mounted) 261 umount2(self->dir, MNT_DETACH); 262 if (self->srv && self->srv != MAP_FAILED) { 263 if (self->srv->fd > 0) 264 close(self->srv->fd); 265 munmap(self->srv, sizeof(*self->srv)); 266 } 267 if (self->dir[0]) 268 rmdir(self->dir); 269 } 270 271 /* 272 * Create the test file with a mid-page EOF and mmap-store POLLUTE into its 273 * post-EOF tail (a legal store, undefined until the file grows). Leaves the 274 * file open and the EOF page mapped in the fixture for the caller to extend. 275 */ 276 static void pollute_eof_tail(struct __test_metadata *_metadata, 277 FIXTURE_DATA(fuse) * self) 278 { 279 off_t eof = 2 * self->page + self->page / 4; 280 char path[128]; 281 char *buf; 282 283 snprintf(path, sizeof(path), "%s/file", self->dir); 284 self->fd = open(path, O_RDWR | O_CREAT | O_TRUNC, 0644); 285 ASSERT_GE(self->fd, 0); 286 self->eof = eof; 287 288 buf = malloc(eof); 289 ASSERT_NE(NULL, buf); 290 memset(buf, 'A', eof); 291 ASSERT_EQ(eof, pwrite(self->fd, buf, eof, 0)); 292 free(buf); 293 294 self->map = mmap(NULL, self->page, PROT_READ | PROT_WRITE, MAP_SHARED, 295 self->fd, eof & ~(self->page - 1)); 296 ASSERT_NE(MAP_FAILED, self->map); 297 memset(self->map + (eof & (self->page - 1)), POLLUTE, 298 self->page - (eof & (self->page - 1))); 299 } 300 301 /* Assert the old post-EOF tail [eof, end of its page) now reads back as zero. */ 302 static void assert_tail_zeroed(struct __test_metadata *_metadata, 303 FIXTURE_DATA(fuse) * self) 304 { 305 off_t base = self->eof & ~(self->page - 1); 306 char *tail = malloc(self->page); 307 int i; 308 309 ASSERT_NE(NULL, tail); 310 ASSERT_EQ(self->page, pread(self->fd, tail, self->page, base)); 311 for (i = self->eof & (self->page - 1); i < self->page; i++) 312 ASSERT_EQ(0, tail[i]); 313 free(tail); 314 } 315 316 /* Basic: pollute the post-EOF tail, extend past it by a later write. */ 317 TEST_F(fuse, write_extend) 318 { 319 pollute_eof_tail(_metadata, self); 320 ASSERT_EQ(4, pwrite(self->fd, "data", 4, 5 * self->page + self->page / 3)); 321 assert_tail_zeroed(_metadata, self); 322 } 323 324 /* Extend via ftruncate() rather than a write. */ 325 TEST_F(fuse, ftruncate_extend) 326 { 327 pollute_eof_tail(_metadata, self); 328 ASSERT_EQ(0, ftruncate(self->fd, 8 * self->page)); 329 assert_tail_zeroed(_metadata, self); 330 } 331 332 /* Extend via fallocate() starting at the old EOF. */ 333 TEST_F(fuse, fallocate_extend) 334 { 335 pollute_eof_tail(_metadata, self); 336 ASSERT_EQ(0, fallocate(self->fd, 0, self->eof, 4 * self->page)); 337 assert_tail_zeroed(_metadata, self); 338 } 339 340 /* A write landing inside the old EOF page must not clobber its own data. */ 341 TEST_F(fuse, extend_into_eof_page_preserves_data) 342 { 343 off_t base, wr; 344 char *buf, *rd; 345 int i; 346 347 pollute_eof_tail(_metadata, self); 348 base = self->eof & ~(self->page - 1); 349 wr = base + 3 * self->page / 4; /* starts in the EOF page */ 350 351 buf = malloc(2 * self->page); 352 ASSERT_NE(NULL, buf); 353 memset(buf, 'B', 2 * self->page); 354 ASSERT_EQ(2 * self->page, pwrite(self->fd, buf, 2 * self->page, wr)); 355 free(buf); 356 357 rd = malloc(self->page); 358 ASSERT_NE(NULL, rd); 359 ASSERT_EQ(self->page, pread(self->fd, rd, self->page, base)); 360 /* [eof, wr) is hole -> zero; [wr, page) is written data -> 'B'. */ 361 for (i = self->eof & (self->page - 1); i < wr - base; i++) 362 ASSERT_EQ(0, rd[i]); 363 for (i = wr - base; i < self->page; i++) 364 ASSERT_EQ('B', rd[i]); 365 free(rd); 366 } 367 368 TEST_HARNESS_MAIN 369