src/aio-dio-regress/: spdx license conversion
[xfstests-dev.git] / src / aio-dio-regress / aio-dio-fcntl-race.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (c) 2014 Dmitry Monakhov.  All Rights Reserved.
4  *
5  * Perform aio writes to file and toggle O_DIRECT flag concurrently
6  * this may trigger race between file->f_flags read and modification
7  * unuligned aio allow to makes race window wider.
8  * Regression test for https://lkml.org/lkml/2014/10/8/545 CVE-2014-8086
9  * Patch proposed: http://www.spinics.net/lists/linux-ext4/msg45683.html
10  */
11 #include <sys/stat.h>
12 #include <sys/types.h>
13 #include <errno.h>
14 #include <fcntl.h>
15 #include <unistd.h>
16 #include <libaio.h>
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <time.h>
20 #include <sys/time.h>
21 #include <sys/types.h>
22 #include <sys/wait.h>
23
24 #define BUF_SIZE        512
25 #define LOOP_SECONDS 10
26
27
28 static int do_aio_loop(int fd, void *buf)
29 {
30         int err, ret;
31         struct io_context *ctx = NULL;
32         struct io_event ev;
33         struct iocb iocb, *iocbs[] = { &iocb };
34         struct timeval start, now, delta = { 0, 0 };
35
36         ret = 0;
37         err = io_setup(1, &ctx);
38         if (err) {
39                 fprintf(stderr, "error %s during %s\n",
40                         strerror(-err), "io_setup" );
41                 return 1;
42         }
43         gettimeofday(&start, NULL);
44         while (1) {
45                 io_prep_pwrite(&iocb, fd, buf, BUF_SIZE, BUF_SIZE);
46                 err = io_submit(ctx, 1, iocbs);
47                 if (err != 1) {
48                         fprintf(stderr, "error %s during %s\n",
49                                 strerror(-err),
50                                 "io_submit");
51                         ret = 1;
52                         break;
53                 }
54                 err = io_getevents(ctx, 1, 1, &ev, NULL);
55                 if (err != 1) {
56                         fprintf(stderr, "error %s during %s\n",
57                                 strerror(-err),
58                                 "io_getevents");
59                         ret = 1;
60                         break;
61                 }
62                 gettimeofday(&now, NULL);
63                 timersub(&now, &start, &delta);
64                 if (delta.tv_sec >= LOOP_SECONDS)
65                         break;
66         }
67         io_destroy(ctx);
68         return ret;
69 }
70
71 int main(int argc, char **argv)
72 {
73         int flags, fd;
74         int pid1, pid2 = 0;
75         int ret1, ret = 0;
76
77         if (argc != 2){
78                 printf("Usage %s fname\n", argv[0]);
79                 return 1;
80         }
81         fd = open(argv[1], O_CREAT | O_TRUNC | O_RDWR, 0600);
82         if (fd < 0) {
83                 perror("open");
84                 return 1;
85         }
86
87         pid1 = fork();
88         if (pid1 < 0) {
89                 perror("fork");
90                 return 1;
91         }
92
93         if (pid1 == 0) {
94                 struct timeval start, now, delta = { 0, 0 };
95
96                 gettimeofday(&start, NULL);
97
98                 /* child: toggle O_DIRECT*/
99                 flags = fcntl(fd, F_GETFL);
100                 while (1) {
101                         ret = fcntl(fd, F_SETFL, flags | O_DIRECT);
102                         if (ret) {
103                                 perror("fcntl O_DIRECT");
104                                 return 1;
105                         }
106                         ret = fcntl(fd, F_SETFL, flags);
107                         if (ret) {
108                                 perror("fcntl");
109                                 return 1;
110                         }
111                         gettimeofday(&now, NULL);
112                         timersub(&now, &start, &delta);
113                         if (delta.tv_sec >= LOOP_SECONDS)
114                                 break;
115                 }
116         } else {
117                 /* parent: AIO */
118                 void *buf = NULL;
119                 int err;
120
121                 err = posix_memalign(&buf, BUF_SIZE, BUF_SIZE);
122                 if (err || buf == NULL) {
123                         fprintf(stderr, "posix_memalign failed: %s\n",
124                                 strerror(err));
125                         exit(1);
126                 }
127                 /* Two tasks which performs unaligned aio will be serialized
128                    which maks race window wider */
129                 pid2 = fork();
130                 if (pid2 < 0)
131                         goto out;
132                 else if (pid2 > 0)
133                         printf("All tasks are spawned\n");
134
135                 ret = do_aio_loop(fd, buf);
136         }
137 out:
138         /* Parent wait for all others */
139         if (pid2 > 0){
140                 waitpid(pid1, &ret1, 0);
141                 if (!ret)
142                         ret = ret1;
143                 waitpid(pid2, &ret1, 0);
144         } else {
145                 waitpid(pid1, &ret1, 0);
146         }
147         if (!ret)
148                 ret = ret1;
149
150         return ret;
151 }