udebug: wait for response after buffer add/remove

Fixes a race condition where freeing a buffer and immediately re-allocating and
adding it would fail to pass the file descriptor to udebugd

Signed-off-by: Felix Fietkau <nbd@nbd.name>
This commit is contained in:
Felix Fietkau 2023-11-30 12:23:30 +01:00
parent e84c000c47
commit 40acbe3463
3 changed files with 90 additions and 81 deletions

View file

@ -25,9 +25,8 @@
#define UDEBUG_TIMEOUT 1000
__hidden int udebug_buf_open(struct udebug_buf *buf, int fd, uint32_t ring_size, uint32_t data_size);
__hidden struct udebug_client_msg *__udebug_poll(struct udebug *ctx, int *fd, bool wait);
__hidden void udebug_send_msg(struct udebug *ctx, struct udebug_client_msg *msg,
struct blob_attr *meta, int fd);
__hidden struct udebug_client_msg *
udebug_send_and_wait(struct udebug *ctx, struct udebug_client_msg *msg, int *rfd);
static inline int32_t u32_sub(uint32_t a, uint32_t b)
{

View file

@ -17,31 +17,6 @@
*/
#include "udebug-priv.h"
static struct udebug_client_msg *
send_and_wait(struct udebug *ctx, struct udebug_client_msg *msg, int *rfd)
{
int type = msg->type;
int fd = -1;
udebug_send_msg(ctx, msg, NULL, -1);
do {
if (fd >= 0)
close(fd);
fd = -1;
msg = __udebug_poll(ctx, &fd, true);
} while (msg && msg->type != type);
if (!msg)
return NULL;
if (rfd)
*rfd = fd;
else if (fd >= 0)
close(fd);
return msg;
}
static int
udebug_remote_get_handle(struct udebug *ctx)
{
@ -53,7 +28,7 @@ udebug_remote_get_handle(struct udebug *ctx)
if (ctx->poll_handle >= 0 || !udebug_is_connected(ctx))
return 0;
msg = send_and_wait(ctx, &send_msg, NULL);
msg = udebug_send_and_wait(ctx, &send_msg, NULL);
if (!msg)
return -1;
@ -82,7 +57,7 @@ int udebug_remote_buf_map(struct udebug *ctx, struct udebug_remote_buf *rb, uint
if (rb->buf.data || !udebug_is_connected(ctx))
return -1;
msg = send_and_wait(ctx, &send_msg, &fd);
msg = udebug_send_and_wait(ctx, &send_msg, &fd);
if (!msg || fd < 0)
return -1;

137
udebug.c
View file

@ -290,8 +290,9 @@ recv_retry(int fd, struct iovec *iov, bool wait, int *recv_fd)
return total;
}
void udebug_send_msg(struct udebug *ctx, struct udebug_client_msg *msg,
struct blob_attr *meta, int fd)
static void
udebug_send_msg(struct udebug *ctx, struct udebug_client_msg *msg,
struct blob_attr *meta, int fd)
{
struct iovec iov[2] = {
{ .iov_base = msg, .iov_len = sizeof(*msg) },
@ -308,6 +309,79 @@ void udebug_send_msg(struct udebug *ctx, struct udebug_client_msg *msg,
writev_retry(ctx->fd.fd, iov, ARRAY_SIZE(iov), fd);
}
static bool
udebug_recv_msg(struct udebug *ctx, struct udebug_client_msg *msg, int *fd,
bool wait)
{
struct iovec iov = {
.iov_base = msg,
.iov_len = sizeof(*msg)
};
int ret;
ret = recv_retry(ctx->fd.fd, &iov, wait, fd);
if (ret == -2)
__udebug_disconnect(ctx, true);
return ret == sizeof(*msg);
}
static struct udebug_client_msg *
__udebug_poll(struct udebug *ctx, int *fd, bool wait)
{
static struct udebug_client_msg msg = {};
while (udebug_recv_msg(ctx, &msg, fd, wait)) {
struct udebug_remote_buf *rb;
void *key;
if (msg.type != CL_MSG_RING_NOTIFY)
return &msg;
if (fd && *fd >= 0)
close(*fd);
if (!ctx->notify_cb)
continue;
key = (void *)(uintptr_t)msg.id;
rb = avl_find_element(&ctx->remote_rings, key, rb, node);
if (!rb || !rb->poll)
continue;
if (ctx->poll_handle >= 0)
__atomic_fetch_or(&rb->buf.hdr->notify,
1UL << ctx->poll_handle,
__ATOMIC_RELAXED);
ctx->notify_cb(ctx, rb);
}
return NULL;
}
static struct udebug_client_msg *
udebug_wait_for_response(struct udebug *ctx, struct udebug_client_msg *msg, int *rfd)
{
int type = msg->type;
int fd = -1;
do {
if (fd >= 0)
close(fd);
fd = -1;
msg = __udebug_poll(ctx, &fd, true);
} while (msg && msg->type != type);
if (!msg)
return NULL;
if (rfd)
*rfd = fd;
else if (fd >= 0)
close(fd);
return msg;
}
static void
udebug_buf_msg(struct udebug_buf *buf, enum udebug_client_msg_type type)
{
@ -317,6 +391,7 @@ udebug_buf_msg(struct udebug_buf *buf, enum udebug_client_msg_type type)
};
udebug_send_msg(buf->ctx, &msg, NULL, -1);
udebug_wait_for_response(buf->ctx, &msg, NULL);
}
static size_t __udebug_headsize(unsigned int ring_size, unsigned int page_size)
@ -604,6 +679,7 @@ __udebug_buf_add(struct udebug *ctx, struct udebug_buf *buf)
blobmsg_close_array(&b, c);
udebug_send_msg(ctx, &msg, b.head, buf->fd);
udebug_wait_for_response(ctx, &msg, NULL);
}
int udebug_buf_add(struct udebug *ctx, struct udebug_buf *buf,
@ -683,60 +759,19 @@ int udebug_connect(struct udebug *ctx, const char *path)
return 0;
}
static bool
udebug_recv_msg(struct udebug *ctx, struct udebug_client_msg *msg, int *fd,
bool wait)
{
struct iovec iov = {
.iov_base = msg,
.iov_len = sizeof(*msg)
};
int ret;
ret = recv_retry(ctx->fd.fd, &iov, wait, fd);
if (ret == -2)
__udebug_disconnect(ctx, true);
return ret == sizeof(*msg);
}
struct udebug_client_msg *__udebug_poll(struct udebug *ctx, int *fd, bool wait)
{
static struct udebug_client_msg msg = {};
while (udebug_recv_msg(ctx, &msg, fd, wait)) {
struct udebug_remote_buf *rb;
void *key;
if (msg.type != CL_MSG_RING_NOTIFY)
return &msg;
if (fd && *fd >= 0)
close(*fd);
if (!ctx->notify_cb)
continue;
key = (void *)(uintptr_t)msg.id;
rb = avl_find_element(&ctx->remote_rings, key, rb, node);
if (!rb || !rb->poll)
continue;
if (ctx->poll_handle >= 0)
__atomic_fetch_or(&rb->buf.hdr->notify,
1UL << ctx->poll_handle,
__ATOMIC_RELAXED);
ctx->notify_cb(ctx, rb);
}
return NULL;
}
void udebug_poll(struct udebug *ctx)
{
while (__udebug_poll(ctx, NULL, false));
}
struct udebug_client_msg *
udebug_send_and_wait(struct udebug *ctx, struct udebug_client_msg *msg, int *rfd)
{
udebug_send_msg(ctx, msg, NULL, -1);
return udebug_wait_for_response(ctx, msg, rfd);
}
static void udebug_fd_cb(struct uloop_fd *fd, unsigned int events)
{
struct udebug *ctx = container_of(fd, struct udebug, fd);