There's a failure case where an application gets a cqe entry, but
the kernel can then overwrite it before the application is done
reading it. This can happen since the io_uring_{get,wait}_completion()
interface both returns a CQE pointer AND increments the ring index.
If the kernel reuses this entry before the applications is done reading
it, the contents may be corrupted.
Remove the CQ head increment from the CQE retrieval, and put it into
a separate helper, io_uring_cqe_seen(). The application must call this
helper when it got a new CQE entry through one of the above calls, and
it's now done reading it.
Signed-off-by: Jens Axboe <axboe@kernel.dk>
120 lines
2.2 KiB
C
120 lines
2.2 KiB
C
/*
|
|
* Description: test io_uring poll cancel handling
|
|
*
|
|
*/
|
|
#include <errno.h>
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <inttypes.h>
|
|
#include <sys/poll.h>
|
|
#include <sys/wait.h>
|
|
#include <sys/signal.h>
|
|
|
|
#include "../src/liburing.h"
|
|
|
|
struct poll_data {
|
|
unsigned is_poll;
|
|
unsigned is_cancel;
|
|
};
|
|
|
|
static void sig_alrm(int sig)
|
|
{
|
|
printf("Timed out!\n");
|
|
exit(1);
|
|
}
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
struct io_uring ring;
|
|
int pipe1[2];
|
|
struct io_uring_cqe *cqe;
|
|
struct io_uring_sqe *sqe;
|
|
struct poll_data *pd, pds[2];
|
|
struct sigaction act;
|
|
int ret;
|
|
|
|
if (pipe(pipe1) != 0) {
|
|
printf("pipe failed\n");
|
|
return 1;
|
|
}
|
|
|
|
ret = io_uring_queue_init(2, &ring, 0);
|
|
if (ret) {
|
|
printf("child: ring setup failed\n");
|
|
return 1;
|
|
}
|
|
|
|
memset(&act, 0, sizeof(act));
|
|
act.sa_handler = sig_alrm;
|
|
act.sa_flags = SA_RESTART;
|
|
sigaction(SIGALRM, &act, NULL);
|
|
alarm(1);
|
|
|
|
sqe = io_uring_get_sqe(&ring);
|
|
if (!sqe) {
|
|
printf("child: get sqe failed\n");
|
|
return 1;
|
|
}
|
|
|
|
io_uring_prep_poll_add(sqe, pipe1[0], POLLIN);
|
|
|
|
pds[0].is_poll = 1;
|
|
pds[0].is_cancel = 0;
|
|
io_uring_sqe_set_data(sqe, &pds[0]);
|
|
|
|
ret = io_uring_submit(&ring);
|
|
if (ret <= 0) {
|
|
printf("child: sqe submit failed\n");
|
|
return 1;
|
|
}
|
|
|
|
sqe = io_uring_get_sqe(&ring);
|
|
if (!sqe) {
|
|
printf("child: get sqe failed\n");
|
|
return 1;
|
|
}
|
|
|
|
pds[1].is_poll = 0;
|
|
pds[1].is_cancel = 0;
|
|
io_uring_prep_poll_remove(sqe, &pds[0]);
|
|
io_uring_sqe_set_data(sqe, &pds[1]);
|
|
|
|
ret = io_uring_submit(&ring);
|
|
if (ret <= 0) {
|
|
printf("child: sqe submit failed\n");
|
|
return 1;
|
|
}
|
|
|
|
ret = io_uring_wait_completion(&ring, &cqe);
|
|
if (ret < 0) {
|
|
printf("child: get cqe failed\n");
|
|
return 1;
|
|
}
|
|
|
|
pd = io_uring_cqe_get_data(cqe);
|
|
if (cqe->res != 0) {
|
|
printf("sqe (add=%d/remove=%d) failed with %ld\n", pd->is_poll,
|
|
pd->is_cancel, (long) cqe->res);
|
|
return 1;
|
|
}
|
|
io_uring_cqe_seen(&ring, cqe);
|
|
|
|
ret = io_uring_wait_completion(&ring, &cqe);
|
|
if (ret < 0) {
|
|
printf("parent: get failed\n");
|
|
return 1;
|
|
}
|
|
|
|
pd = io_uring_cqe_get_data(cqe);
|
|
if (cqe->res != 0) {
|
|
printf("sqe (add=%d/remove=%d) failed with %ld\n", pd->is_poll,
|
|
pd->is_cancel, (long) cqe->res);
|
|
return 1;
|
|
}
|
|
|
|
io_uring_cqe_seen(&ring, cqe);
|
|
return 0;
|
|
}
|