Created
May 11, 2012 06:55
-
-
Save coffeemug/2658017 to your computer and use it in GitHub Desktop.
Ubuntu 12.04 ext4 ftruncate deadlock
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* This program demonstrates a bug in the Linux kernel version 3.2.0 using the | |
`ext4` filesystem. | |
Steps to compile: | |
$ gcc -o demo demo.c -laio | |
Steps to reproduce: | |
1. Run `./demo FILE`, where `FILE` is the path where the program will put the | |
file it operates on. The path must point into an `ext4` filesystem. | |
2. The program opens the file with the `O_DIRECT` flag; calls `ftruncate()` on | |
it; submits a hundred write operations using `libaio`; and then calls | |
`ftruncate()` again. | |
3. The second call to `ftruncate()` does not return. The process does not | |
respond to signals, not even `SIGKILL`. Any other process that tries to | |
access the file also locks up and does not respond to `SIGKILL`. | |
*/ | |
#define _GNU_SOURCE | |
#include <unistd.h> | |
#include <sys/types.h> | |
#include <sys/stat.h> | |
#include <fcntl.h> | |
#include <stdio.h> | |
#include <libaio.h> | |
#include <stdlib.h> | |
#define FIRST_FTRUNCATE_SIZE (1024 * 1024 * 20) | |
#define BLOCK_SIZE 4096 | |
#define BLOCK_COUNT 100 | |
#define IO_QUEUE_DEPTH 64 | |
#define SECOND_FTRUNCATE_SIZE (1024 * 1024 * 40) | |
int main(int argc, char *argv[]) { | |
int res, fd, i; | |
void *buffer; | |
struct iocb **iocbs; | |
io_context_t ioctx; | |
if (argc != 2) { | |
fprintf(stderr, "usage: %s FILE\n", argv[0]); | |
exit(EXIT_FAILURE); | |
} | |
fd = open(argv[1], O_RDWR|O_CREAT|O_EXCL|O_DIRECT, 0644); | |
if (fd == -1) { | |
perror("open()"); | |
exit(EXIT_FAILURE); | |
} | |
res = ftruncate(fd, FIRST_FTRUNCATE_SIZE); | |
if (res != 0) { | |
perror("ftruncate()"); | |
exit(EXIT_FAILURE); | |
} | |
res = posix_memalign(&buffer, getpagesize(), BLOCK_SIZE); | |
if (res != 0) { | |
perror("posix_memalign()"); | |
exit(EXIT_FAILURE); | |
} | |
bzero(buffer, BLOCK_SIZE); | |
iocbs = malloc(sizeof(struct iocb *) * BLOCK_COUNT); | |
if (!iocbs) { | |
fprintf(stderr, "malloc() = NULL\n"); | |
exit(EXIT_FAILURE); | |
} | |
for (i = 0; i < BLOCK_COUNT; i++) { | |
iocbs[i] = malloc(sizeof(struct iocb)); | |
if (!iocbs[i]) { | |
fprintf(stderr, "malloc() = NULL\n"); | |
exit(EXIT_FAILURE); | |
} | |
bzero(iocbs[i], sizeof(struct iocb)); | |
iocbs[i]->aio_fildes = fd; | |
iocbs[i]->u.c.offset = i * BLOCK_SIZE; | |
iocbs[i]->u.c.buf = buffer; | |
iocbs[i]->u.c.nbytes = BLOCK_SIZE; | |
iocbs[i]->aio_lio_opcode = IO_CMD_PWRITE; | |
} | |
bzero(&ioctx, sizeof(io_context_t)); | |
res = io_setup(IO_QUEUE_DEPTH, &ioctx); | |
if (res != 0) { | |
fprintf(stderr, "io_setup(): %s\n", strerror(-res)); | |
exit(EXIT_FAILURE); | |
} | |
i = 0; | |
while (i < BLOCK_COUNT) { | |
res = io_submit(ioctx, BLOCK_COUNT - i, iocbs + i); | |
if (res < 0) { | |
fprintf(stderr, "io_submit(): %s\n", strerror(-res)); | |
exit(EXIT_FAILURE); | |
} | |
i += res; | |
} | |
fprintf(stderr, "This might lock up..\n"); | |
res = ftruncate(fd, SECOND_FTRUNCATE_SIZE); | |
if (res != 0) { | |
perror("ftruncate()"); | |
exit(EXIT_FAILURE); | |
} | |
fprintf(stderr, "It didn't lock up.\n"); | |
res = io_destroy(ioctx); | |
if (res != 0) { | |
fprintf(stderr, "io_destroy(): %s\n", strerror(-res)); | |
exit(EXIT_FAILURE); | |
} | |
for (i = 0; i < BLOCK_COUNT; i++) { | |
free(iocbs[i]); | |
} | |
free(iocbs); | |
free(buffer); | |
res = close(fd); | |
if (res != 0) { | |
perror("close()"); | |
exit(EXIT_FAILURE); | |
} | |
return EXIT_SUCCESS; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment