This is an archive of the discontinued LLVM Phabricator instance.

[Darwin] Fix symbolization for recent simulator runtimes.
AbandonedPublic

Authored by delcypher on Apr 7 2020, 10:25 PM.

Details

Reviewers
kubamracek
yln
Summary

Due to sandbox restrictions in the recent versions of the simulator runtime the
atos program is no longer able to access the task port of a parent process
without additional help.

This patch fixes this by registering a task port for the parent process
before spawning atos and also tells atos to look for this by setting
a special environment variable.

This patch is based on an Apple internal fix (rdar://problem/43693565) that
unfortunately contained a bug (rdar://problem/58789439) because it used
setenv() to set the special environment variable. This is not safe because in
certain circumstances this can trigger a call to realloc() which can fail
during symbolization leading to deadlock. A test case is included that captures
this problem.

This patch takes a different approach by modifying a copy of the environment
that is passed to atos and so fixes both rdar://problem/43693565 and
rdar://problem/58789439.

In more detail calling setenv() during symbolization sometimes caused
a deadlock in the following situation:

  1. Before TSan detects an issue Something calls setenv() that sets a

new environment variable that wasn't previously set. This triggers a
call to malloc() to allocate a new environment array. This uses TSan's
normal user-facing allocator. LibC stores this pointer for future use
later.

  1. TSan detects an issue and tries to launch the symbolizer. When we are in the

symbolizer we switch to a different (internal allocator) and then we call
setenv() to set a new environment variable. When this happen setenv() sees
that it needs to make the environment array larger and calls realloc() on the
existing enviroment array because it remembers that it previously allocated
memory for it. Calling realloc() fails here because it is being called on a
pointer its never seen before.

The included test case closely reproduces the originally reported
problem but it doesn't replicate the `((kBlockMagic)) ==
((((u64*)addr)[0])` assertion failure exactly. This is due to the way
TSan's normal allocator allocates the environment array the first time
it is allocated. In the test program addr[0] accesses an inaccessible
page and raises SIGBUS. If TSan's SIGBUS signal handler is active, the
signal is caught and symbolication is attempted again which results in
deadlock.

In the originally reported problem the pointer is successfully derefenced but
then the assert fails due to the provided pointer not coming from the active
allocator. When the assert fails TSan tries to symbolicate the stacktrace while
already being in the middle of symbolication which results in deadlock.

rdar://problem/58789439

Diff Detail

Unit TestsFailed

Event Timeline

delcypher created this revision.Apr 7 2020, 10:25 PM
Herald added a project: Restricted Project. · View Herald TranscriptApr 7 2020, 10:25 PM
Herald added subscribers: Restricted Project, jfb. · View Herald Transcript
delcypher updated this revision to Diff 256135.Apr 8 2020, 4:06 PM

typo fix.

delcypher updated this revision to Diff 256145.Apr 8 2020, 4:41 PM
  • Bug fix. If we are going to overwrite and existing entry but need to truncate use the existing entry slot.
kubamracek added inline comments.Apr 8 2020, 5:42 PM
compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_mac.cpp
79

I would like this to be done with a sequence of call for helper functions: 1) Copy, 2) AddOrReplace. Please separate the symbolizer-specific logic (printing the warning, using custom_env_ as the destination) from the env manipulation logic.

delcypher marked an inline comment as done.Apr 8 2020, 7:53 PM
delcypher added inline comments.
compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_mac.cpp
79

To make that work AddOrReplace would also need to be given the storage for the entry being added (as well as the storage for the copy of the environment array) because AtosSymbolizerProcess owns the storage (atos_mach_port_env_entry_).

These helper functions you are proposing will be extremely cumbersome to use (due to needing to pass the storage) which makes we wonder if they will ever be re-used elsewhere in the code. Do you foresee places in the code where we would want to use them? If the answer is no then I think refactoring now would be premature.

delcypher marked an inline comment as done.Apr 9 2020, 12:37 PM
delcypher added inline comments.
compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_mac.cpp
79

@kubamracek I've come up with an abstraction for buffers that actually makes what you're asking for a lot more elegant and feasible. I'll rework my patches based on this.

template <class T>
struct BufferRef {
  T *data;
  const uptr size; // number of elements of type T in buffer.
  BufferRef() : data(nullptr), size(0) {}
  BufferRef(T *data, uptr size) : data(data), size(size) {}
  T &operator[](uptr idx) {
    CHECK_LT(idx, size);
    return data[idx];
  }
  void copyFrom(BufferRef<T> &src) {
    CHECK_GE(size, src.size);
    internal_memcpy(data, src.data, sizeof(T) * src.size);
  }
};
delcypher updated this revision to Diff 257093.Apr 13 2020, 1:23 PM

Use new EnvArray type.