unplugged-system/external/ltp/testcases/kernel/syscalls/mmap/mmap16.c

199 lines
4.4 KiB
C

// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) 2015 Fujitsu Ltd. Xiaoguang Wang <wangxg.fnst@cn.fujitsu.com>
* Copyright (C) 2021 SUSE LLC Andrea Cervesato <andrea.cervesato@suse.com>
*/
/*\
* [Description]
*
* This is a regression test for a silent data corruption for a mmaped file
* when filesystem gets out of space.
*
* Fixed by commits:
*
* commit 0572639ff66dcffe62d37adfe4c4576f9fc398f4
* Author: Xiaoguang Wang <wangxg.fnst@cn.fujitsu.com>
* Date: Thu Feb 12 23:00:17 2015 -0500
*
* ext4: fix mmap data corruption in nodelalloc mode when blocksize < pagesize
*
* commit d6320cbfc92910a3e5f10c42d98c231c98db4f60
* Author: Jan Kara <jack@suse.cz>
* Date: Wed Oct 1 21:49:46 2014 -0400
*
* ext4: fix mmap data corruption when blocksize < pagesize
*/
#define _GNU_SOURCE
#include <stdlib.h>
#include <signal.h>
#include <time.h>
#include <sys/mman.h>
#include <sys/wait.h>
#include "tst_test.h"
#define MNTPOINT "mntpoint"
#define FILE_PARENT "mntpoint/testfilep"
#define FILE_CHILD "mntpoint/testfilec"
#define FS_BLOCKSIZE 1024
#define LOOPS 10
static int parentfd = -1;
static int childfd = -1;
static void do_child(void)
{
int offset;
int page_size;
char buf[FS_BLOCKSIZE];
char *addr = NULL;
page_size = getpagesize();
childfd = SAFE_OPEN(FILE_CHILD, O_RDWR | O_CREAT, 0666);
memset(buf, 'a', FS_BLOCKSIZE);
SAFE_WRITE(SAFE_WRITE_ALL, childfd, buf, FS_BLOCKSIZE);
/*
* In case mremap() may fail because that memory area can not be
* expanded at current virtual address(MREMAP_MAYMOVE is not set),
* we first do a mmap(page_size * 2) operation to reserve some
* free address space.
*/
addr = SAFE_MMAP(NULL, page_size * 2, PROT_WRITE | PROT_READ,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
SAFE_MUNMAP(addr, page_size * 2);
addr = SAFE_MMAP(addr, FS_BLOCKSIZE, PROT_WRITE | PROT_READ, MAP_SHARED, childfd, 0);
addr[0] = 'a';
SAFE_FTRUNCATE(childfd, page_size * 2);
addr = mremap(addr, FS_BLOCKSIZE, 2 * page_size, 0);
if (addr == MAP_FAILED)
tst_brk(TBROK | TERRNO, "mremap failed unexpectedly");
/*
* Let parent process consume FS free blocks as many as possible, then
* there'll be no free blocks allocated for this new file mmaping for
* offset starting at 1024, 2048, or 3072. If this above kernel bug
* has been fixed, usually child process will killed by SIGBUS signal,
* if not, the data 'A', 'B', 'C' will be silently discarded later when
* kernel writepage is called, that means data corruption.
*/
TST_CHECKPOINT_WAKE_AND_WAIT(0);
for (offset = FS_BLOCKSIZE; offset < page_size; offset += FS_BLOCKSIZE)
addr[offset] = 'a';
SAFE_MUNMAP(addr, 2 * page_size);
SAFE_CLOSE(childfd);
exit(1);
}
static void run_single(void)
{
int ret, status;
pid_t child;
char buf[FS_BLOCKSIZE];
int bug_reproduced = 0;
child = SAFE_FORK();
if (!child) {
do_child();
return;
}
parentfd = SAFE_OPEN(FILE_PARENT, O_RDWR | O_CREAT, 0666);
memset(buf, 'a', FS_BLOCKSIZE);
TST_CHECKPOINT_WAIT(0);
while (1) {
ret = write(parentfd, buf, FS_BLOCKSIZE);
if (ret < 0) {
if (errno == ENOSPC)
break;
tst_brk(TBROK | TERRNO, "write failed unexpectedly");
}
}
SAFE_CLOSE(parentfd);
TST_CHECKPOINT_WAKE(0);
SAFE_WAITPID(child, &status, 0);
if (WIFEXITED(status) && WEXITSTATUS(status) == 1) {
bug_reproduced = 1;
} else {
/*
* If child process was killed by SIGBUS, bug is not reproduced.
*/
if (!WIFSIGNALED(status) || WTERMSIG(status) != SIGBUS) {
tst_brk(TBROK | TERRNO, "child process terminate unexpectedly with status %s",
tst_strstatus(status));
}
}
SAFE_UNLINK(FILE_PARENT);
SAFE_UNLINK(FILE_CHILD);
if (bug_reproduced)
tst_res(TFAIL, "bug is reproduced");
else
tst_res(TPASS, "bug is not reproduced");
}
static void run(void)
{
int i;
for (i = 0; i < LOOPS; i++)
run_single();
}
static void cleanup(void)
{
if (childfd >= 0)
SAFE_CLOSE(childfd);
if (parentfd >= 0)
SAFE_CLOSE(parentfd);
}
static struct tst_test test = {
.test_all = run,
.cleanup = cleanup,
.forks_child = 1,
.needs_root = 1,
.needs_checkpoints = 1,
.mount_device = 1,
.mntpoint = MNTPOINT,
.dev_fs_type = "ext4",
.dev_fs_opts = (const char *const[]){
"-b",
"1024",
NULL,
},
.dev_extra_opts = (const char *const[]){
"10240",
NULL,
},
.needs_cmds = (const char *const[]){
"mkfs.ext4",
NULL,
},
.tags = (const struct tst_tag[]){
{"linux-git", "d6320cbfc929"},
{"linux-git", "0572639ff66d"},
{},
},
};