summaryrefslogtreecommitdiff
path: root/src/t_mmap_cow_memory_failure.c
blob: bb3fd3fbdc914867d833a68df495d61380e06e98 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2022 Fujitsu Limited.  All Rights Reserved. */
#include <errno.h>
#include <fcntl.h>
#include <libgen.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <semaphore.h>
#include <sys/mman.h>
#include <sys/wait.h>
#include <sys/sem.h>
#include <time.h>
#include <unistd.h>

sem_t *sem;

void sigbus_handler(int signal)
{
	printf("Process is killed by signal: %d\n", signal);
	sem_post(sem);
}

void mmap_read_file(char *filename, off_t offset, size_t size)
{
	int fd;
	char *map, *dummy;
	struct timespec ts;

	fd = open(filename, O_RDWR);
	map = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, offset);
	dummy = malloc(size);

	/* make sure page fault happens */
	memcpy(dummy, map, size);

	/* ready */
	sem_post(sem);

	usleep(200000);

	clock_gettime(CLOCK_REALTIME, &ts);
	ts.tv_sec += 3;
	/* wait for injection done */
	sem_timedwait(sem, &ts);

	free(dummy);
	munmap(map, size);
	close(fd);
}

void mmap_read_file_then_poison(char *filename, off_t offset, size_t size,
		off_t poisonOffset, size_t poisonSize)
{
	int fd, error;
	char *map, *dummy;

	/* wait for parent preparation done */
	sem_wait(sem);

	fd = open(filename, O_RDWR);
	map = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, offset);
	dummy = malloc(size);

	/* make sure page fault happens */
	memcpy(dummy, map, size);

	printf("Inject poison...\n");
	error = madvise(map + poisonOffset, poisonSize, MADV_HWPOISON);
	if (error)
		printf("madvise() has fault: %d, errno: %d\n", error, errno);

	free(dummy);
	munmap(map, size);
	close(fd);
}

int main(int argc, char *argv[])
{
	char *pReadFile = NULL, *pPoisonFile = NULL;
	size_t mmapSize, poisonSize;
	off_t mmapOffset = 0, poisonOffset = 0;
	long pagesize = sysconf(_SC_PAGESIZE);
	int c;
	pid_t pid;

	if (pagesize < 1) {
		fprintf(stderr, "sysconf(_SC_PAGESIZE): failed to get page size\n");
		abort();
	}

	/* default mmap / poison size, in unit of System Page Size */
	mmapSize = poisonSize = pagesize;

	while ((c = getopt(argc, argv, "o::s::O::S::R:P:")) != -1) {
		switch (c) {
		/* mmap offset */
		case 'o':
			mmapOffset = atoi(optarg) * pagesize;
			break;
		/* mmap size */
		case 's':
			mmapSize = atoi(optarg) * pagesize;
			break;
		/* madvice offset */
		case 'O':
			poisonOffset = atoi(optarg) * pagesize;
			break;
		/* madvice size */
		case 'S':
			poisonSize = atoi(optarg) * pagesize;
			break;
		/* filename for mmap read */
		case 'R':
			pReadFile = optarg;
			break;
		/* filename for poison read */
		case 'P':
			pPoisonFile = optarg;
			break;
		default:
			printf("Unknown option: %c\n", c);
			exit(1);
		}
	}

	if (!pReadFile || !pPoisonFile) {
		printf("Usage: \n"
		       "  %s [-o mmapOffset] [-s mmapSize] [-O mmapOffset] [-S mmapSize] -R readFile -P poisonFile\n"
		       "  (offset and size are both in unit of System Page Size: %ld)\n",
				basename(argv[0]), pagesize);
		exit(0);
	}
	if (poisonSize < mmapSize)
		mmapSize = poisonSize;

	/* fork and mmap files */
	pid = fork();
	if (pid == 0) {
		/* handle SIGBUS */
		signal(SIGBUS, sigbus_handler);
		sem = sem_open("sync", O_CREAT, 0666, 0);

		/* mread & do memory failure on poison file */
		mmap_read_file_then_poison(pPoisonFile, mmapOffset, mmapSize,
				poisonOffset, poisonSize);

		sem_close(sem);
	} else {
		sem = sem_open("sync", O_CREAT, 0666, 0);

		/* mread read file, wait for child process to be killed */
		mmap_read_file(pReadFile, mmapOffset, mmapSize);
		sem_close(sem);
	}
	exit(0);
}