1 /*
2 * Test program for Linux memory error recovery.
3 * Requires special injection support.
4 *
5 * This is a early primitive version of tinjpage.c,
6 * but simpler to debug in some cases.
7 */
8 #define _GNU_SOURCE 1
9 #include <sys/mman.h>
10 #include <stdio.h>
11 #include <signal.h>
12 #include <unistd.h>
13 #include <sys/fcntl.h>
14 #include <stdlib.h>
15 #include <setjmp.h>
16 #include <errno.h>
17 #include <string.h>
18
19 #define MADV_POISON 100
20
21 #define err(x) perror(x),exit(1)
22
23 int count = 20;
24 int failure = 0;
25 int total_cases = 0;
26 sigjmp_buf recover;
27 int PS;
28
sighandler(int sig,siginfo_t * si,void * arg)29 void sighandler(int sig, siginfo_t *si, void *arg)
30 {
31 printf("signal %d code %d addr %p\n", sig, si->si_code, si->si_addr);
32
33 if (--count == 0)
34 exit(1);
35
36 siglongjmp(recover, 1);
37 }
38
testmem(char * msg,char * page,int write)39 void testmem(char *msg, char *page, int write)
40 {
41 printf("%s page %p\n", msg, page);
42 total_cases++;
43 if (sigsetjmp(recover,1) == 0) {
44 if (madvise(page, PS, MADV_POISON) != 0) {
45 failure++;
46 perror("madvise");
47 }
48 if (write)
49 *page = 2;
50 else
51 printf("%x\n", *(unsigned char *)page);
52 }
53 printf("recovered\n");
54 }
55
expecterr(char * msg,int res)56 void expecterr(char *msg, int res)
57 {
58 if (res == 0)
59 printf("no error on %s\n", msg);
60 else
61 perror(msg);
62 }
63
tempfd(void)64 int tempfd(void)
65 {
66 static int tmpcount;
67 int fd;
68 char buf[30];
69 snprintf(buf,30,"/tmp/test%d.XXXXXXXX",tmpcount++);
70 fd = mkstemp(buf);
71 if (fd >= 0)
72 unlink(buf);
73 return fd;
74 }
75
76 #define RANDOM_FILE "/etc/profile"
77
main(void)78 int main(void)
79 {
80 PS = getpagesize();
81 char *page;
82
83 struct sigaction sa = {
84 .sa_sigaction = sighandler,
85 .sa_flags = SA_SIGINFO
86 };
87 sigaction(SIGBUS, &sa, NULL);
88 // sigaction(SIGSEGV, &sa, NULL);
89
90 page = mmap(NULL, PS, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_POPULATE, 0, 0);
91 testmem("dirty", page, 1);
92
93 page = mmap(NULL, PS, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_LOCKED, 0, 0);
94 testmem("mlocked", page, 1);
95
96 int fd = open(RANDOM_FILE, O_RDONLY);
97 if (fd < 0) err("open " RANDOM_FILE);
98 page = mmap(NULL, PS, PROT_READ, MAP_SHARED|MAP_POPULATE, fd, 0);
99 if (page == (char *)-1) err("mmap");
100 close(fd);
101 testmem("clean file", page, 0);
102
103 fd = tempfd();
104 if (fd < 0) err("open testfile");
105 char *tmp = malloc(PS);
106 if (!tmp) err("no enough memory");
107 memset(tmp, 0xff, PS);
108 write(fd, tmp, PS);
109 free(tmp);
110 page = mmap(NULL, PS, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
111 if (page == (char*)-1) err("mmap");
112 *page = 1;
113 testmem("file dirty", page, 0);
114 expecterr("msync expect error", msync(page, PS, MS_SYNC));
115 expecterr("fsync expect error", fsync(fd));
116 close(fd);
117
118 /* hole case still broken in the kernel -- doesn't report error */
119 fd = tempfd();
120 if (fd < 0) err("open testfile");
121 ftruncate(fd, PS);
122 page = mmap(NULL, PS, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
123 if (page == (char*)-1) err("mmap");
124 *page = 1;
125 testmem("hole file dirty", page, 0);
126 expecterr("hole msync expect error", msync(page, PS, MS_SYNC));
127 expecterr("hole fsync expect error", fsync(fd));
128 close(fd);
129
130 #if 0
131 const int NPAGES = 10;
132 int i;
133 fd = tempfd();
134 if (fd < 0) err("open rfp testfile");
135 tmp = malloc(PS);
136 if (!tmp) exit(ENOMEM);
137 for (i = 0; i < NPAGES; i++) {
138 memset(tmp, i, PS);
139 write(fd, tmp, PS);
140 }
141 free(tmp);
142 page = mmap(NULL, PS*NPAGES, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
143 if (page == (char*)-1) err("mmap");
144 int k = NPAGES - 1;
145 for (i = 0; i < NPAGES; i++, k--) {
146 if (remap_file_pages(page + i*PS, PS, 0, k, 0))
147 perror("remap_file_pages");
148 }
149 *page = 1;
150 testmem("rfp file dirty", page, 0);
151 expecterr("rfp msync expect error", msync(page, PS, MS_SYNC));
152 expecterr("rfp fsync expect error", fsync(fd));
153 close(fd);
154 #endif
155
156 if (failure > 0) {
157 printf("FAILURE -- %d of %d cases broken!\n", failure, total_cases);
158 return 1;
159 }
160 printf("SUCCESS\n");
161
162 return 0;
163 }
164
165
166