mirror of
https://codeberg.org/dnkl/foot.git
synced 2026-04-04 07:15:29 -04:00
selection: add a 2 second timeout when receiving clipboard data
When reading clipboard data, a malicious clipboard provider could stall us forever, by not sending any data, and not closing the pipe. This commit adds a timer_fd based timeout of 2 seconds. If the timer triggers, we abort the clipboard receive.
This commit is contained in:
parent
777a2eac51
commit
81222dac57
1 changed files with 78 additions and 11 deletions
89
selection.c
89
selection.c
|
|
@ -8,6 +8,7 @@
|
||||||
#include <wctype.h>
|
#include <wctype.h>
|
||||||
|
|
||||||
#include <sys/epoll.h>
|
#include <sys/epoll.h>
|
||||||
|
#include <sys/timerfd.h>
|
||||||
|
|
||||||
#define LOG_MODULE "selection"
|
#define LOG_MODULE "selection"
|
||||||
#define LOG_ENABLE_DBG 0
|
#define LOG_ENABLE_DBG 0
|
||||||
|
|
@ -993,12 +994,50 @@ selection_to_clipboard(struct seat *seat, struct terminal *term, uint32_t serial
|
||||||
}
|
}
|
||||||
|
|
||||||
struct clipboard_receive {
|
struct clipboard_receive {
|
||||||
|
int read_fd;
|
||||||
|
int timeout_fd;
|
||||||
|
struct itimerspec timeout;
|
||||||
|
|
||||||
/* Callback data */
|
/* Callback data */
|
||||||
void (*cb)(const char *data, size_t size, void *user);
|
void (*cb)(const char *data, size_t size, void *user);
|
||||||
void (*done)(void *user);
|
void (*done)(void *user);
|
||||||
void *user;
|
void *user;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void
|
||||||
|
clipboard_receive_done(struct fdm *fdm, struct clipboard_receive *ctx)
|
||||||
|
{
|
||||||
|
fdm_del(fdm, ctx->timeout_fd);
|
||||||
|
fdm_del(fdm, ctx->read_fd);
|
||||||
|
ctx->done(ctx->user);
|
||||||
|
free(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
fdm_receive_timeout(struct fdm *fdm, int fd, int events, void *data)
|
||||||
|
{
|
||||||
|
struct clipboard_receive *ctx = data;
|
||||||
|
if (events & EPOLLHUP)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
assert(events & EPOLLIN);
|
||||||
|
|
||||||
|
uint64_t expire_count;
|
||||||
|
ssize_t ret = read(fd, &expire_count, sizeof(expire_count));
|
||||||
|
if (ret < 0) {
|
||||||
|
if (errno == EAGAIN)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
LOG_ERRNO("failed to read clipboard timeout timer");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_WARN("no data received from clipboard in %llu seconds, aborting",
|
||||||
|
(unsigned long long)ctx->timeout.it_value.tv_sec);
|
||||||
|
|
||||||
|
clipboard_receive_done(fdm, ctx);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
fdm_receive(struct fdm *fdm, int fd, int events, void *data)
|
fdm_receive(struct fdm *fdm, int fd, int events, void *data)
|
||||||
|
|
@ -1008,6 +1047,12 @@ fdm_receive(struct fdm *fdm, int fd, int events, void *data)
|
||||||
if ((events & EPOLLHUP) && !(events & EPOLLIN))
|
if ((events & EPOLLHUP) && !(events & EPOLLIN))
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
|
/* Reset timeout timer */
|
||||||
|
if (timerfd_settime(ctx->timeout_fd, 0, &ctx->timeout, NULL) < 0) {
|
||||||
|
LOG_ERRNO("failed to re-arm clipboard timeout timer");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/* Read until EOF */
|
/* Read until EOF */
|
||||||
while (true) {
|
while (true) {
|
||||||
char text[256];
|
char text[256];
|
||||||
|
|
@ -1044,9 +1089,7 @@ fdm_receive(struct fdm *fdm, int fd, int events, void *data)
|
||||||
}
|
}
|
||||||
|
|
||||||
done:
|
done:
|
||||||
fdm_del(fdm, fd);
|
clipboard_receive_done(fdm, ctx);
|
||||||
ctx->done(ctx->user);
|
|
||||||
free(ctx);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1055,28 +1098,52 @@ begin_receive_clipboard(struct terminal *term, int read_fd,
|
||||||
void (*cb)(const char *data, size_t size, void *user),
|
void (*cb)(const char *data, size_t size, void *user),
|
||||||
void (*done)(void *user), void *user)
|
void (*done)(void *user), void *user)
|
||||||
{
|
{
|
||||||
|
int timeout_fd = -1;
|
||||||
|
struct clipboard_receive *ctx = NULL;
|
||||||
|
|
||||||
int flags;
|
int flags;
|
||||||
if ((flags = fcntl(read_fd, F_GETFL)) < 0 ||
|
if ((flags = fcntl(read_fd, F_GETFL)) < 0 ||
|
||||||
fcntl(read_fd, F_SETFL, flags | O_NONBLOCK) < 0)
|
fcntl(read_fd, F_SETFL, flags | O_NONBLOCK) < 0)
|
||||||
{
|
{
|
||||||
LOG_ERRNO("failed to set O_NONBLOCK");
|
LOG_ERRNO("failed to set O_NONBLOCK");
|
||||||
close(read_fd);
|
goto err;
|
||||||
done(user);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct clipboard_receive *ctx = xmalloc(sizeof(*ctx));
|
timeout_fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);
|
||||||
|
if (timeout_fd < 0) {
|
||||||
|
LOG_ERRNO("failed to create clipboard timeout timer FD");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct itimerspec timeout = {.it_value = {.tv_sec = 2}};
|
||||||
|
if (timerfd_settime(timeout_fd, 0, &timeout, NULL) < 0) {
|
||||||
|
LOG_ERRNO("faild to arm clipboard timeout timer");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = xmalloc(sizeof(*ctx));
|
||||||
*ctx = (struct clipboard_receive) {
|
*ctx = (struct clipboard_receive) {
|
||||||
|
.read_fd = read_fd,
|
||||||
|
.timeout_fd = timeout_fd,
|
||||||
|
.timeout = timeout,
|
||||||
.cb = cb,
|
.cb = cb,
|
||||||
.done = done,
|
.done = done,
|
||||||
.user = user,
|
.user = user,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!fdm_add(term->fdm, read_fd, EPOLLIN, &fdm_receive, ctx)) {
|
if (!fdm_add(term->fdm, read_fd, EPOLLIN, &fdm_receive, ctx) ||
|
||||||
close(read_fd);
|
!fdm_add(term->fdm, timeout_fd, EPOLLIN, &fdm_receive_timeout, ctx))
|
||||||
free(ctx);
|
{
|
||||||
done(user);
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
err:
|
||||||
|
free(ctx);
|
||||||
|
fdm_del(term->fdm, timeout_fd);
|
||||||
|
fdm_del(term->fdm, read_fd);
|
||||||
|
done(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue