pinweaver: add fuzzer
Fuzzing result of pinweaver.c:
Regions: 735
Missed Regions: 9
Cover: 98.78%
Functions: 44
Missed Functions: 0
Executed: 100.00%
Lines: 1311
Missed Lines: 13
Cover: 99.01%
BUG=b:195016396
TEST=run fuzzer
Change-Id: I7835f82a97c20fa381a9e121fb5e1ff80b09b499
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/pinweaver/+/3070728
Tested-by: Yi Chou <yich@google.com>
Reviewed-by: Leo Lai <cylai@google.com>
Reviewed-by: Andrey Pronin <apronin@chromium.org>
Commit-Queue: Yi Chou <yich@google.com>
Auto-Submit: Yi Chou <yich@google.com>
diff --git a/fuzzer/.gitignore b/fuzzer/.gitignore
new file mode 100644
index 0000000..dc84959
--- /dev/null
+++ b/fuzzer/.gitignore
@@ -0,0 +1,2 @@
+build/
+
diff --git a/fuzzer/Makefile b/fuzzer/Makefile
new file mode 100644
index 0000000..db30782
--- /dev/null
+++ b/fuzzer/Makefile
@@ -0,0 +1,96 @@
+# Copyright 2021 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+ifneq ($(V),)
+ Q :=
+else
+ Q := @
+endif
+
+# we need clang because gtest is built with clang
+CC = clang
+CXX = clang++
+MKDIR ?= mkdir
+
+BUILD_PATH = $(abspath $(CURDIR)/build)
+TOP_PATH = $(abspath $(CURDIR)/..)
+TPM_STORAGE_PATH = $(TOP_PATH)/eal/tpm_storage
+TEST_PATH = $(TPM_STORAGE_PATH)/test
+CORPUS_PATH = $(BUILD_PATH)/corpus
+LOG_PATH = $(BUILD_PATH)/log
+
+TARGET = $(BUILD_PATH)/pinweaver_test
+TARGET_PROFRAW = $(TARGET).profraw
+TARGET_PROFDATA = $(TARGET).profdata
+COV_REPORT = $(BUILD_PATH)/coverage_report.html
+TESTED_SRCS = pinweaver.c
+SRCS = $(TESTED_SRCS) pinweaver_eal_fuzzed.cc pinweaver_fuzzer.cc \
+ tpm_storage_fuzzed.cc
+TESTED_OBJS = $(TESTED_SRCS:%.c=$(BUILD_PATH)/%.o)
+OBJS = $(filter %.o,$(SRCS:%.c=$(BUILD_PATH)/%.o))
+OBJS += $(filter %.o,$(SRCS:%.cc=$(BUILD_PATH)/%.o))
+INCLUDES = -I$(TPM_STORAGE_PATH) -I$(TOP_PATH)
+FUZZER_FLAGS = -fsanitize=fuzzer,address,undefined -fprofile-instr-generate \
+ -fcoverage-mapping
+OPTIONS = -g -O3 $(FUZZER_FLAGS)
+CXXOPTIONS = $(OPTIONS) -std=c++14
+LDFLAGS += $(FUZZER_FLAGS)
+LIBS = -lgcov -lstdc++ -lssl -lcrypto
+DEPS = Makefile
+
+.PHONY: all build clean cov run
+.DEFAULT: all
+
+all: run cov
+
+build: $(TARGET)
+
+clean:
+ $(RM) -rf $(BUILD_PATH)
+
+repro: $(LOG_PATH) $(TARGET)
+ LLVM_PROFILE_FILE=/tmp/default.profraw ASAN_OPTIONS=log_path=$(LOG_PATH) \
+ $(TARGET) $(BUILD_PATH)/crash*
+
+cov: $(COV_REPORT)
+ $(Q)llvm-cov report $(TESTED_OBJS) --instr-profile=$(TARGET_PROFDATA) \
+ --ignore-filename-regex=".*\.h"
+
+$(TARGET): $(BUILD_PATH) $(OBJS)
+ @echo " BIN $@"
+ $(Q)$(CC) -o $@ $(OBJS) -L/usr/lib64 $(LDFLAGS) -L$(BUILD_PATH) $(LIBS)
+
+$(COV_REPORT): $(TARGET) $(TARGET_PROFDATA)
+ $(Q)llvm-cov show $(TESTED_OBJS) --instr-profile=$(TARGET_PROFDATA) \
+ --format=html > $(COV_REPORT)
+
+$(TARGET_PROFDATA): $(TARGET_PROFRAW)
+ $(Q)llvm-profdata merge -sparse $(TARGET_PROFRAW) -o $(TARGET_PROFDATA)
+
+.PRECIOUS: $(TARGET_PROFRAW)
+
+run: $(CORPUS_PATH) $(LOG_PATH) $(TARGET)
+ $(Q)LLVM_PROFILE_FILE=$(TARGET_PROFRAW) ASAN_OPTIONS=log_path=$(LOG_PATH) \
+ $(TARGET) $(CORPUS_PATH) -artifact_prefix=$(BUILD_PATH)/ -timeout=10 \
+ -ignore_ooms=false -ignore_timeouts=false -use_value_profile=true \
+ -detect_leaks=1 -fork=1
+
+$(CORPUS_PATH):
+ $(Q)$(MKDIR) -p $@
+
+$(LOG_PATH):
+ $(Q)$(MKDIR) -p $@
+
+$(BUILD_PATH):
+ $(Q)$(MKDIR) -p $@
+
+vpath %.c $(TOP_PATH)
+
+$(BUILD_PATH)/%.o: %.c $(DEPS)
+ @echo " CC $@"
+ $(Q)$(CC) -c $< -o $@ $(CFLAGS) $(OPTIONS) $(INCLUDES)
+
+$(BUILD_PATH)/%.o: %.cc $(DEPS)
+ @echo " CPP $@"
+ $(Q)$(CXX) -c -o $@ $< $(CXXFLAGS) $(CXXOPTIONS) $(INCLUDES)
diff --git a/fuzzer/fuzzer_provider.h b/fuzzer/fuzzer_provider.h
new file mode 100644
index 0000000..18c6864
--- /dev/null
+++ b/fuzzer/fuzzer_provider.h
@@ -0,0 +1,12 @@
+/* Copyright 2021 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <fuzzer/FuzzedDataProvider.h>
+
+#include <memory>
+
+extern std::unique_ptr<FuzzedDataProvider> g_data_provider;
+
+extern uint64_t g_time_base;
\ No newline at end of file
diff --git a/fuzzer/pinweaver_eal_fuzzed.cc b/fuzzer/pinweaver_eal_fuzzed.cc
new file mode 100644
index 0000000..72172fe
--- /dev/null
+++ b/fuzzer/pinweaver_eal_fuzzed.cc
@@ -0,0 +1,218 @@
+/* Copyright 2021 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <openssl/bn.h>
+#include <openssl/ec.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/rand.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "pinweaver_eal.h"
+#include "pinweaver_eal_tpm.h"
+#include "fuzzer_provider.h"
+
+uint64_t g_time_base = 0;
+
+extern "C" {
+int pinweaver_eal_safe_memcmp(const void *s1, const void *s2, size_t len)
+{
+ const uint8_t *us1 = static_cast<const uint8_t *>(s1);
+ const uint8_t *us2 = static_cast<const uint8_t *>(s2);
+ int result = 0;
+
+ while (len--)
+ result ^= *us1++ ^ *us2++;
+
+ return result & 1;
+}
+
+int pinweaver_eal_rand_bytes(void *buf, size_t size)
+{
+ if (g_data_provider->ConsumeBool())
+ return -1;
+ auto bytes = g_data_provider->ConsumeBytes<char>(size);
+ bytes.resize(size);
+ memcpy(buf, bytes.data(), size);
+ return 0;
+}
+
+uint64_t pinweaver_eal_seconds_since_boot()
+{
+ if (g_data_provider->ConsumeIntegralInRange<char>(0, 7) == 0)
+ g_time_base +=
+ g_data_provider->ConsumeIntegralInRange<uint64_t>(0,
+ 3600);
+ return g_time_base;
+}
+
+int pinweaver_eal_sha256_init(pinweaver_eal_sha256_ctx_t *ctx)
+{
+ if (g_data_provider->ConsumeBool())
+ return -1;
+
+ int rv = SHA256_Init(ctx);
+ if (rv != 1) {
+ PINWEAVER_EAL_INFO("SHA256_Init failed: %d", rv);
+ }
+ return rv == 1 ? 0 : -1;
+}
+
+int pinweaver_eal_sha256_update(pinweaver_eal_sha256_ctx_t *ctx,
+ const void *data, size_t size)
+{
+ if (g_data_provider->ConsumeBool())
+ return -1;
+
+ int rv = SHA256_Update(ctx, data, size);
+ if (rv != 1) {
+ PINWEAVER_EAL_INFO("SHA256_Update failed: %d", rv);
+ }
+ return rv == 1 ? 0 : -1;
+}
+
+int pinweaver_eal_sha256_final(pinweaver_eal_sha256_ctx_t *ctx, void *res)
+{
+ if (g_data_provider->ConsumeBool())
+ return -1;
+
+ int rv = SHA256_Final((unsigned char *)res, ctx);
+ if (rv != 1) {
+ PINWEAVER_EAL_INFO("SHA256_Final failed: %d", rv);
+ }
+ return rv == 1 ? 0 : -1;
+}
+
+int pinweaver_eal_hmac_sha256_init(pinweaver_eal_hmac_sha256_ctx_t *ctx,
+ const void *key,
+ size_t key_size /* in bytes */)
+{
+ if (g_data_provider->ConsumeBool())
+ return -1;
+
+ *ctx = HMAC_CTX_new();
+ if (!*ctx) {
+ PINWEAVER_EAL_INFO("HMAC_CTX_new failed");
+ return -1;
+ }
+ int rv = HMAC_Init_ex(*ctx, key, key_size, EVP_sha256(), NULL);
+ if (rv != 1) {
+ PINWEAVER_EAL_INFO("HMAC_Init_ex failed: %d", rv);
+ }
+ return rv == 1 ? 0 : -1;
+}
+
+int pinweaver_eal_hmac_sha256_update(pinweaver_eal_hmac_sha256_ctx_t *ctx,
+ const void *data, size_t size)
+{
+ if (g_data_provider->ConsumeBool())
+ return -1;
+
+ int rv = HMAC_Update(*ctx, (const unsigned char *)data, size);
+ if (rv != 1) {
+ PINWEAVER_EAL_INFO("HMAC_Update failed: %d", rv);
+ }
+ return rv == 1 ? 0 : -1;
+}
+
+int pinweaver_eal_hmac_sha256_final(pinweaver_eal_hmac_sha256_ctx_t *ctx,
+ void *res)
+{
+ unsigned int len;
+ // Free the memory to prevent leaking memory.
+ int rv = HMAC_Final(*ctx, (unsigned char *)res, &len);
+ HMAC_CTX_free(*ctx);
+ *ctx = NULL;
+ if (rv != 1) {
+ PINWEAVER_EAL_INFO("HMAC_Final failed: %d", rv);
+ }
+ if (g_data_provider->ConsumeBool()) {
+ return -1;
+ }
+ return rv == 1 ? 0 : -1;
+}
+
+int pinweaver_eal_aes256_ctr(const void *key, size_t key_size, /* in
+ bytes
+ */
+ const void *iv, const void *data, size_t size,
+ void *res)
+{
+ EVP_CIPHER_CTX *ctx;
+ int rv;
+ int len, len_final;
+
+ if (g_data_provider->ConsumeBool())
+ return -1;
+
+ if (key_size != 256 / 8)
+ return -1;
+ ctx = EVP_CIPHER_CTX_new();
+ if (!ctx)
+ return -1;
+ rv = EVP_EncryptInit(ctx, EVP_aes_256_ctr(), (const unsigned char *)key,
+ (const unsigned char *)iv);
+ if (rv != 1)
+ goto out;
+ rv = EVP_EncryptUpdate(ctx, (unsigned char *)res, &len,
+ (const unsigned char *)data, size);
+ if (rv != 1)
+ goto out;
+ rv = EVP_EncryptFinal(ctx, (unsigned char *)res + len, &len_final);
+out:
+ EVP_CIPHER_CTX_free(ctx);
+ return rv == 1 ? 0 : -1;
+}
+
+uint8_t pinweaver_eal_get_current_pcr_digest(const uint8_t bitmask[2],
+ uint8_t sha256_of_selected_pcr[32])
+{
+ if (g_data_provider->ConsumeBool())
+ return -1;
+
+ uint8_t pcr_value[SHA256_DIGEST_SIZE];
+ /* TODO */
+ memset(pcr_value, 0, 32);
+
+ pinweaver_eal_sha256_ctx_t ctx;
+ if (pinweaver_eal_sha256_init(&ctx))
+ return -1;
+ if (pinweaver_eal_sha256_update(&ctx, pcr_value, sizeof(pcr_value))) {
+ pinweaver_eal_sha256_final(&ctx, sha256_of_selected_pcr);
+ return -1;
+ }
+ return pinweaver_eal_sha256_final(&ctx, sha256_of_selected_pcr);
+}
+
+int pinweaver_eal_memcpy_s(void *dest, size_t destsz, const void *src,
+ size_t count)
+{
+ if (g_data_provider->ConsumeBool()) {
+ return -1;
+ }
+
+ if (count == 0) {
+ return 0;
+ }
+
+ if (dest == NULL) {
+ abort();
+ }
+
+ if (src == NULL) {
+ memset(dest, 0, destsz);
+ abort();
+ }
+
+ if (destsz < count) {
+ memset(dest, 0, destsz);
+ abort();
+ }
+
+ memcpy(dest, src, count);
+ return 0;
+}
+}
\ No newline at end of file
diff --git a/fuzzer/pinweaver_fuzzer.cc b/fuzzer/pinweaver_fuzzer.cc
new file mode 100644
index 0000000..f8b349a
--- /dev/null
+++ b/fuzzer/pinweaver_fuzzer.cc
@@ -0,0 +1,44 @@
+/* Copyright 2021 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <memory>
+#include <utility>
+
+#include "fuzzer_provider.h"
+#include "pinweaver.h"
+
+std::unique_ptr<FuzzedDataProvider> g_data_provider;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ g_data_provider = std::make_unique<FuzzedDataProvider>(data, size);
+ g_time_base = 0;
+ if (g_data_provider->ConsumeBool())
+ force_restart_count(
+ g_data_provider->ConsumeIntegralInRange<int>(1, 30));
+ pinweaver_init();
+ while (g_data_provider->remaining_bytes()) {
+ int req_sz = sizeof(struct pw_request_t) + PW_MAX_MESSAGE_SIZE;
+ auto request = g_data_provider->ConsumeBytes<char>(req_sz);
+ request.resize(req_sz);
+
+ struct pw_request_t *pw_request =
+ static_cast<struct pw_request_t *>(
+ static_cast<void *>(request.data()));
+ pw_request->header.data_length =
+ g_data_provider->ConsumeIntegralInRange<size_t>(
+ 0, PW_MAX_MESSAGE_SIZE);
+
+ size_t request_size = sizeof(pw_request->header) +
+ pw_request->header.data_length;
+
+ std::vector<char> response(req_sz);
+ size_t response_size;
+
+ pinweaver_command(request.data(), request_size, response.data(),
+ &response_size);
+ }
+ return 0;
+}
diff --git a/fuzzer/tpm_storage_fuzzed.cc b/fuzzer/tpm_storage_fuzzed.cc
new file mode 100644
index 0000000..687504c
--- /dev/null
+++ b/fuzzer/tpm_storage_fuzzed.cc
@@ -0,0 +1,98 @@
+/* Copyright 2021 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <stdbool.h>
+#include <string.h>
+
+#include "fuzzer_provider.h"
+#include "pinweaver_eal.h"
+
+extern "C" {
+
+int pinweaver_eal_derive_keys(struct merkle_tree_t *merkle_tree)
+{
+ auto wrap_key = g_data_provider->ConsumeBytes<char>(
+ sizeof(merkle_tree->wrap_key));
+ wrap_key.resize(sizeof(merkle_tree->wrap_key));
+ memcpy(merkle_tree->wrap_key, wrap_key.data(),
+ sizeof(merkle_tree->wrap_key));
+
+ auto hmac_key = g_data_provider->ConsumeBytes<char>(
+ sizeof(merkle_tree->hmac_key));
+ hmac_key.resize(sizeof(merkle_tree->hmac_key));
+ memcpy(merkle_tree->hmac_key, hmac_key.data(),
+ sizeof(merkle_tree->hmac_key));
+ if (g_data_provider->ConsumeBool())
+ return g_data_provider->ConsumeIntegral<int>();
+ return 0;
+}
+
+int pinweaver_eal_storage_start()
+{
+ if (g_data_provider->ConsumeBool())
+ return g_data_provider->ConsumeIntegral<int>();
+ return 0;
+}
+
+int pinweaver_eal_storage_init_state(uint8_t *root_hash,
+ uint32_t *restart_count)
+{
+ auto rh = g_data_provider->ConsumeBytes<char>(PW_HASH_SIZE);
+ rh.resize(PW_HASH_SIZE);
+ memcpy(root_hash, rh.data(), PW_HASH_SIZE);
+
+ *restart_count = g_data_provider->ConsumeIntegral<uint32_t>();
+
+ if (g_data_provider->ConsumeBool())
+ return g_data_provider->ConsumeIntegral<int>();
+ return 0;
+}
+
+int pinweaver_eal_storage_get_log(struct pw_log_storage_t *dest)
+{
+ auto log = g_data_provider->ConsumeBytes<char>(
+ sizeof(struct pw_log_storage_t));
+ log.resize(sizeof(struct pw_log_storage_t));
+ memcpy(dest, log.data(), sizeof(struct pw_log_storage_t));
+
+ if (g_data_provider->ConsumeBool())
+ return g_data_provider->ConsumeIntegral<int>();
+ return 0;
+}
+
+int pinweaver_eal_storage_set_log(const struct pw_log_storage_t *log)
+{
+ if (g_data_provider->ConsumeBool())
+ return g_data_provider->ConsumeIntegral<int>();
+ return 0;
+}
+
+int pinweaver_eal_storage_get_tree_data(struct pw_long_term_storage_t *dest)
+{
+ auto tree = g_data_provider->ConsumeBytes<char>(
+ sizeof(struct pw_long_term_storage_t));
+ tree.resize(sizeof(struct pw_long_term_storage_t));
+ memcpy(dest, tree.data(), sizeof(struct pw_long_term_storage_t));
+
+ if (g_data_provider->ConsumeBool())
+ return g_data_provider->ConsumeIntegral<int>();
+ return 0;
+}
+
+int pinweaver_eal_storage_set_tree_data(
+ const struct pw_long_term_storage_t *data)
+{
+ if (g_data_provider->ConsumeBool())
+ return g_data_provider->ConsumeIntegral<int>();
+ return 0;
+}
+
+int pinweaver_eal_storage_initialize_owner()
+{
+ if (g_data_provider->ConsumeBool())
+ return g_data_provider->ConsumeIntegral<int>();
+ return 0;
+}
+}
\ No newline at end of file