1f08c3bdfSopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 2f08c3bdfSopenharmony_ci/* 3f08c3bdfSopenharmony_ci * Copyright (c) 2015 Fujitsu Ltd. Xiaoguang Wang <wangxg.fnst@cn.fujitsu.com> 4f08c3bdfSopenharmony_ci * Copyright (C) 2021 SUSE LLC Andrea Cervesato <andrea.cervesato@suse.com> 5f08c3bdfSopenharmony_ci */ 6f08c3bdfSopenharmony_ci 7f08c3bdfSopenharmony_ci/*\ 8f08c3bdfSopenharmony_ci * [Description] 9f08c3bdfSopenharmony_ci * 10f08c3bdfSopenharmony_ci * This is a regression test for a silent data corruption for a mmaped file 11f08c3bdfSopenharmony_ci * when filesystem gets out of space. 12f08c3bdfSopenharmony_ci * 13f08c3bdfSopenharmony_ci * Fixed by commits: 14f08c3bdfSopenharmony_ci * 15f08c3bdfSopenharmony_ci * commit 0572639ff66dcffe62d37adfe4c4576f9fc398f4 16f08c3bdfSopenharmony_ci * Author: Xiaoguang Wang <wangxg.fnst@cn.fujitsu.com> 17f08c3bdfSopenharmony_ci * Date: Thu Feb 12 23:00:17 2015 -0500 18f08c3bdfSopenharmony_ci * 19f08c3bdfSopenharmony_ci * ext4: fix mmap data corruption in nodelalloc mode when blocksize < pagesize 20f08c3bdfSopenharmony_ci * 21f08c3bdfSopenharmony_ci * commit d6320cbfc92910a3e5f10c42d98c231c98db4f60 22f08c3bdfSopenharmony_ci * Author: Jan Kara <jack@suse.cz> 23f08c3bdfSopenharmony_ci * Date: Wed Oct 1 21:49:46 2014 -0400 24f08c3bdfSopenharmony_ci * 25f08c3bdfSopenharmony_ci * ext4: fix mmap data corruption when blocksize < pagesize 26f08c3bdfSopenharmony_ci */ 27f08c3bdfSopenharmony_ci 28f08c3bdfSopenharmony_ci#define _GNU_SOURCE 29f08c3bdfSopenharmony_ci 30f08c3bdfSopenharmony_ci#include <stdlib.h> 31f08c3bdfSopenharmony_ci#include <signal.h> 32f08c3bdfSopenharmony_ci#include <time.h> 33f08c3bdfSopenharmony_ci#include <sys/mman.h> 34f08c3bdfSopenharmony_ci#include <sys/wait.h> 35f08c3bdfSopenharmony_ci#include "tst_test.h" 36f08c3bdfSopenharmony_ci 37f08c3bdfSopenharmony_ci#define MNTPOINT "mntpoint" 38f08c3bdfSopenharmony_ci#define FILE_PARENT "mntpoint/testfilep" 39f08c3bdfSopenharmony_ci#define FILE_CHILD "mntpoint/testfilec" 40f08c3bdfSopenharmony_ci#define FS_BLOCKSIZE 1024 41f08c3bdfSopenharmony_ci#define LOOPS 10 42f08c3bdfSopenharmony_ci 43f08c3bdfSopenharmony_cistatic int parentfd = -1; 44f08c3bdfSopenharmony_cistatic int childfd = -1; 45f08c3bdfSopenharmony_ci 46f08c3bdfSopenharmony_cistatic void do_child(void) 47f08c3bdfSopenharmony_ci{ 48f08c3bdfSopenharmony_ci int offset; 49f08c3bdfSopenharmony_ci int page_size; 50f08c3bdfSopenharmony_ci char buf[FS_BLOCKSIZE]; 51f08c3bdfSopenharmony_ci char *addr = NULL; 52f08c3bdfSopenharmony_ci 53f08c3bdfSopenharmony_ci page_size = getpagesize(); 54f08c3bdfSopenharmony_ci 55f08c3bdfSopenharmony_ci childfd = SAFE_OPEN(FILE_CHILD, O_RDWR | O_CREAT, 0666); 56f08c3bdfSopenharmony_ci 57f08c3bdfSopenharmony_ci memset(buf, 'a', FS_BLOCKSIZE); 58f08c3bdfSopenharmony_ci SAFE_WRITE(SAFE_WRITE_ALL, childfd, buf, FS_BLOCKSIZE); 59f08c3bdfSopenharmony_ci 60f08c3bdfSopenharmony_ci /* 61f08c3bdfSopenharmony_ci * In case mremap() may fail because that memory area can not be 62f08c3bdfSopenharmony_ci * expanded at current virtual address(MREMAP_MAYMOVE is not set), 63f08c3bdfSopenharmony_ci * we first do a mmap(page_size * 2) operation to reserve some 64f08c3bdfSopenharmony_ci * free address space. 65f08c3bdfSopenharmony_ci */ 66f08c3bdfSopenharmony_ci addr = SAFE_MMAP(NULL, page_size * 2, PROT_WRITE | PROT_READ, 67f08c3bdfSopenharmony_ci MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 68f08c3bdfSopenharmony_ci SAFE_MUNMAP(addr, page_size * 2); 69f08c3bdfSopenharmony_ci 70f08c3bdfSopenharmony_ci addr = SAFE_MMAP(addr, FS_BLOCKSIZE, PROT_WRITE | PROT_READ, MAP_SHARED, childfd, 0); 71f08c3bdfSopenharmony_ci 72f08c3bdfSopenharmony_ci addr[0] = 'a'; 73f08c3bdfSopenharmony_ci 74f08c3bdfSopenharmony_ci SAFE_FTRUNCATE(childfd, page_size * 2); 75f08c3bdfSopenharmony_ci 76f08c3bdfSopenharmony_ci addr = mremap(addr, FS_BLOCKSIZE, 2 * page_size, 0); 77f08c3bdfSopenharmony_ci if (addr == MAP_FAILED) 78f08c3bdfSopenharmony_ci tst_brk(TBROK | TERRNO, "mremap failed unexpectedly"); 79f08c3bdfSopenharmony_ci 80f08c3bdfSopenharmony_ci /* 81f08c3bdfSopenharmony_ci * Let parent process consume FS free blocks as many as possible, then 82f08c3bdfSopenharmony_ci * there'll be no free blocks allocated for this new file mmaping for 83f08c3bdfSopenharmony_ci * offset starting at 1024, 2048, or 3072. If this above kernel bug 84f08c3bdfSopenharmony_ci * has been fixed, usually child process will killed by SIGBUS signal, 85f08c3bdfSopenharmony_ci * if not, the data 'A', 'B', 'C' will be silently discarded later when 86f08c3bdfSopenharmony_ci * kernel writepage is called, that means data corruption. 87f08c3bdfSopenharmony_ci */ 88f08c3bdfSopenharmony_ci TST_CHECKPOINT_WAKE_AND_WAIT(0); 89f08c3bdfSopenharmony_ci 90f08c3bdfSopenharmony_ci for (offset = FS_BLOCKSIZE; offset < page_size; offset += FS_BLOCKSIZE) 91f08c3bdfSopenharmony_ci addr[offset] = 'a'; 92f08c3bdfSopenharmony_ci 93f08c3bdfSopenharmony_ci SAFE_MUNMAP(addr, 2 * page_size); 94f08c3bdfSopenharmony_ci SAFE_CLOSE(childfd); 95f08c3bdfSopenharmony_ci 96f08c3bdfSopenharmony_ci exit(1); 97f08c3bdfSopenharmony_ci} 98f08c3bdfSopenharmony_ci 99f08c3bdfSopenharmony_cistatic void run_single(void) 100f08c3bdfSopenharmony_ci{ 101f08c3bdfSopenharmony_ci int ret, status; 102f08c3bdfSopenharmony_ci pid_t child; 103f08c3bdfSopenharmony_ci char buf[FS_BLOCKSIZE]; 104f08c3bdfSopenharmony_ci int bug_reproduced = 0; 105f08c3bdfSopenharmony_ci 106f08c3bdfSopenharmony_ci child = SAFE_FORK(); 107f08c3bdfSopenharmony_ci if (!child) { 108f08c3bdfSopenharmony_ci do_child(); 109f08c3bdfSopenharmony_ci return; 110f08c3bdfSopenharmony_ci } 111f08c3bdfSopenharmony_ci 112f08c3bdfSopenharmony_ci parentfd = SAFE_OPEN(FILE_PARENT, O_RDWR | O_CREAT, 0666); 113f08c3bdfSopenharmony_ci 114f08c3bdfSopenharmony_ci memset(buf, 'a', FS_BLOCKSIZE); 115f08c3bdfSopenharmony_ci 116f08c3bdfSopenharmony_ci TST_CHECKPOINT_WAIT(0); 117f08c3bdfSopenharmony_ci 118f08c3bdfSopenharmony_ci while (1) { 119f08c3bdfSopenharmony_ci ret = write(parentfd, buf, FS_BLOCKSIZE); 120f08c3bdfSopenharmony_ci if (ret < 0) { 121f08c3bdfSopenharmony_ci if (errno == ENOSPC) 122f08c3bdfSopenharmony_ci break; 123f08c3bdfSopenharmony_ci 124f08c3bdfSopenharmony_ci tst_brk(TBROK | TERRNO, "write failed unexpectedly"); 125f08c3bdfSopenharmony_ci } 126f08c3bdfSopenharmony_ci } 127f08c3bdfSopenharmony_ci 128f08c3bdfSopenharmony_ci SAFE_CLOSE(parentfd); 129f08c3bdfSopenharmony_ci 130f08c3bdfSopenharmony_ci TST_CHECKPOINT_WAKE(0); 131f08c3bdfSopenharmony_ci 132f08c3bdfSopenharmony_ci SAFE_WAITPID(child, &status, 0); 133f08c3bdfSopenharmony_ci if (WIFEXITED(status) && WEXITSTATUS(status) == 1) { 134f08c3bdfSopenharmony_ci bug_reproduced = 1; 135f08c3bdfSopenharmony_ci } else { 136f08c3bdfSopenharmony_ci /* 137f08c3bdfSopenharmony_ci * If child process was killed by SIGBUS, bug is not reproduced. 138f08c3bdfSopenharmony_ci */ 139f08c3bdfSopenharmony_ci if (!WIFSIGNALED(status) || WTERMSIG(status) != SIGBUS) { 140f08c3bdfSopenharmony_ci tst_brk(TBROK | TERRNO, "child process terminate unexpectedly with status %s", 141f08c3bdfSopenharmony_ci tst_strstatus(status)); 142f08c3bdfSopenharmony_ci } 143f08c3bdfSopenharmony_ci } 144f08c3bdfSopenharmony_ci 145f08c3bdfSopenharmony_ci SAFE_UNLINK(FILE_PARENT); 146f08c3bdfSopenharmony_ci SAFE_UNLINK(FILE_CHILD); 147f08c3bdfSopenharmony_ci 148f08c3bdfSopenharmony_ci if (bug_reproduced) 149f08c3bdfSopenharmony_ci tst_res(TFAIL, "bug is reproduced"); 150f08c3bdfSopenharmony_ci else 151f08c3bdfSopenharmony_ci tst_res(TPASS, "bug is not reproduced"); 152f08c3bdfSopenharmony_ci} 153f08c3bdfSopenharmony_ci 154f08c3bdfSopenharmony_cistatic void run(void) 155f08c3bdfSopenharmony_ci{ 156f08c3bdfSopenharmony_ci int i; 157f08c3bdfSopenharmony_ci 158f08c3bdfSopenharmony_ci for (i = 0; i < LOOPS; i++) 159f08c3bdfSopenharmony_ci run_single(); 160f08c3bdfSopenharmony_ci} 161f08c3bdfSopenharmony_ci 162f08c3bdfSopenharmony_cistatic void cleanup(void) 163f08c3bdfSopenharmony_ci{ 164f08c3bdfSopenharmony_ci if (childfd >= 0) 165f08c3bdfSopenharmony_ci SAFE_CLOSE(childfd); 166f08c3bdfSopenharmony_ci 167f08c3bdfSopenharmony_ci if (parentfd >= 0) 168f08c3bdfSopenharmony_ci SAFE_CLOSE(parentfd); 169f08c3bdfSopenharmony_ci} 170f08c3bdfSopenharmony_ci 171f08c3bdfSopenharmony_cistatic struct tst_test test = { 172f08c3bdfSopenharmony_ci .test_all = run, 173f08c3bdfSopenharmony_ci .cleanup = cleanup, 174f08c3bdfSopenharmony_ci .forks_child = 1, 175f08c3bdfSopenharmony_ci .needs_root = 1, 176f08c3bdfSopenharmony_ci .needs_checkpoints = 1, 177f08c3bdfSopenharmony_ci .mount_device = 1, 178f08c3bdfSopenharmony_ci .mntpoint = MNTPOINT, 179f08c3bdfSopenharmony_ci .dev_fs_type = "ext4", 180f08c3bdfSopenharmony_ci .dev_fs_opts = (const char *const[]){ 181f08c3bdfSopenharmony_ci "-b", 182f08c3bdfSopenharmony_ci "1024", 183f08c3bdfSopenharmony_ci NULL, 184f08c3bdfSopenharmony_ci }, 185f08c3bdfSopenharmony_ci .dev_extra_opts = (const char *const[]){ 186f08c3bdfSopenharmony_ci "10240", 187f08c3bdfSopenharmony_ci NULL, 188f08c3bdfSopenharmony_ci }, 189f08c3bdfSopenharmony_ci .needs_cmds = (const char *const[]){ 190f08c3bdfSopenharmony_ci "mkfs.ext4", 191f08c3bdfSopenharmony_ci NULL, 192f08c3bdfSopenharmony_ci }, 193f08c3bdfSopenharmony_ci .tags = (const struct tst_tag[]){ 194f08c3bdfSopenharmony_ci {"linux-git", "d6320cbfc929"}, 195f08c3bdfSopenharmony_ci {"linux-git", "0572639ff66d"}, 196f08c3bdfSopenharmony_ci {}, 197f08c3bdfSopenharmony_ci }, 198f08c3bdfSopenharmony_ci}; 199