1 /* 2 * Copyright (c) 2015 Fujitsu Ltd. 3 * Author: Xiaoguang Wang <wangxg.fnst (at) cn.fujitsu.com> 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See 13 * the GNU General Public License for more details. 14 */ 15 16 /* 17 * This is a regression test for commit: 18 * http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/ 19 * commit/?id=90a8020 20 * http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/ 21 * commit/?id=d6320cb 22 */ 23 24 #define _GNU_SOURCE 25 26 #include <stdio.h> 27 #include <errno.h> 28 #include <fcntl.h> 29 #include <sys/types.h> 30 #include <sys/wait.h> 31 #include <unistd.h> 32 #include <sys/mount.h> 33 #include <sys/mman.h> 34 35 #include "test.h" 36 #include "safe_macros.h" 37 38 #define MNTPOINT "mntpoint" 39 #define FS_BLOCKSIZE 1024 40 #define SUB_LOOPCOUNT 10 41 42 static void setup(void); 43 static void cleanup(void); 44 static void do_child(void); 45 static void do_test(void); 46 47 static const char *device; 48 static const char *fs_type = "ext4"; 49 static int mount_flag; 50 static int chdir_flag; 51 52 static int page_size; 53 static int bug_reproduced; 54 55 char *TCID = "mmap16"; 56 int TST_TOTAL = 1; 57 58 int main(int ac, char **av) 59 { 60 int i, lc; 61 62 tst_parse_opts(ac, av, NULL, NULL); 63 64 setup(); 65 66 /* 67 * If child process was killed by SIGBUS, indeed that can not guarantee 68 * this bug must have been fixed, If we're luckily enough, virtual 69 * memory subsystem happen to decide that it is time to write dirty 70 * pages, it will make mapped pages write-protect, so ->page_mkwrite() 71 * still will be called, child process will be killed by SIGBUS, but 72 * it's not because of above fixing patches. So here we run this test 73 * 10 times, if once, child process exits normally, we can sure that 74 * this bug is not fixed. 75 */ 76 for (lc = 0; TEST_LOOPING(lc); lc++) { 77 tst_count = 0; 78 79 for (i = 0; i < SUB_LOOPCOUNT; i++) 80 do_test(); 81 } 82 83 if (bug_reproduced) 84 tst_resm(TFAIL, "Bug is reproduced!"); 85 else 86 tst_resm(TPASS, "Bug is not reproduced!"); 87 88 cleanup(); 89 tst_exit(); 90 } 91 92 static void do_test(void) 93 { 94 int fd, ret, status; 95 pid_t child; 96 char buf[FS_BLOCKSIZE]; 97 98 SAFE_TOUCH(cleanup, "testfilep", 0644, NULL); 99 SAFE_TOUCH(cleanup, "testfilec", 0644, NULL); 100 101 child = tst_fork(); 102 switch (child) { 103 case -1: 104 tst_brkm(TBROK | TERRNO, cleanup, "fork failed"); 105 case 0: 106 do_child(); 107 default: 108 fd = SAFE_OPEN(cleanup, "testfilep", O_RDWR); 109 memset(buf, 'a', FS_BLOCKSIZE); 110 111 TST_SAFE_CHECKPOINT_WAIT(cleanup, 0); 112 while (1) { 113 ret = write(fd, buf, FS_BLOCKSIZE); 114 if (ret < 0) { 115 if (errno == ENOSPC) { 116 break; 117 } else { 118 tst_brkm(TBROK | TERRNO, cleanup, 119 "write failed unexpectedly"); 120 } 121 } 122 } 123 SAFE_CLOSE(cleanup, fd); 124 TST_SAFE_CHECKPOINT_WAKE(cleanup, 0); 125 } 126 127 wait(&status); 128 if (WIFEXITED(status) && WEXITSTATUS(status) == 1) { 129 bug_reproduced = 1; 130 } else { 131 /* 132 * If child process was killed by SIGBUS, bug is not reproduced. 133 */ 134 if (!WIFSIGNALED(status) || WTERMSIG(status) != SIGBUS) { 135 tst_brkm(TBROK | TERRNO, cleanup, 136 "child process terminate unexpectedly"); 137 } 138 } 139 140 SAFE_UNLINK(cleanup, "testfilep"); 141 SAFE_UNLINK(cleanup, "testfilec"); 142 } 143 144 static void setup(void) 145 { 146 const char *fs_opts[3] = {"-b", "1024", NULL}; 147 148 tst_sig(FORK, DEF_HANDLER, NULL); 149 tst_require_root(); 150 151 TEST_PAUSE; 152 tst_tmpdir(); 153 154 TST_CHECKPOINT_INIT(tst_rmdir); 155 156 page_size = getpagesize(); 157 158 device = tst_acquire_device(cleanup); 159 if (!device) 160 tst_brkm(TCONF, cleanup, "Failed to obtain block device"); 161 tst_mkfs(cleanup, device, fs_type, fs_opts, "10240"); 162 163 SAFE_MKDIR(cleanup, MNTPOINT, 0755); 164 /* 165 * Disable ext4 delalloc feature, so block will be allocated 166 * as soon as possible 167 */ 168 SAFE_MOUNT(cleanup, device, MNTPOINT, fs_type, 0, "nodelalloc"); 169 mount_flag = 1; 170 171 SAFE_CHDIR(cleanup, MNTPOINT); 172 chdir_flag = 1; 173 174 } 175 176 static void do_child(void) 177 { 178 int fd, offset; 179 char buf[FS_BLOCKSIZE]; 180 char *addr = NULL; 181 182 /* 183 * We have changed SIGBUS' handler in parent process by calling 184 * tst_sig(FORK, DEF_HANDLER, NULL), so here just restore it. 185 */ 186 if (signal(SIGBUS, SIG_DFL) == SIG_ERR) 187 tst_brkm(TBROK | TERRNO, NULL, "signal(SIGBUS) failed"); 188 189 memset(buf, 'a', FS_BLOCKSIZE); 190 fd = SAFE_OPEN(NULL, "testfilec", O_RDWR); 191 SAFE_PWRITE(NULL, 1, fd, buf, FS_BLOCKSIZE, 0); 192 193 /* 194 * In case mremap() may fail because that memory area can not be 195 * expanded at current virtual address(MREMAP_MAYMOVE is not set), 196 * we first do a mmap(page_size * 2) operation to reserve some 197 * free address space. 198 */ 199 addr = SAFE_MMAP(NULL, NULL, page_size * 2, PROT_WRITE | PROT_READ, 200 MAP_PRIVATE_EXCEPT_UCLINUX | MAP_ANONYMOUS, -1, 0); 201 SAFE_MUNMAP(NULL, addr, page_size * 2); 202 203 addr = SAFE_MMAP(NULL, addr, FS_BLOCKSIZE, PROT_WRITE | PROT_READ, 204 MAP_SHARED, fd, 0); 205 206 addr[0] = 'a'; 207 208 SAFE_FTRUNCATE(NULL, fd, page_size * 2); 209 addr = mremap(addr, FS_BLOCKSIZE, 2 * page_size, 0); 210 if (addr == MAP_FAILED) 211 tst_brkm(TBROK | TERRNO, NULL, "mremap failed unexpectedly"); 212 213 /* 214 * Let parent process consume FS free blocks as many as possible, then 215 * there'll be no free blocks allocated for this new file mmaping for 216 * offset starting at 1024, 2048, or 3072. If this above kernel bug 217 * has been fixed, usually child process will killed by SIGBUS signal, 218 * if not, the data 'A', 'B', 'C' will be silently discarded later when 219 * kernel writepage is called, that means data corruption. 220 */ 221 TST_SAFE_CHECKPOINT_WAKE(NULL, 0); 222 TST_SAFE_CHECKPOINT_WAIT(NULL, 0); 223 224 for (offset = FS_BLOCKSIZE; offset < page_size; offset += FS_BLOCKSIZE) 225 addr[offset] = 'a'; 226 227 SAFE_MUNMAP(NULL, addr, 2 * page_size); 228 SAFE_CLOSE(NULL, fd); 229 exit(TFAIL); 230 } 231 232 static void cleanup(void) 233 { 234 if (chdir_flag && chdir("..")) 235 tst_resm(TWARN | TERRNO, "chdir('..') failed"); 236 if (mount_flag && tst_umount(MNTPOINT) < 0) 237 tst_resm(TWARN | TERRNO, "umount device:%s failed", device); 238 if (device) 239 tst_release_device(device); 240 tst_rmdir(); 241 } 242