-
-
Notifications
You must be signed in to change notification settings - Fork 49
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add event buffering for cloaking user input patterns #149
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -37,6 +37,8 @@ | |||||
#include <sys/wait.h> | ||||||
#include <sys/resource.h> | ||||||
#include <sys/uio.h> | ||||||
#include <sys/queue.h> | ||||||
#include <sys/random.h> | ||||||
#include <signal.h> | ||||||
#include <poll.h> | ||||||
#include <errno.h> | ||||||
|
@@ -2386,11 +2388,71 @@ static void process_xevent_xembed(Ghandles * g, const XClientMessageEvent * ev) | |||||
|
||||||
} | ||||||
|
||||||
/* get current time */ | ||||||
static int64_t ebuf_current_time_ms() | ||||||
{ | ||||||
int64_t timeval; | ||||||
struct timespec spec; | ||||||
clock_gettime(CLOCK_MONOTONIC, &spec); | ||||||
timeval = (((int64_t)spec.tv_sec) * 1000LL) + (((int64_t)spec.tv_nsec) / 1000000LL); | ||||||
return timeval; | ||||||
} | ||||||
|
||||||
/* get random delay value */ | ||||||
static uint32_t ebuf_random_delay(Ghandles * g, uint32_t lower_bound) | ||||||
{ | ||||||
uint32_t maxval; | ||||||
uint32_t randval; | ||||||
size_t randsize; | ||||||
union ebuf_rand ebuf_rand_data; | ||||||
|
||||||
if (lower_bound >= g->ebuf_max_delay) { | ||||||
fprintf(stderr, | ||||||
"Bug detected - lower_bound >= g->ebuf_max_delay, events may get briefly stuck"); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is reachable if 2 events arrive within the same ms and the |
||||||
return g->ebuf_max_delay; | ||||||
} | ||||||
|
||||||
maxval = g->ebuf_max_delay - lower_bound + 1; | ||||||
|
||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nitpick: trainling whitespace (for some reason GitHub's web UI doesn't show it, but it's in commit 541c10d?) |
||||||
do { | ||||||
randsize = getrandom(ebuf_rand_data.raw, sizeof(uint32_t), 0); | ||||||
if (randsize != sizeof(uint32_t)) | ||||||
continue; | ||||||
} while (ebuf_rand_data.val >= UINT32_MAX - (UINT32_MAX % maxval)); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this check is correct. But it unnecessarily throws away value if 2**32 is a multiple of maxval. That was a bit confusing during review. |
||||||
|
||||||
randval = ebuf_rand_data.val % maxval; | ||||||
return lower_bound + randval; | ||||||
} | ||||||
|
||||||
/* queue input event */ | ||||||
static void ebuf_queue_xevent(Ghandles * g, XEvent xev) | ||||||
{ | ||||||
int64_t current_time; | ||||||
uint32_t random_delay; | ||||||
struct ebuf_entry *new_ebuf_entry; | ||||||
uint32_t lower_bound; | ||||||
|
||||||
current_time = ebuf_current_time_ms(); | ||||||
lower_bound = min(max(g->ebuf_prev_release_time - current_time, 0), g->ebuf_max_delay); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This deserves some explanation. Why do you make the random delay dependent on the previous random delay value? |
||||||
random_delay = ebuf_random_delay(g, lower_bound); | ||||||
new_ebuf_entry = malloc(sizeof(struct ebuf_entry)); | ||||||
if (new_ebuf_entry == NULL) { | ||||||
perror("Could not allocate ebuf_entry:"); | ||||||
exit(1); | ||||||
} | ||||||
if (current_time > 0 && random_delay > (LONG_MAX - current_time)) { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
fprintf(stderr, "Event scheduler overflow detected, cannot continue"); | ||||||
exit(1); | ||||||
} | ||||||
new_ebuf_entry->time = current_time + random_delay; | ||||||
new_ebuf_entry->xev = xev; | ||||||
TAILQ_INSERT_TAIL(&(g->ebuf_head), new_ebuf_entry, entries); | ||||||
g->ebuf_prev_release_time = new_ebuf_entry->time; | ||||||
} | ||||||
|
||||||
/* dispatch local Xserver event */ | ||||||
static void process_xevent(Ghandles * g) | ||||||
static void process_xevent_core(Ghandles * g, XEvent event_buffer) | ||||||
{ | ||||||
XEvent event_buffer; | ||||||
XNextEvent(g->display, &event_buffer); | ||||||
switch (event_buffer.type) { | ||||||
case KeyPress: | ||||||
case KeyRelease: | ||||||
|
@@ -2449,6 +2511,40 @@ static void process_xevent(Ghandles * g) | |||||
} | ||||||
} | ||||||
|
||||||
/* dispatch queued events */ | ||||||
static void ebuf_release_xevents(Ghandles * g) | ||||||
{ | ||||||
int64_t current_time; | ||||||
struct ebuf_entry *current_ebuf_entry; | ||||||
|
||||||
current_time = ebuf_current_time_ms(); | ||||||
while ((current_ebuf_entry = TAILQ_FIRST(&(g->ebuf_head))) | ||||||
&& (current_time >= current_ebuf_entry->time)) { | ||||||
XEvent event_buffer = current_ebuf_entry->xev; | ||||||
process_xevent_core(g, event_buffer); | ||||||
TAILQ_REMOVE(&(g->ebuf_head), current_ebuf_entry, entries); | ||||||
free(current_ebuf_entry); | ||||||
} | ||||||
current_ebuf_entry = TAILQ_FIRST(&(g->ebuf_head)); | ||||||
if (current_ebuf_entry == NULL) { | ||||||
g->ebuf_next_timeout = VCHAN_DEFAULT_POLL_DURATION; | ||||||
} else { | ||||||
g->ebuf_next_timeout = (int)(current_ebuf_entry->time - current_time); | ||||||
} | ||||||
} | ||||||
|
||||||
/* handle or queue local Xserver event */ | ||||||
static void process_xevent(Ghandles * g) | ||||||
{ | ||||||
XEvent event_buffer; | ||||||
XNextEvent(g->display, &event_buffer); | ||||||
if (g->ebuf_max_delay > 0) { | ||||||
ebuf_queue_xevent(g, event_buffer); | ||||||
} else { | ||||||
process_xevent_core(g, event_buffer); | ||||||
} | ||||||
} | ||||||
|
||||||
|
||||||
/* handle VM message: MSG_SHMIMAGE | ||||||
* pass message data to do_shm_update - there input validation will be done */ | ||||||
|
@@ -4237,11 +4333,23 @@ static void parse_vm_config(Ghandles * g, config_setting_t * group) | |||||
g->disable_override_redirect = 0; | ||||||
else { | ||||||
fprintf(stderr, | ||||||
"unsupported value ‘%s’ for override_redirect (must be ‘disabled’ or ‘allow’\n", | ||||||
"unsupported value '%s' for override_redirect (must be 'disabled' or 'allow')\n", | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nitpick: Including such an unrelated small change in a PR is ok, but belongs into a separate commit. |
||||||
value); | ||||||
exit(1); | ||||||
} | ||||||
} | ||||||
|
||||||
if ((setting = | ||||||
config_setting_get_member(group, "events_max_delay"))) { | ||||||
int delay_val = config_setting_get_int(setting); | ||||||
if (delay_val < 0 || delay_val > 5000) { | ||||||
fprintf(stderr, | ||||||
"unsupported value '%d' for events_max_delay (must be >= 0 and <= 5000)", | ||||||
delay_val); | ||||||
exit(1); | ||||||
} | ||||||
g->ebuf_max_delay = delay_val; | ||||||
} | ||||||
} | ||||||
|
||||||
static void parse_config(Ghandles * g) | ||||||
|
@@ -4364,11 +4472,13 @@ int main(int argc, char **argv) | |||||
/* parse cmdline, possibly overriding values from config */ | ||||||
parse_cmdline(&ghandles, argc, argv); | ||||||
get_boot_lock(ghandles.domid); | ||||||
/* init event queue */ | ||||||
TAILQ_INIT(&(ghandles.ebuf_head)); | ||||||
|
||||||
if (!ghandles.nofork) { | ||||||
// daemonize... | ||||||
if (pipe(pipe_notify) < 0) { | ||||||
perror("canot create pipe:"); | ||||||
perror("cannot create pipe:"); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nitpick: Including such an unrelated typo fix in a PR is ok, but belongs into a separate commit. |
||||||
exit(1); | ||||||
} | ||||||
|
||||||
|
@@ -4559,8 +4669,15 @@ int main(int argc, char **argv) | |||||
handle_message(&ghandles); | ||||||
busy = 1; | ||||||
} | ||||||
if (ghandles.ebuf_max_delay > 0) { | ||||||
ebuf_release_xevents(&ghandles); | ||||||
} | ||||||
} while (busy); | ||||||
wait_for_vchan_or_argfd(ghandles.vchan, xfd); | ||||||
if (ghandles.ebuf_max_delay > 0) { | ||||||
wait_for_vchan_or_argfd_once(ghandles.vchan, xfd, ghandles.ebuf_next_timeout); | ||||||
} else { | ||||||
wait_for_vchan_or_argfd_once(ghandles.vchan, xfd, VCHAN_DEFAULT_POLL_DURATION); | ||||||
} | ||||||
} | ||||||
return 0; | ||||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given that the function is otherwise self-contained, I think it would be better to pass the upper bound instead of the full
Ghandles
.