[PATCH RFC 10/11] um: Delay timer_read only in possible busy loops in TT-mode

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

Some userspace programs use the current timestamp as a looping condition
(directly or indirectly), which can lead to infinite loops in TT-mode
since the timestamp doesn't change until the next time step.

Commit 065038706f77 ("um: Support time travel mode") introduced a
workaround by always inserting a time step when the current timestamp is
read. However, this introduces delays, even in cases where such a loop
doesn't exist, for example, in filesystem code that updates access
timestamps.

This slows down external TT-mode as more simulation roundtrips are
required, and it unnecessarily affects the determinism and accuracy of
the simulation.

This patch attempts to detect potential busy loops by identifying the
currently executed syscall that would return a timestamp. If 10
consecutive calls at the same timestamp occur, the timer_read is
delayed.

Since the patch does not consider the PID of the caller, it might be
fooled by legitimate processes that take a single timestamp. However,
the result is no worse than the original behavior.

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

diff --git a/arch/um/kernel/time.c b/arch/um/kernel/time.c
index f76237cfc1ea..1782f1ed140e 100644
--- a/arch/um/kernel/time.c
+++ b/arch/um/kernel/time.c
@@ -22,6 +22,7 @@
#include <linux/time-internal.h>
#include <linux/um_timetravel.h>
#include <shared/init.h>
+#include <asm/syscall-generic.h>

#ifdef CONFIG_UML_TIME_TRAVEL_SUPPORT
enum time_travel_mode time_travel_mode;
@@ -721,6 +722,27 @@ static irqreturn_t um_timer(int irq, void *dev)
return IRQ_HANDLED;
}

+static const int suspicious_busy_loop_syscalls[] = {
+ 36, //sys_getitimer
+ 96, //sys_gettimeofday
+ 201, //sys_time
+ 224, //sys_timer_gettime
+ 228, //sys_clock_gettime
+ 287, //sys_timerfd_gettime
+};
+
+static bool suspicious_busy_loop(void)
+{
+ int i;
+ int syscall = syscall_get_nr(current, task_pt_regs(current));
+
+ for (i = 0; i < ARRAY_SIZE(suspicious_busy_loop_syscalls); i++) {
+ if (suspicious_busy_loop_syscalls[i] == syscall)
+ return true;
+ }
+ return false;
+};
+
static u64 timer_read(struct clocksource *cs)
{
static unsigned long long last_timer_read;
@@ -742,11 +764,12 @@ static u64 timer_read(struct clocksource *cs)
if (last_timer_read != time_travel_time) {
last_timer_read = time_travel_time;
consecutive_calls_at_same_time = 0;
- } else {
+ } else if (suspicious_busy_loop()) {
consecutive_calls_at_same_time++;
}
if (!irqs_disabled() && !in_interrupt() && !in_softirq() &&
- !time_travel_ext_waiting && consecutive_calls_at_same_time > 10)
+ !time_travel_ext_waiting && consecutive_calls_at_same_time > 10 &&
+ suspicious_busy_loop())
time_travel_update_time(time_travel_time +
TIMER_MULTIPLIER,
false);

--
2.34.1