Skip to content

Commit

Permalink
chronos: Add a ptrace-based tracer.
Browse files Browse the repository at this point in the history
This is based on our past SystemSan work.

This will recursively find the command that builds the fuzz target, and
print out the environment variables and commands for building the
target.

Usage:
```
$ tracer <fuzz_target_source_name> <output_path> <build command>
```

Output (written to <output_path>):
```
export ENV=val
export ENV=val

cd /path/to/cwd
clang -o fuzz_target ...
```
  • Loading branch information
oliverchang committed Oct 11, 2024
1 parent c8bca3b commit 36621f7
Show file tree
Hide file tree
Showing 12 changed files with 463 additions and 88 deletions.
4 changes: 2 additions & 2 deletions infra/experimental/chronos/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ export FUZZING_LANGUAGE=c
infra/experimental/chronos/prepare-recompile "$PROJECT" "$FUZZ_TARGET" "$FUZZING_LANGUAGE"
python infra/helper.py build_image "$PROJECT"
# AddressSanitizer.
docker run -ti --entrypoint="/bin/sh" --env SANITIZER="address" --name "${PROJECT}-origin-asan" "gcr.io/oss-fuzz/${PROJECT}" -c "compile && rm -rf /out/*"
docker run --cap-add=SYS_PTRACE -ti --entrypoint="/bin/sh" --env SANITIZER="address" --name "${PROJECT}-origin-asan" "gcr.io/oss-fuzz/${PROJECT}" -c "compile && rm -rf /out/*"
docker commit "${PROJECT}-origin-asan" "gcr.io/oss-fuzz/${PROJECT}-ofg-cached-asan"
docker run -ti --entrypoint="recompile" "gcr.io/oss-fuzz/${PROJECT}-ofg-cached-asan"

# Coverage measurement.
docker run -ti --entrypoint="/bin/sh" --env SANITIZER="coverage" --name "${PROJECT}-origin-cov" "gcr.io/oss-fuzz/${PROJECT}" -c "compile && rm -rf /out/*"
docker run --cap-add=SYS_PTRACE -ti --entrypoint="/bin/sh" --env SANITIZER="coverage" --name "${PROJECT}-origin-cov" "gcr.io/oss-fuzz/${PROJECT}" -c "compile && rm -rf /out/*"
docker commit "${PROJECT}-origin-cov" "gcr.io/oss-fuzz/${PROJECT}-ofg-cached-cov"
docker run -ti --entrypoint="recompile" "gcr.io/oss-fuzz/${PROJECT}-ofg-cached-cov"
```
Expand Down
17 changes: 17 additions & 0 deletions infra/experimental/chronos/chronos-build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#!/bin/sh
# Copyright 2024 Google LLC
#
# 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
#
# http://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.
#
################################################################################
/src/tracer $FUZZ_TARGET /usr/local/bin/recompile $SRC/real_build.sh
81 changes: 0 additions & 81 deletions infra/experimental/chronos/chronos.sh

This file was deleted.

9 changes: 5 additions & 4 deletions infra/experimental/chronos/prepare-recompile
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,14 @@ FUZZ_TARGET=$2
FUZZING_LANGUAGE=$3

# Step 1: Copy chronos.sh to its project directory.
cp infra/experimental/chronos/chronos.sh "projects/$PROJECT/"
cp infra/experimental/chronos/chronos-build.sh "projects/$PROJECT/"

# Step 2: Copy chronos.sh to image and set FUZZ_TARGET and FUZZING_LANGUAGE in its Dockerfile.
{
echo "COPY chronos.sh /src";
echo "ADD https://clusterfuzz-builds.storage.googleapis.com/tracer \$SRC/";
echo "RUN chmod +x \$SRC/tracer";
echo "RUN cp \$SRC/build.sh \$SRC/real_build.sh";
echo "COPY chronos-build.sh \$SRC/build.sh";
echo "ENV FUZZ_TARGET=\"$FUZZ_TARGET\"";
echo "ENV FUZZING_LANGUAGE=\"$FUZZING_LANGUAGE\"";
# Step 3: Source chronos.sh at the beginning of its build.sh.
echo "RUN sed -i.bak \"1s|^|source \\\"/src/chronos.sh\\\"\\n|\" \"/src/build.sh\""
} >> "projects/$PROJECT/Dockerfile"
1 change: 1 addition & 0 deletions infra/experimental/chronos/tracer/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
tracer
11 changes: 11 additions & 0 deletions infra/experimental/chronos/tracer/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
.POSIX:
CXX = clang++
CFLAGS = -std=c++20 -Wall -Wextra -O3 -g3 -Werror -static

all: tracer

tracer: tracer.cpp inspect_utils.cpp
$(CXX) $(CFLAGS) -lpthread -o $@ $^

clean:
rm -f tracer
6 changes: 6 additions & 0 deletions infra/experimental/chronos/tracer/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Deployment

```
$ make
$ gsutil cp tracer gs://clusterfuzz-builds/
```
87 changes: 87 additions & 0 deletions infra/experimental/chronos/tracer/inspect_utils.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/*
* Copyright 2022 Google LLC
* 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
* http://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.
*/
/* A detector that uses ptrace to identify DNS arbitrary resolutions. */

/* C standard library */
#include <signal.h>

/* POSIX */
#include <unistd.h>

/* Linux */
#include <sys/ptrace.h>

#include <iostream>
#include <string>
#include <vector>
#include <map>

#include "inspect_utils.h"

extern pid_t g_root_pid;
extern std::map<pid_t, ThreadParent> root_pids;

std::vector<std::byte> read_memory(pid_t pid, unsigned long long address,
size_t size) {
std::vector<std::byte> memory;

for (size_t i = 0; i < size; i += sizeof(long)) {
long word = ptrace(PTRACE_PEEKTEXT, pid, address + i, 0);
if (word == -1) {
return memory;
}

std::byte *word_bytes = reinterpret_cast<std::byte *>(&word);
memory.insert(memory.end(), word_bytes, word_bytes + sizeof(long));
}

return memory;
}

// Construct a string with the memory specified in a register.
std::string read_string(pid_t pid, unsigned long long reg, unsigned long length) {
auto memory = read_memory(pid, reg, length);
if (!memory.size()) {
return "";
}

std::string content(reinterpret_cast<char *>(memory.data()),
std::min(memory.size(), length));
return content.c_str();
}

unsigned long long read_pointer(pid_t pid, unsigned long long address) {
auto memory = read_memory(pid, address, sizeof(unsigned long long));
return *reinterpret_cast<unsigned long long *>(memory.data());
}

// Read null pointer terminated array.
std::vector<std::string> read_null_pointer_terminated_array(
pid_t pid, unsigned long long address, const int max_item_len, const int max_array_len) {
std::vector<std::string> result;

for (int i = 0; i < max_array_len; ++i) {
auto ptr = read_pointer(pid, address);
if (ptr == 0) {
break;
}
auto value = read_string(pid, ptr, max_item_len);
result.push_back(value);
address += sizeof(unsigned long long);
}

return result;
}
42 changes: 42 additions & 0 deletions infra/experimental/chronos/tracer/inspect_utils.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright 2022 Google LLC
* 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
* http://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.
*/
/* A detector that uses ptrace to identify DNS arbitrary resolutions. */


/* POSIX */
#include <unistd.h>

#include <string>
#include <vector>

// Structure to know which thread id triggered the bug.
struct ThreadParent {
// Parent thread ID, ie creator.
pid_t parent_tid;
// Current thread ID ran exec to become another process.
bool ran_exec = false;

ThreadParent() : parent_tid(0) {}
ThreadParent(pid_t tid) : parent_tid(tid) {}
};

std::vector<std::byte> read_memory(pid_t pid, unsigned long long address,
size_t size);
std::string read_string(pid_t pid, unsigned long long reg, unsigned long length);
unsigned long long read_pointer(pid_t pid, unsigned long long address);

std::vector<std::string> read_null_pointer_terminated_array(
pid_t pid, unsigned long long address, const int max_item_len, const int max_array_len);
Loading

0 comments on commit 36621f7

Please sign in to comment.