blob: d9e55156694884d8dc32cd3b8038ac2b849dcf73 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2014 Dmitry Monakhov. All Rights Reserved.
*
* Perform aio writes to file and toggle O_DIRECT flag concurrently
* this may trigger race between file->f_flags read and modification
* unuligned aio allow to makes race window wider.
* Regression test for https://lkml.org/lkml/2014/10/8/545 CVE-2014-8086
* Patch proposed: http://www.spinics.net/lists/linux-ext4/msg45683.html
*/
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <libaio.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
#define BUF_SIZE 512
#define LOOP_SECONDS 10
static int do_aio_loop(int fd, void *buf)
{
int err, ret;
struct io_context *ctx = NULL;
struct io_event ev;
struct iocb iocb, *iocbs[] = { &iocb };
struct timeval start, now, delta = { 0, 0 };
ret = 0;
err = io_setup(1, &ctx);
if (err) {
fprintf(stderr, "error %s during %s\n",
strerror(-err), "io_setup" );
return 1;
}
gettimeofday(&start, NULL);
while (1) {
io_prep_pwrite(&iocb, fd, buf, BUF_SIZE, BUF_SIZE);
err = io_submit(ctx, 1, iocbs);
if (err != 1) {
fprintf(stderr, "error %s during %s\n",
strerror(-err),
"io_submit");
ret = 1;
break;
}
err = io_getevents(ctx, 1, 1, &ev, NULL);
if (err != 1) {
fprintf(stderr, "error %s during %s\n",
strerror(-err),
"io_getevents");
ret = 1;
break;
}
gettimeofday(&now, NULL);
timersub(&now, &start, &delta);
if (delta.tv_sec >= LOOP_SECONDS)
break;
}
io_destroy(ctx);
return ret;
}
int main(int argc, char **argv)
{
int flags, fd;
int pid1, pid2 = 0;
int ret1, ret = 0;
if (argc != 2){
printf("Usage %s fname\n", argv[0]);
return 1;
}
fd = open(argv[1], O_CREAT | O_TRUNC | O_RDWR, 0600);
if (fd < 0) {
perror("open");
return 1;
}
pid1 = fork();
if (pid1 < 0) {
perror("fork");
return 1;
}
if (pid1 == 0) {
struct timeval start, now, delta = { 0, 0 };
gettimeofday(&start, NULL);
/* child: toggle O_DIRECT*/
flags = fcntl(fd, F_GETFL);
while (1) {
ret = fcntl(fd, F_SETFL, flags | O_DIRECT);
if (ret) {
perror("fcntl O_DIRECT");
return 1;
}
ret = fcntl(fd, F_SETFL, flags);
if (ret) {
perror("fcntl");
return 1;
}
gettimeofday(&now, NULL);
timersub(&now, &start, &delta);
if (delta.tv_sec >= LOOP_SECONDS)
break;
}
} else {
/* parent: AIO */
void *buf = NULL;
int err;
err = posix_memalign(&buf, BUF_SIZE, BUF_SIZE);
if (err || buf == NULL) {
fprintf(stderr, "posix_memalign failed: %s\n",
strerror(err));
exit(1);
}
/* Two tasks which performs unaligned aio will be serialized
which maks race window wider */
pid2 = fork();
if (pid2 < 0)
goto out;
else if (pid2 > 0)
printf("All tasks are spawned\n");
ret = do_aio_loop(fd, buf);
}
out:
/* Parent wait for all others */
if (pid2 > 0){
waitpid(pid1, &ret1, 0);
if (!ret)
ret = ret1;
waitpid(pid2, &ret1, 0);
} else {
waitpid(pid1, &ret1, 0);
}
if (!ret)
ret = ret1;
return ret;
}