[PATCH v2] um: time-travel: fix time corruption

Vincent Whitchurch Vincent.Whitchurch at axis.com
Thu Oct 26 00:23:49 PDT 2023


On Wed, 2023-10-25 at 22:45 +0200, Johannes Berg wrote:
> From: Johannes Berg <johannes.berg at intel.com>
> 
> In 'basic' time-travel mode (without =inf-cpu or =ext), we
> still get timer interrupts. These can happen at arbitrary
> points in time, i.e. while in timer_read(), which pushes
> time forward just a little bit. Then, if we happen to get
> the interrupt after calculating the new time to push to,
> but before actually finishing that, the interrupt will set
> the time to a value that's incompatible with the forward,
> and we'll crash because time goes backwards when we do the
> forwarding.
> 
> Fix this by reading the time_travel_time, calculating the
> adjustment, and doing the adjustment all with interrupts
> disabled.
> 
> Reported-by: Vincent Whitchurch <Vincent.Whitchurch at axis.com>
> Signed-off-by: Johannes Berg <johannes.berg at intel.com>

Thanks, this works for me too.  However, one question below.

> ---
> v2: remove stray debug code
> ---
>  arch/um/kernel/time.c | 32 +++++++++++++++++++++++++++-----
>  1 file changed, 27 insertions(+), 5 deletions(-)
> 
> diff --git a/arch/um/kernel/time.c b/arch/um/kernel/time.c
> index 8ff46bc86d09..0c01674e14d5 100644
> --- a/arch/um/kernel/time.c
> +++ b/arch/um/kernel/time.c
> @@ -551,9 +551,29 @@ static void time_travel_update_time(unsigned long long next, bool idle)
>  	time_travel_del_event(&ne);
>  }
>  
> 
> +static void time_travel_update_time_rel(unsigned long long offs)
> +{
> +	unsigned long flags;
> +
> +	/*
> +	 * Disable interrupts before calculating the new time so
> +	 * that a real timer interrupt (signal) can't happen at
> +	 * a bad time e.g. after we read time_travel_time but
> +	 * before we've completed updating the time.
> +	 */
> +	local_irq_save(flags);
> +	time_travel_update_time(time_travel_time + offs, false);
> +	local_irq_restore(flags);
> +}
> +
>  void time_travel_ndelay(unsigned long nsec)
>  {
> -	time_travel_update_time(time_travel_time + nsec, false);
> +	/*
> +	 * Not strictly needed to use _rel() version since this is
> +	 * only used in INFCPU/EXT modes, but it doesn't hurt and
> +	 * is more readable too.
> +	 */
> +	time_travel_update_time_rel(nsec);
>  }
>  EXPORT_SYMBOL(time_travel_ndelay);
>  
> 
> @@ -687,7 +707,11 @@ static void time_travel_set_start(void)
>  #define time_travel_time 0
>  #define time_travel_ext_waiting 0
>  
> 
> -static inline void time_travel_update_time(unsigned long long ns, bool retearly)
> +static inline void time_travel_update_time(unsigned long long ns, bool idle)
> +{
> +}
> +
> +static inline void time_travel_update_time_rel(unsigned long long offs)
>  {
>  }
>  
> 
> @@ -839,9 +863,7 @@ static u64 timer_read(struct clocksource *cs)
>  		 */
>  		if (!irqs_disabled() && !in_interrupt() && !in_softirq() &&
>  		    !time_travel_ext_waiting)
> -			time_travel_update_time(time_travel_time +
> -						TIMER_MULTIPLIER,
> -						false);
> +			time_travel_update_time_rel(TIMER_MULTIPLIER);
>  		return time_travel_time / TIMER_MULTIPLIER;
>  	}

The reason I hesitated with putting the whole of
time_travel_update_time() under local_irq_save() in my attempt was
because I didn't quite understand the reason for the !irqs_disabled()
condition here and the comment just above it about recursion and things
getting messed up.  If it's OK to disable interrupts as this patch does,
is the !irqs_disabled() condition valid?


More information about the linux-um mailing list