[PATCH RFC 08/11] um: Protect accesses to the timetravel event list

Benjamin Beichler
2023-11-03T16:42:24Z

When the timetravel ext modus is used, accessing the timetravel event
list can be interrupted by a message on the timetravel socket in the
SIGIO signal handler. This interruption can potentially modify the event
list, leading to race conditions that cause deadlocks in the timetravel
protocol or disrupt the ordered nature of the list, triggering bugs.

Previously, the normal irq-save function did not intentionally block the
timetravel handlers. However, the additional (un)block_signals_hard
functions do block them. Therefore, these functions have been added at
the appropriate places to address the issue.

It's worth noting that although the functions are named as blocking,
they primarily defer the actual execution of the SIGIO handlers until
the unblock call. If no signal was issued, this mainly results in a
variable assignment and a memory barrier.

Signed-off-by: Benjamin Beichler <benjamin.beichler@uni-rostock.de>
---
arch/um/kernel/time.c | 8 ++++++++
1 file changed, 8 insertions(+)

diff --git a/arch/um/kernel/time.c b/arch/um/kernel/time.c
index e513256eadfe..f1b2ca45994d 100644
--- a/arch/um/kernel/time.c
+++ b/arch/um/kernel/time.c
@@ -288,6 +288,7 @@ static void __time_travel_add_event(struct time_travel_event *e,
e->time = time;

local_irq_save(flags);
+ block_signals_hard();
list_for_each_entry(tmp, &time_travel_events, list) {
/*
* Add the new entry before one with higher time,
@@ -310,6 +311,7 @@ static void __time_travel_add_event(struct time_travel_event *e,
tmp = time_travel_first_event();
time_travel_ext_update_request(tmp->time);
time_travel_next_event = tmp->time;
+ unblock_signals_hard();
local_irq_restore(flags);
}

@@ -349,6 +351,7 @@ void deliver_time_travel_irqs(void)
return;

local_irq_save(flags);
+ block_signals_hard();
irq_enter();
while ((e = list_first_entry_or_null(&time_travel_irqs,
struct time_travel_event,
@@ -358,6 +361,7 @@ void deliver_time_travel_irqs(void)
e->fn(e);
}
irq_exit();
+ unblock_signals_hard();
local_irq_restore(flags);
}

@@ -370,7 +374,9 @@ static void time_travel_deliver_event(struct time_travel_event *e)
*/
e->fn(e);
} else if (irqs_disabled()) {
+ block_signals_hard();
list_add_tail(&e->list, &time_travel_irqs);
+ unblock_signals_hard();
/*
* set pending again, it was set to false when the
* event was deleted from the original list, but
@@ -395,8 +401,10 @@ bool time_travel_del_event(struct time_travel_event *e)
if (!e->pending)
return false;
local_irq_save(flags);
+ block_signals_hard();
list_del(&e->list);
e->pending = false;
+ unblock_signals_hard();
local_irq_restore(flags);
return true;
}

--
2.34.1