Skip to content

Commit

Permalink
#Centipede Detect thread termination and call OnThreadStop in the mid…
Browse files Browse the repository at this point in the history
…dle of the thread body (e.g. when calling pthread_exit).

PiperOrigin-RevId: 574999308
  • Loading branch information
xinhaoyuan authored and copybara-github committed Oct 20, 2023
1 parent 59bb908 commit 6500fd4
Show file tree
Hide file tree
Showing 6 changed files with 98 additions and 8 deletions.
1 change: 1 addition & 0 deletions centipede/puzzles/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,5 @@ package(default_visibility = ["@com_google_fuzztest//centipede/puzzles:__subpack
"autodictionary_stress",
"paths",
"thread_uint32_cmp_1",
"pthread_uint32_cmp_1",
]]
49 changes: 49 additions & 0 deletions centipede/puzzles/pthread_uint32_cmp_1.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Copyright 2023 The Centipede Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Centipede puzzle: one 4-byte cmp, in a separate thread using pthread
// interface. We should be able to solve it w/o cmp features *or* w/o auto
// dictionary. RUN: Run && SolutionIs Fuzz RUN: Run --use_auto_dictionary=0 &&
// SolutionIs Fuzz RUN: Run --use_cmp_features=0 && SolutionIs Fuzz

#include <pthread.h>

#include <cstdint>
#include <cstdlib>
#include <cstring>

// non-const, to avoid compiler optimization.
static char expected_data[] = "Fuzz";

extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
pthread_t pt;
struct ThreadArg {
const uint8_t *data;
size_t size;
} arg = {.data = data, .size = size};
auto pt_entry = +[](const ThreadArg *thread_arg) {
uint32_t value, expected_value;
if (thread_arg->size == sizeof(value)) {
memcpy(&value, thread_arg->data, sizeof(value));
memcpy(&expected_value, expected_data, sizeof(expected_value));
if (value == expected_value) abort();
pthread_exit(nullptr);
}
};
if (pthread_create(&pt, nullptr,
reinterpret_cast<void *(*)(void *)>(pt_entry), &arg) != 0)
return 1;
if (pthread_join(pt, nullptr) != 0) return 1;
return 0;
}
4 changes: 1 addition & 3 deletions centipede/puzzles/thread_uint32_cmp_1.cc
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,7 @@
// We should be able to solve it w/o cmp features *or* w/o auto dictionary.
// RUN: Run && SolutionIs Fuzz
// RUN: Run --use_auto_dictionary=0 && SolutionIs Fuzz
// TODO(b/295378866): Centipede runner currently fails to collect dictionary
// entries from merged threads, so if we disable cmp features we can't crack
// this puzzle. Enable "Run --use_cmp_features=0 && SolutionIs Fuzz"
// RUN: Run --use_cmp_features=0 && SolutionIs Fuzz

#include <cstdint>
#include <cstdlib>
Expand Down
34 changes: 34 additions & 0 deletions centipede/runner.cc
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,17 @@ size_t LengthOfCommonPrefix(const void *s1, const void *s2, size_t n) {
return n;
}

class ThreadTerminationDetector {
public:
// A dummy method to trigger the construction and make sure that the
// destructor will be called on the thread termination.
__attribute__((optnone)) void EnsureAlive() {}

~ThreadTerminationDetector() { tls.OnThreadStop(); }
};

thread_local ThreadTerminationDetector termination_detector;

} // namespace

// Use of the fixed init priority allows to call CentipedeRunnerMain
Expand Down Expand Up @@ -126,6 +137,7 @@ void ThreadLocalRunnerState::TraceMemCmp(uintptr_t caller_pc, const uint8_t *s1,
}

void ThreadLocalRunnerState::OnThreadStart() {
termination_detector.EnsureAlive();
tls.lowest_sp = tls.top_frame_sp =
reinterpret_cast<uintptr_t>(__builtin_frame_address(0));
tls.call_stack.Reset(state.run_time_flags.callstack_level);
Expand All @@ -151,6 +163,15 @@ void ThreadLocalRunnerState::OnThreadStop() {
prev_tls->next = next_tls;
if (next_tls != nullptr) next_tls->prev = prev_tls;
}
tls.next = tls.prev = nullptr;
if (tls.ignore) return;
// Create a detached copy on heap and add it to detached_tls_list to
// collect its coverage later.
ThreadLocalRunnerState *detached_tls = new ThreadLocalRunnerState(tls);
auto *old_list = state.detached_tls_list;
detached_tls->next = old_list;
state.detached_tls_list = detached_tls;
if (old_list != nullptr) old_list->prev = detached_tls;
}

static size_t GetPeakRSSMb() {
Expand Down Expand Up @@ -233,6 +254,16 @@ static void CheckWatchdogLimits() {
}
}

void GlobalRunnerState::CleanUpDetachedTls() {
LockGuard lock(tls_list_mu);
ThreadLocalRunnerState *it_next = nullptr;
for (auto *it = detached_tls_list; it; it = it_next) {
it_next = it->next;
delete it;
}
detached_tls_list = nullptr;
}

void GlobalRunnerState::StartWatchdogThread() {
if (state.run_time_flags.timeout_per_input == 0 &&
state.run_time_flags.timeout_per_batch == 0 &&
Expand Down Expand Up @@ -313,6 +344,7 @@ static void WriteFeaturesToFile(FILE *file, const feature_t *features,
__attribute__((noinline)) // so that we see it in profile.
static void
PrepareCoverage(bool full_clear) {
state.CleanUpDetachedTls();
if (state.run_time_flags.path_level != 0) {
state.ForEachTls([](ThreadLocalRunnerState &tls) {
tls.path_ring_buffer.Reset(state.run_time_flags.path_level);
Expand Down Expand Up @@ -929,6 +961,8 @@ GlobalRunnerState::~GlobalRunnerState() {
StartSendingOutputsToEngine(outputs_blobseq);
FinishSendingOutputsToEngine(outputs_blobseq);
}
// Always clean up detached TLSs to avoid leakage.
CleanUpDetachedTls();
}

// If HasFlag(:shmem:), state.arg1 and state.arg2 are the names
Expand Down
17 changes: 13 additions & 4 deletions centipede/runner.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,9 @@ struct ThreadLocalRunnerState {
// Guarded by state.tls_list_mu.
ThreadLocalRunnerState *next, *prev;

// The pthread_create() interceptor calls OnThreadStart()/OnThreadStop()
// before/after the thread callback.
// The main thread calls OnThreadStart().
// The pthread_create() interceptor calls OnThreadStart()before the thread
// callback. The main thread also calls OnThreadStart(). OnThreadStop() will
// be called when thread termination is detected internally - see runner.cc
void OnThreadStart();
void OnThreadStop();

Expand All @@ -111,6 +111,7 @@ struct ThreadLocalRunnerState {
CmpTrace<0, 64> cmp_traceN;

// Set this to true if the thread needs to be ignored in ForEachTLS.
// It should be always false if the state is in the global detached_tls_list.
bool ignore;
};

Expand Down Expand Up @@ -191,7 +192,9 @@ struct GlobalRunnerState {

// Doubly linked list of TLSs of all live threads.
ThreadLocalRunnerState *tls_list;
pthread_mutex_t tls_list_mu; // Guards tls_list.
// Doubly linked list of detached TLSs.
ThreadLocalRunnerState *detached_tls_list;
pthread_mutex_t tls_list_mu; // Guards tls_list and detached_tls_list.
// Iterates all TLS objects under tls_list_mu, except those with `ignore` set.
// Calls `callback()` on every TLS.
template <typename Callback>
Expand All @@ -200,8 +203,14 @@ struct GlobalRunnerState {
for (auto *it = tls_list; it; it = it->next) {
if (!it->ignore) callback(*it);
}
for (auto *it = detached_tls_list; it; it = it->next) {
callback(*it);
}
}

// Reclaims all TLSs in detached_tls_list and cleans up the list.
void CleanUpDetachedTls();

// Computed by DlInfo().
// Usually, the main object is the executable binary containing main()
// and most of the executable code (we assume that the target is
Expand Down
1 change: 0 additions & 1 deletion centipede/runner_interceptors.cc
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ void *MyThreadStart(void *arg) {
auto *args = static_cast<ThreadCreateArgs *>(arg);
tls.OnThreadStart();
void *retval = args->start_routine(args->arg);
tls.OnThreadStop();
delete args; // allocated in the pthread_create wrapper.
return retval;
}
Expand Down

0 comments on commit 6500fd4

Please sign in to comment.