diff --git a/trusty/coverage/Android.bp b/trusty/coverage/Android.bp new file mode 100644 index 000000000..a4adf81be --- /dev/null +++ b/trusty/coverage/Android.bp @@ -0,0 +1,45 @@ +// Copyright (C) 2020 The Android Open-Source Project +// +// 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. + +cc_library { + name: "libtrusty_coverage", + srcs: [ + "coverage.cpp", + ], + export_include_dirs: [ + "include", + ], + static_libs: [ + "libtrusty_test", + ], + shared_libs: [ + "libbase", + "liblog", + ], +} + +cc_test { + name: "libtrusty_coverage_test", + srcs: [ + "coverage_test.cpp", + ], + static_libs: [ + "libtrusty_coverage", + "libtrusty_test", + ], + shared_libs: [ + "libbase", + "liblog", + ], +} diff --git a/trusty/coverage/coverage.cpp b/trusty/coverage/coverage.cpp new file mode 100644 index 000000000..1162f42aa --- /dev/null +++ b/trusty/coverage/coverage.cpp @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2020 The Android Open Sourete Project + * + * 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. + */ + +#define LOG_TAG "coverage" + +#include +#include +#include +#include +#include +#include +#include +#include + +#define COVERAGE_CLIENT_PORT "com.android.trusty.coverage.client" + +namespace android { +namespace trusty { +namespace coverage { + +using android::base::ErrnoError; +using android::base::Error; +using std::string; + +static inline uintptr_t RoundPageUp(uintptr_t val) { + return (val + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1); +} + +CoverageRecord::CoverageRecord(string tipc_dev, struct uuid* uuid) + : tipc_dev_(std::move(tipc_dev)), + coverage_srv_fd_(-1), + uuid_(*uuid), + record_len_(0), + shm_(NULL), + shm_len_(0) {} + +CoverageRecord::~CoverageRecord() { + if (shm_) { + munmap((void*)shm_, shm_len_); + } +} + +Result CoverageRecord::Rpc(coverage_client_req* req, int req_fd, coverage_client_resp* resp) { + int rc; + + if (req_fd < 0) { + rc = write(coverage_srv_fd_, req, sizeof(*req)); + } else { + iovec iov = { + .iov_base = req, + .iov_len = sizeof(*req), + }; + + trusty_shm shm = { + .fd = req_fd, + .transfer = TRUSTY_SHARE, + }; + + rc = tipc_send(coverage_srv_fd_, &iov, 1, &shm, 1); + } + + if (rc != (int)sizeof(*req)) { + return ErrnoError() << "failed to send request to coverage server: "; + } + + rc = read(coverage_srv_fd_, resp, sizeof(*resp)); + if (rc != (int)sizeof(*resp)) { + return ErrnoError() << "failed to read reply from coverage server: "; + } + + if (resp->hdr.cmd != (req->hdr.cmd | COVERAGE_CLIENT_CMD_RESP_BIT)) { + return ErrnoError() << "unknown response cmd: " << resp->hdr.cmd; + } + + return {}; +} + +Result CoverageRecord::Open() { + coverage_client_req req; + coverage_client_resp resp; + + if (shm_) { + return {}; /* already initialized */ + } + + int fd = tipc_connect(tipc_dev_.c_str(), COVERAGE_CLIENT_PORT); + if (fd < 0) { + return ErrnoError() << "failed to connect to Trusty coverarge server: "; + } + coverage_srv_fd_.reset(fd); + + req.hdr.cmd = COVERAGE_CLIENT_CMD_OPEN; + req.open_args.uuid = uuid_; + auto ret = Rpc(&req, -1, &resp); + if (!ret.ok()) { + return Error() << "failed to open coverage client: "; + } + record_len_ = resp.open_args.record_len; + shm_len_ = RoundPageUp(record_len_); + + fd = memfd_create("trusty-coverage", 0); + if (fd < 0) { + return ErrnoError() << "failed to create memfd: "; + } + unique_fd memfd(fd); + + if (ftruncate(memfd, shm_len_) < 0) { + return ErrnoError() << "failed to resize memfd: "; + } + + void* shm = mmap(0, shm_len_, PROT_READ | PROT_WRITE, MAP_SHARED, memfd, 0); + if (shm == MAP_FAILED) { + return ErrnoError() << "failed to map memfd: "; + } + + req.hdr.cmd = COVERAGE_CLIENT_CMD_SHARE_RECORD; + req.share_record_args.shm_len = shm_len_; + ret = Rpc(&req, memfd, &resp); + if (!ret.ok()) { + return Error() << "failed to send shared memory: "; + } + + shm_ = shm; + return {}; +} + +void CoverageRecord::Reset() { + for (size_t i = 0; i < shm_len_; i++) { + *((volatile uint8_t*)shm_ + i) = 0; + } +} + +void CoverageRecord::GetRawData(volatile void** begin, volatile void** end) { + assert(shm_); + + *begin = shm_; + *end = (uint8_t*)(*begin) + record_len_; +} + +uint64_t CoverageRecord::CountEdges() { + assert(shm_); + + uint64_t counter = 0; + + volatile uint8_t* begin = NULL; + volatile uint8_t* end = NULL; + + GetRawData((volatile void**)&begin, (volatile void**)&end); + + for (volatile uint8_t* x = begin; x < end; x++) { + counter += *x; + } + + return counter; +} + +} // namespace coverage +} // namespace trusty +} // namespace android diff --git a/trusty/coverage/coverage_test.cpp b/trusty/coverage/coverage_test.cpp new file mode 100644 index 000000000..d8df7a46f --- /dev/null +++ b/trusty/coverage/coverage_test.cpp @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * 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. + */ + +#include +#include +#include +#include +#include + +using android::base::unique_fd; +using std::array; +using std::make_unique; +using std::unique_ptr; + +#define TIPC_DEV "/dev/trusty-ipc-dev0" +#define TEST_SRV_PORT "com.android.trusty.sancov.test.srv" + +namespace android { +namespace trusty { +namespace coverage { + +/* Test server's UUID is 77f68803-c514-43ba-bdce-3254531c3d24 */ +static struct uuid test_srv_uuid = { + 0x77f68803, + 0xc514, + 0x43ba, + {0xbd, 0xce, 0x32, 0x54, 0x53, 0x1c, 0x3d, 0x24}, +}; + +class CoverageTest : public ::testing::Test { + public: + void SetUp() override { + record_ = make_unique(TIPC_DEV, &test_srv_uuid); + auto ret = record_->Open(); + ASSERT_TRUE(ret.ok()) << ret.error(); + } + + void TearDown() override { record_.reset(); } + + unique_ptr record_; +}; + +TEST_F(CoverageTest, CoverageReset) { + record_->Reset(); + auto counter = record_->CountEdges(); + ASSERT_EQ(counter, 0); +} + +TEST_F(CoverageTest, TestServerCoverage) { + unique_fd test_srv(tipc_connect(TIPC_DEV, TEST_SRV_PORT)); + ASSERT_GE(test_srv, 0); + + uint32_t mask = (uint32_t)-1; + uint32_t magic = 0xdeadbeef; + uint64_t high_watermark = 0; + + for (size_t i = 1; i < sizeof(magic) * 8; i++) { + /* Reset coverage */ + record_->Reset(); + + /* Send message to test server */ + uint32_t msg = magic & ~(mask << i); + int rc = write(test_srv, &msg, sizeof(msg)); + ASSERT_EQ(rc, sizeof(msg)); + + /* Read message from test server */ + rc = read(test_srv, &msg, sizeof(msg)); + ASSERT_EQ(rc, sizeof(msg)); + + /* Count number of non-unique blocks executed */ + auto counter = record_->CountEdges(); + /* Each consecutive input should exercise more or same blocks */ + ASSERT_GE(counter, high_watermark); + high_watermark = counter; + } + + ASSERT_GT(high_watermark, 0); +} + +} // namespace coverage +} // namespace trusty +} // namespace android diff --git a/trusty/coverage/include/trusty/coverage/coverage.h b/trusty/coverage/include/trusty/coverage/coverage.h new file mode 100644 index 000000000..b61b95930 --- /dev/null +++ b/trusty/coverage/include/trusty/coverage/coverage.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * 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. + */ + +#pragma once + +#include + +#include +#include +#include +#include + +namespace android { +namespace trusty { +namespace coverage { + +using android::base::Result; +using android::base::unique_fd; + +class CoverageRecord { + public: + CoverageRecord(std::string tipc_dev, struct uuid* uuid); + ~CoverageRecord(); + Result Open(); + void Reset(); + void GetRawData(volatile void** begin, volatile void** end); + uint64_t CountEdges(); + + private: + Result Rpc(coverage_client_req* req, int req_fd, coverage_client_resp* resp); + + std::string tipc_dev_; + unique_fd coverage_srv_fd_; + struct uuid uuid_; + size_t record_len_; + volatile void* shm_; + size_t shm_len_; +}; + +} // namespace coverage +} // namespace trusty +} // namespace android diff --git a/trusty/coverage/include/trusty/coverage/tipc.h b/trusty/coverage/include/trusty/coverage/tipc.h new file mode 100644 index 000000000..c4157c4d6 --- /dev/null +++ b/trusty/coverage/include/trusty/coverage/tipc.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * 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. + */ + +/* This file needs to be kept in-sync with it's counterpart on Trusty side */ + +#pragma once + +#include + +#define COVERAGE_CLIENT_PORT "com.android.trusty.coverage.client" + +struct uuid { + uint32_t time_low; + uint16_t time_mid; + uint16_t time_hi_and_version; + uint8_t clock_seq_and_node[8]; +}; + +enum coverage_client_cmd { + COVERAGE_CLIENT_CMD_RESP_BIT = 1U, + COVERAGE_CLIENT_CMD_SHIFT = 1U, + COVERAGE_CLIENT_CMD_OPEN = (1U << COVERAGE_CLIENT_CMD_SHIFT), + COVERAGE_CLIENT_CMD_SHARE_RECORD = (2U << COVERAGE_CLIENT_CMD_SHIFT), +}; + +struct coverage_client_hdr { + uint32_t cmd; +}; + +struct coverage_client_open_req { + struct uuid uuid; +}; + +struct coverage_client_open_resp { + uint32_t record_len; +}; + +struct coverage_client_share_record_req { + uint32_t shm_len; +}; + +struct coverage_client_req { + struct coverage_client_hdr hdr; + union { + struct coverage_client_open_req open_args; + struct coverage_client_share_record_req share_record_args; + }; +}; + +struct coverage_client_resp { + struct coverage_client_hdr hdr; + union { + struct coverage_client_open_resp open_args; + }; +}; diff --git a/trusty/fuzz/Android.bp b/trusty/fuzz/Android.bp index ac49751ea..22d834dda 100644 --- a/trusty/fuzz/Android.bp +++ b/trusty/fuzz/Android.bp @@ -15,6 +15,7 @@ cc_defaults { name: "trusty_fuzzer_defaults", shared_libs: [ + "libtrusty_coverage", "libtrusty_fuzz_utils", "libbase", "liblog", @@ -31,9 +32,16 @@ cc_defaults { cc_library { name: "libtrusty_fuzz_utils", - srcs: ["utils.cpp"], + srcs: [ + "counters.cpp", + "utils.cpp", + ], export_include_dirs: ["include"], + static_libs: [ + "libFuzzer", + ], shared_libs: [ + "libtrusty_coverage", "libtrusty_test", "libbase", "liblog", diff --git a/trusty/fuzz/counters.cpp b/trusty/fuzz/counters.cpp new file mode 100644 index 000000000..3fc9f48e7 --- /dev/null +++ b/trusty/fuzz/counters.cpp @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2020 The Android Open Sourete Project + * + * 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. + */ + +#define LOG_TAG "trusty-fuzz-counters" + +#include + +#include + +#include +#include +#include + +using android::base::ErrnoError; +using android::base::Error; +using android::base::Result; + +/* + * We don't know how many counters the coverage record will contain. So, eyeball + * the size of this section. + */ +__attribute__((section("__libfuzzer_extra_counters"))) volatile uint8_t counters[PAGE_SIZE]; + +namespace android { +namespace trusty { +namespace fuzz { + +ExtraCounters::ExtraCounters(coverage::CoverageRecord* record) : record_(record) { + assert(fuzzer::ExtraCountersBegin()); + assert(fuzzer::ExtraCountersEnd()); + + uint8_t* begin = NULL; + uint8_t* end = NULL; + record_->GetRawData((volatile void**)&begin, (volatile void**)&end); + assert(end - begin <= sizeof(counters)); +} + +ExtraCounters::~ExtraCounters() { + Flush(); +} + +void ExtraCounters::Reset() { + record_->Reset(); + fuzzer::ClearExtraCounters(); +} + +void ExtraCounters::Flush() { + volatile uint8_t* begin = NULL; + volatile uint8_t* end = NULL; + + record_->GetRawData((volatile void**)&begin, (volatile void**)&end); + + size_t num_counters = end - begin; + for (size_t i = 0; i < num_counters; i++) { + *(counters + i) = *(begin + i); + } +} + +} // namespace fuzz +} // namespace trusty +} // namespace android diff --git a/trusty/fuzz/include/trusty/fuzz/counters.h b/trusty/fuzz/include/trusty/fuzz/counters.h new file mode 100644 index 000000000..db933d9b6 --- /dev/null +++ b/trusty/fuzz/include/trusty/fuzz/counters.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * 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. + */ + +#pragma once + +#include + +#include +#include + +namespace android { +namespace trusty { +namespace fuzz { + +class ExtraCounters { + public: + ExtraCounters(coverage::CoverageRecord* record); + ~ExtraCounters(); + + void Reset(); + void Flush(); + + private: + coverage::CoverageRecord* record_; +}; + +} // namespace fuzz +} // namespace trusty +} // namespace android diff --git a/trusty/fuzz/test/Android.bp b/trusty/fuzz/test/Android.bp new file mode 100644 index 000000000..66e103d86 --- /dev/null +++ b/trusty/fuzz/test/Android.bp @@ -0,0 +1,19 @@ +// Copyright (C) 2020 The Android Open Source Project +// +// 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. + +cc_fuzz { + name: "trusty_test_fuzzer", + defaults: ["trusty_fuzzer_defaults"], + srcs: ["fuzz.cpp"], +} diff --git a/trusty/fuzz/test/fuzz.cpp b/trusty/fuzz/test/fuzz.cpp new file mode 100644 index 000000000..28bb3f7a8 --- /dev/null +++ b/trusty/fuzz/test/fuzz.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * 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. + */ + +#undef NDEBUG + +#include +#include +#include +#include +#include +#include +#include + +using android::trusty::coverage::CoverageRecord; +using android::trusty::fuzz::ExtraCounters; +using android::trusty::fuzz::TrustyApp; + +#define TIPC_DEV "/dev/trusty-ipc-dev0" +#define TEST_SRV_PORT "com.android.trusty.sancov.test.srv" + +/* Test server's UUID is 77f68803-c514-43ba-bdce-3254531c3d24 */ +static struct uuid test_srv_uuid = { + 0x77f68803, + 0xc514, + 0x43ba, + {0xbd, 0xce, 0x32, 0x54, 0x53, 0x1c, 0x3d, 0x24}, +}; + +static CoverageRecord record(TIPC_DEV, &test_srv_uuid); + +extern "C" int LLVMFuzzerInitialize(int* /* argc */, char*** /* argv */) { + auto ret = record.Open(); + assert(ret.ok()); + return 0; +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + static uint8_t buf[TIPC_MAX_MSG_SIZE]; + + ExtraCounters counters(&record); + counters.Reset(); + + TrustyApp ta(TIPC_DEV, TEST_SRV_PORT); + auto ret = ta.Connect(); + if (!ret.ok()) { + android::trusty::fuzz::Abort(); + } + + /* Send message to test server */ + ret = ta.Write(data, size); + if (!ret.ok()) { + return -1; + } + + /* Read message from test server */ + ret = ta.Read(&buf, sizeof(buf)); + if (!ret.ok()) { + return -1; + } + + return 0; +} diff --git a/trusty/gatekeeper/fuzz/fuzz.cpp b/trusty/gatekeeper/fuzz/fuzz.cpp index f8ec93131..c0e8abb0c 100644 --- a/trusty/gatekeeper/fuzz/fuzz.cpp +++ b/trusty/gatekeeper/fuzz/fuzz.cpp @@ -19,22 +19,42 @@ #include #include #include +#include +#include #include #include +using android::trusty::coverage::CoverageRecord; +using android::trusty::fuzz::ExtraCounters; +using android::trusty::fuzz::TrustyApp; + #define TIPC_DEV "/dev/trusty-ipc-dev0" #define GATEKEEPER_PORT "com.android.trusty.gatekeeper" +/* Gatekeeper TA's UUID is 38ba0cdc-df0e-11e4-9869-233fb6ae4795 */ +static struct uuid gatekeeper_uuid = { + 0x38ba0cdc, + 0xdf0e, + 0x11e4, + {0x98, 0x69, 0x23, 0x3f, 0xb6, 0xae, 0x47, 0x95}, +}; + +static CoverageRecord record(TIPC_DEV, &gatekeeper_uuid); + +extern "C" int LLVMFuzzerInitialize(int* /* argc */, char*** /* argv */) { + auto ret = record.Open(); + assert(ret.ok()); + return 0; +} + extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { static uint8_t buf[TIPC_MAX_MSG_SIZE]; - android::trusty::fuzz::TrustyApp ta(TIPC_DEV, GATEKEEPER_PORT); + ExtraCounters counters(&record); + counters.Reset(); + android::trusty::fuzz::TrustyApp ta(TIPC_DEV, GATEKEEPER_PORT); auto ret = ta.Connect(); - /* - * If we can't connect, then assume TA crashed. - * TODO: Get some more info, e.g. stacks, to help Haiku dedup crashes. - */ if (!ret.ok()) { android::trusty::fuzz::Abort(); }