Re: [PATCH 1/1] Fix: trace sched switch start/stop racy updates

From: Mathieu Desnoyers
Date: Sat Aug 17 2019 - 10:27:43 EST

----- On Aug 16, 2019, at 3:15 PM, rostedt rostedt@xxxxxxxxxxx wrote:

> On Fri, 16 Aug 2019 13:19:20 -0400 (EDT)
> Mathieu Desnoyers <mathieu.desnoyers@xxxxxxxxxxxx> wrote:
>> ----- On Aug 16, 2019, at 12:25 PM, rostedt rostedt@xxxxxxxxxxx wrote:
>> > On Fri, 16 Aug 2019 10:26:43 -0400 Mathieu Desnoyers
>> > <mathieu.desnoyers@xxxxxxxxxxxx> wrote:
>> >
>> [...]
>> >>
>> >> Also, write and read to/from those variables should be done with
>> >> WRITE_ONCE() and READ_ONCE(), given that those are read within tracing
>> >> probes without holding the sched_register_mutex.
>> >>
>> >
>> > I understand the READ_ONCE() but is the WRITE_ONCE() truly necessary?
>> > It's done while holding the mutex. It's not that critical of a path,
>> > and makes the code look ugly.
>> The update is done while holding the mutex, but the read-side does not
>> hold that mutex, so it can observe the intermediate state caused by
>> store-tearing or invented stores which can be generated by the compiler
>> on the update-side.
>> Please refer to the following LWN article:
>> Sections:
>> - "Store tearing"
>> - "Invented stores"
>> Arguably, based on that article, store tearing is only observed in the
>> wild for constants (which is not the case here), and invented stores
>> seem to require specific code patterns. But I wonder why we would ever want to
>> pair a fragile non-volatile store with a READ_ONCE() ? Considering the pain
>> associated to reproduce and hunt down this kind of issue in the wild, I would
>> be tempted to enforce that any READ_ONCE() operating on a variable would either
>> need to be paired with WRITE_ONCE() or with atomic operations, so those can
>> eventually be validated by static code checkers and code sanitizers.
> My issue is that this is just a case to decide if we should cache a
> comm or not. It's a helper, nothing more. There's no guarantee that
> something will get cached.

I get your point wrt WRITE_ONCE(): since it's a cache it should not have
user-visible effects if a temporary incorrect value is observed. Well in
reality, it's not a cache: if the lookup fails, it returns "<...>" instead,
so cache lookup failure ends up not providing any useful data in the trace.
Let's assume this is a known and documented tracer limitation.

However, wrt READ_ONCE(), things are different. The variable read ends up
being used to control various branches in the code, and the compiler could
decide to re-fetch the variable (with a different state), and therefore
cause _some_ of the branches to be inconsistent. See
tracing_record_taskinfo_sched_switch() and tracing_record_taskinfo() @flags

AFAIU the current code should not generate any out-of-bound writes in case of
re-fetch, but no comment in there documents how fragile this is.



Mathieu Desnoyers
EfficiOS Inc.