This is an archive of the discontinued LLVM Phabricator instance.

[dfsan] Clean TLS after sigaction callbacks
ClosedPublic

Authored by stephan.yichao.zhao on Jan 28 2021, 2:49 PM.

Details

Summary
DFSan uses TLS to pass metadata of arguments and return values. When an
instrumented function accesses the TLS, if a signal callback happens, and
the callback calls other instrumented functions with updating the same TLS,
the TLS is in an inconsistent state after the callback ends. This may cause
either under-tainting or over-tainting.

This fix follows MSan's workaround.
  https://github.com/llvm/llvm-project/commit/cb22c67a21e4b5e1ade65141117a70be318be072
It simply resets TLS at restore. This prevents from over-tainting. Although
under-tainting may still happen, a taint flow can be found eventually if we
run a DFSan-instrumented program multiple times. The alternative option is
saving the entire TLS. However the TLS storage takes 2k bytes, and signal calls
could be nested. So it does not seem worth.

This diff fixes sigaction. A following diff will be fixing signal.

Diff Detail

Event Timeline

stephan.yichao.zhao requested review of this revision.Jan 28 2021, 2:49 PM
Herald added a project: Restricted Project. · View Herald TranscriptJan 28 2021, 2:49 PM
Herald added a subscriber: Restricted Project. · View Herald Transcript
morehouse added inline comments.Jan 29 2021, 10:05 AM
compiler-rt/lib/dfsan/dfsan.h
79
96

RAII seems like overkill since Backup() does nothing and we don't have any early returns in the functions we use it from. How about a simple function call instead?

compiler-rt/lib/dfsan/dfsan_custom.cpp
840
853

Can these params actually be labeled? Usually labels come in via TLS or extra args. (But I am not sure how signals work with DFSan)

868

sizeof(*si)?

899–932

I think we need to block signals before taking this lock. sigaction needs to be async-signal safe.

906

Any reason not to simply use SA_SIGINFO (and other standard SIG_IGN, SIG_DFL constants below)?

910

Is this cast necessary?

916

Is this cast necessary?

927

I think we need logic here to differentiate sa_sigaction and sa_handler instead of blindly casting to sa_sigaction.

compiler-rt/test/dfsan/sigaction.c
9

Is it worth testing legacy mode at all? I think the relevant code paths are the same.

33
38

Can remove this line if we do psa = {}; above.

41

We should also test that the correct oldact is returned.

compiler-rt/test/dfsan/sigaction_stress_test.c
3

Is it worth testing legacy mode at all? I think the relevant code paths are the same.

41–42
browneee added inline comments.Jan 29 2021, 10:46 AM
compiler-rt/lib/dfsan/dfsan.h
78

Typo: headlers -> handlers

The typo morehouse raised also appears in change description: accesse -> accesses

stephan.yichao.zhao added inline comments.
compiler-rt/lib/dfsan/dfsan_custom.cpp
853

Thank you for catching this. Right. This works only for *si and *uc. For others, their TLS need to be set. Will be changing.

899–932

Yeah. Such a case seems possible. Does blocking signals work like this?

sigset_t original;                                                         
sigset_t blocked;                                                          
sigemptyset(&blocked);                                           // is it fine to block all signals or needs to allow some, for example, SIGPROF and SIGALRM?  
sigprocmask(SIG_BLOCK, &blocked, &original);                              
{ lock, ///
}
sigprocmask(SIG_SETMASK, &original, nullptr);

MSan's code is likely to have the similar issue. @eugenis: Does MSan's case need a similar update?

morehouse added inline comments.Jan 29 2021, 12:26 PM
compiler-rt/lib/dfsan/dfsan_custom.cpp
899–932

s/sigemptyset/sigfillset

I would also recommend creating an RAII SignalSpinMutexLock that blocks signals prior to locking.

stephan.yichao.zhao edited the summary of this revision. (Show Details)Jan 29 2021, 2:21 PM
stephan.yichao.zhao marked 16 inline comments as done.

addressed comments

stephan.yichao.zhao marked an inline comment as done.

addressed comments

compiler-rt/lib/dfsan/dfsan.h
78

Thanks

96

I feel this one is more about making the code easy to read, and in case we need to make SignalAction and SignalHandler with multiple return points. it still works.

compiler-rt/lib/dfsan/dfsan_custom.cpp
840

rephrased after using a RAII async-safe lock.

899–932

introduced SignalSpinLocker. Named it as SignalSpinLocker but not SignalSpinMutexLock because mutex is moved to the class's private static member, and after that the class looks like a lock.

906

Using them avoids casting from handle_t to uptr. But since this function does not use __sanitizer::* consistently. replaced.

910

removed. they were requred if pnew_act was declared as __sanitizer::action_type.

927

Thank you for catching this. It worked before because the undefined behavior in linux: both sa_sigaction and sa_handler are defined as a union, and at runtime I guess linux always applies the 3 arguments that sa_sigaction needs, so it works for both sa_sigaction and sa_handler because sa_handler can safely use the first argument...

compiler-rt/test/dfsan/sigaction.c
41

This test case is for testing callbacks work by SIGHUP since that custom.cpp test cannot be kiiled.
I added some test cases about oldact in custom.cpp.

morehouse added inline comments.Feb 1 2021, 2:37 PM
compiler-rt/lib/dfsan/dfsan.h
90

Since this is only used in dfsan_custom.cpp, should we move it there?

96

Then let's remove Backup since it's unused. And also rename it to something more accurate, e.g. ClearThreadLocalState

compiler-rt/lib/dfsan/dfsan_custom.cpp
839

I don't see any atomics in the implementation.

885–887
925

I think these uptr casts are unnecessary.

compiler-rt/test/dfsan/custom.cpp
817–818
stephan.yichao.zhao marked 6 inline comments as done.

updated based on comments.

stephan.yichao.zhao marked an inline comment as done.Feb 1 2021, 3:22 PM
stephan.yichao.zhao added inline comments.
compiler-rt/lib/dfsan/dfsan.h
90

defined dfsan_clear_thread_local_state in dfsan.h, and moved ScopedThreadLocalStateBackup to dfsan_custom.cpp.

96

renamed to ScopedClearThreadLocalState and moved to dfsan_custom.cpp.

compiler-rt/lib/dfsan/dfsan_custom.cpp
885–887

Thanks.

This revision is now accepted and ready to land.Feb 1 2021, 4:02 PM
Harbormaster completed remote builds in B87437: Diff 320622.