Updates to fuzz tests
This CL updates the fuzz tests as well as some changes to documentation. A few OEMCrypto enumerations were updated to include "test only" values to help the automated fuzz tools. This should make it easier for partners to run fuzz testing on their own implementations. Bug: 186785830
This commit is contained in:
@@ -379,6 +379,7 @@ typedef struct {
|
||||
typedef enum OEMCrypto_Algorithm {
|
||||
OEMCrypto_AES_CBC_128_NO_PADDING = 0,
|
||||
OEMCrypto_HMAC_SHA256 = 1,
|
||||
OEMCrypto_Algorithm_MaxValue = 1,
|
||||
} OEMCrypto_Algorithm;
|
||||
|
||||
/// @}
|
||||
|
||||
@@ -116,7 +116,7 @@ typedef enum OEMCrypto_Usage_Entry_Status {
|
||||
typedef enum OEMCrypto_LicenseType {
|
||||
OEMCrypto_ContentLicense = 0,
|
||||
OEMCrypto_EntitlementLicense = 1,
|
||||
OEMCrypto_LicenstType_MaxValue = OEMCrypto_EntitlementLicense,
|
||||
OEMCrypto_LicenseType_MaxValue = OEMCrypto_EntitlementLicense,
|
||||
} OEMCrypto_LicenseType;
|
||||
|
||||
/* Private key type used in the provisioning response. */
|
||||
|
||||
@@ -1,326 +0,0 @@
|
||||
/* Copyright 2019 Google LLC. All rights reserved. This file and proprietary
|
||||
* source code may only be used and distributed under the Widevine Master
|
||||
* License Agreement.
|
||||
*/
|
||||
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "OEMCryptoCENCCommon.h"
|
||||
#include "core_message_deserialize.h"
|
||||
#include "core_message_serialize.h"
|
||||
#include "core_message_types.h"
|
||||
#include "odk.h"
|
||||
#include "odk_serialize.h"
|
||||
#include "odk_structs.h"
|
||||
#include "odk_structs_priv.h"
|
||||
|
||||
typedef std::function<void(const uint8_t*, uint8_t*, size_t, size_t)>
|
||||
roundtrip_fun;
|
||||
|
||||
using oemcrypto_core_message::ODK_LicenseRequest;
|
||||
using oemcrypto_core_message::ODK_ProvisioningRequest;
|
||||
using oemcrypto_core_message::ODK_RenewalRequest;
|
||||
|
||||
using oemcrypto_core_message::deserialize::CoreLicenseRequestFromMessage;
|
||||
using oemcrypto_core_message::deserialize::CoreProvisioningRequestFromMessage;
|
||||
using oemcrypto_core_message::deserialize::CoreRenewalRequestFromMessage;
|
||||
|
||||
using oemcrypto_core_message::serialize::CreateCoreLicenseResponse;
|
||||
using oemcrypto_core_message::serialize::CreateCoreProvisioningResponse;
|
||||
using oemcrypto_core_message::serialize::CreateCoreRenewalResponse;
|
||||
|
||||
// @ kdo deserialize; odk serialize
|
||||
static OEMCryptoResult odk_serialize_LicenseRequest(
|
||||
const void* in, uint8_t* out, size_t* size,
|
||||
const ODK_LicenseRequest& core_license_request,
|
||||
const ODK_NonceValues* nonce_values) {
|
||||
return ODK_PrepareCoreLicenseRequest(out, SIZE_MAX, size, nonce_values);
|
||||
}
|
||||
|
||||
static OEMCryptoResult odk_serialize_RenewalRequest(
|
||||
const void* in, uint8_t* out, size_t* size,
|
||||
const ODK_RenewalRequest& core_renewal, ODK_NonceValues* nonce_values) {
|
||||
ODK_ClockValues clock{};
|
||||
memcpy(&clock, in, sizeof(ODK_ClockValues));
|
||||
uint64_t system_time_seconds = core_renewal.playback_time_seconds;
|
||||
return ODK_PrepareCoreRenewalRequest(out, SIZE_MAX, size, nonce_values,
|
||||
&clock, system_time_seconds);
|
||||
}
|
||||
|
||||
static OEMCryptoResult odk_serialize_ProvisioningRequest(
|
||||
const void* in, uint8_t* out, size_t* size,
|
||||
const ODK_ProvisioningRequest& core_provisioning,
|
||||
const ODK_NonceValues* nonce_values) {
|
||||
const std::string& device_id = core_provisioning.device_id;
|
||||
return ODK_PrepareCoreProvisioningRequest(
|
||||
out, SIZE_MAX, size, nonce_values,
|
||||
reinterpret_cast<const uint8_t*>(device_id.data()), device_id.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Template arguments:
|
||||
* T: kdo deserialize output/odk serialize input structure
|
||||
* F: kdo deserialize function
|
||||
* G: odk serialize function
|
||||
*
|
||||
* raw bytes -> F deserialize -> struct T -> G serialize -> raw bytes
|
||||
*/
|
||||
template <typename T, typename F, typename G>
|
||||
static roundtrip_fun kdo_odk(const F& kdo_fun, const G& odk_fun) {
|
||||
auto roundtrip = [&](const uint8_t* in, uint8_t* out, size_t size,
|
||||
size_t clock_value_size) -> void {
|
||||
if (size <= clock_value_size) {
|
||||
return;
|
||||
}
|
||||
// Input byte array format: [Clock Values][data to parse]
|
||||
std::string input(reinterpret_cast<const char*>(in) + clock_value_size,
|
||||
size - clock_value_size);
|
||||
T t = {};
|
||||
if (!kdo_fun(input, &t)) {
|
||||
return;
|
||||
}
|
||||
ODK_NonceValues nonce_values = {t.api_minor_version, t.api_major_version,
|
||||
t.nonce, t.session_id};
|
||||
OEMCryptoResult err = odk_fun(in, out, &size, t, &nonce_values);
|
||||
if (OEMCrypto_SUCCESS != err) {
|
||||
return;
|
||||
}
|
||||
assert(0 == memcmp(in + clock_value_size, out, size));
|
||||
};
|
||||
return roundtrip;
|
||||
}
|
||||
|
||||
// @ odk deserialize; kdo serialize
|
||||
namespace {
|
||||
struct ODK_ParseLicense_Args {
|
||||
ODK_NonceValues nonce_values;
|
||||
uint8_t initial_license_load;
|
||||
uint8_t usage_entry_present;
|
||||
uint8_t request_hash[32];
|
||||
ODK_TimerLimits timer_limits;
|
||||
ODK_ClockValues clock_values;
|
||||
};
|
||||
struct ODK_ParseRenewal_Args {
|
||||
ODK_NonceValues nonce_values;
|
||||
uint64_t system_time;
|
||||
ODK_TimerLimits timer_limits;
|
||||
ODK_ClockValues clock_values;
|
||||
};
|
||||
struct ODK_ParseProvisioning_Args {
|
||||
ODK_NonceValues nonce_values;
|
||||
size_t device_id_length;
|
||||
uint8_t device_id[64];
|
||||
};
|
||||
} // namespace
|
||||
|
||||
bool convert_byte_to_valid_boolean(const bool* in) {
|
||||
const int value = *reinterpret_cast<const int*>(in);
|
||||
return value != 0;
|
||||
}
|
||||
|
||||
static OEMCryptoResult odk_deserialize_LicenseResponse(
|
||||
const uint8_t* message, size_t core_message_length,
|
||||
ODK_ParseLicense_Args* a, ODK_NonceValues* nonce_values,
|
||||
ODK_ParsedLicense* parsed_lic) {
|
||||
return ODK_ParseLicense(message, SIZE_MAX, core_message_length,
|
||||
static_cast<bool>(a->initial_license_load),
|
||||
static_cast<bool>(a->usage_entry_present),
|
||||
a->request_hash, &a->timer_limits, &a->clock_values,
|
||||
nonce_values, parsed_lic);
|
||||
}
|
||||
|
||||
static bool kdo_serialize_LicenseResponse(const ODK_ParseLicense_Args* args,
|
||||
const ODK_ParsedLicense& parsed_lic,
|
||||
std::string* oemcrypto_core_message) {
|
||||
const auto& nonce_values = args->nonce_values;
|
||||
ODK_LicenseRequest core_request{nonce_values.api_minor_version,
|
||||
nonce_values.api_major_version,
|
||||
nonce_values.nonce, nonce_values.session_id};
|
||||
std::string core_request_sha_256(
|
||||
reinterpret_cast<const char*>(args->request_hash), 32);
|
||||
return CreateCoreLicenseResponse(
|
||||
parsed_lic, core_request, core_request_sha_256, oemcrypto_core_message);
|
||||
}
|
||||
|
||||
static OEMCryptoResult odk_deserialize_RenewalResponse(
|
||||
const uint8_t* buf, size_t len, ODK_ParseRenewal_Args* a,
|
||||
ODK_NonceValues* nonce_values, ODK_PreparedRenewalRequest* renewal_msg) {
|
||||
/* Address Sanitizer doesn't like values other than 0 OR 1 for boolean
|
||||
* variables. Input from fuzzer can be parsed and any random bytes can be
|
||||
* assigned to boolean variables. Using the workaround to mitigate sanitizer
|
||||
* errors in fuzzer code and converting random bytes to 0 OR 1.
|
||||
* This has no negative security impact*/
|
||||
a->timer_limits.soft_enforce_playback_duration =
|
||||
convert_byte_to_valid_boolean(
|
||||
&a->timer_limits.soft_enforce_playback_duration);
|
||||
a->timer_limits.soft_enforce_rental_duration = convert_byte_to_valid_boolean(
|
||||
&a->timer_limits.soft_enforce_rental_duration);
|
||||
uint64_t timer_value = 0;
|
||||
OEMCryptoResult err =
|
||||
ODK_ParseRenewal(buf, SIZE_MAX, len, nonce_values, a->system_time,
|
||||
&a->timer_limits, &a->clock_values, &timer_value);
|
||||
if (OEMCrypto_SUCCESS == err) {
|
||||
Message* msg = nullptr;
|
||||
AllocateMessage(&msg, message_block);
|
||||
InitMessage(msg, const_cast<uint8_t*>(buf), len);
|
||||
SetSize(msg, len);
|
||||
Unpack_ODK_PreparedRenewalRequest(msg, renewal_msg);
|
||||
assert(ValidMessage(msg));
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static bool kdo_serialize_RenewalResponse(
|
||||
const ODK_ParseRenewal_Args* args,
|
||||
const ODK_PreparedRenewalRequest& renewal_msg,
|
||||
std::string* oemcrypto_core_message) {
|
||||
const auto& nonce_values = args->nonce_values;
|
||||
ODK_RenewalRequest core_request{
|
||||
nonce_values.api_minor_version, nonce_values.api_major_version,
|
||||
nonce_values.nonce, nonce_values.session_id, renewal_msg.playback_time};
|
||||
return CreateCoreRenewalResponse(
|
||||
core_request, args->timer_limits.initial_renewal_duration_seconds,
|
||||
oemcrypto_core_message);
|
||||
}
|
||||
|
||||
static OEMCryptoResult odk_deserialize_ProvisioningResponse(
|
||||
const uint8_t* buf, size_t len, ODK_ParseProvisioning_Args* a,
|
||||
ODK_NonceValues* nonce_values, ODK_ParsedProvisioning* parsed_prov) {
|
||||
return ODK_ParseProvisioning(buf, SIZE_MAX, len, nonce_values, a->device_id,
|
||||
a->device_id_length, parsed_prov);
|
||||
}
|
||||
|
||||
static bool kdo_serialize_ProvisioningResponse(
|
||||
const ODK_ParseProvisioning_Args* args,
|
||||
const ODK_ParsedProvisioning& parsed_prov,
|
||||
std::string* oemcrypto_core_message) {
|
||||
const auto& nonce_values = args->nonce_values;
|
||||
if (args->device_id_length > sizeof(args->device_id)) {
|
||||
return false;
|
||||
}
|
||||
ODK_ProvisioningRequest core_request{
|
||||
nonce_values.api_minor_version, nonce_values.api_major_version,
|
||||
nonce_values.nonce, nonce_values.session_id,
|
||||
std::string(reinterpret_cast<const char*>(args->device_id),
|
||||
args->device_id_length)};
|
||||
return CreateCoreProvisioningResponse(parsed_prov, core_request,
|
||||
oemcrypto_core_message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Template arguments:
|
||||
* A: struct holding function arguments
|
||||
* T: odk deserialize output/kdo serialize input structure
|
||||
* F: odk deserialize function
|
||||
* G: kdo serialize function
|
||||
*
|
||||
* raw bytes -> F deserialize -> struct T -> G serialize -> raw bytes
|
||||
*/
|
||||
template <typename A, typename T, typename F, typename G>
|
||||
static roundtrip_fun odk_kdo(const F& odk_fun, const G& kdo_fun) {
|
||||
auto roundtrip = [&](const uint8_t* in, uint8_t* out, size_t size,
|
||||
size_t args_size) -> void {
|
||||
// Input byte array format: [function arguments][data to parse]
|
||||
if (args_size > size) {
|
||||
return;
|
||||
}
|
||||
T t = {};
|
||||
const uint8_t* buf = in + args_size;
|
||||
std::shared_ptr<A> _args(new A());
|
||||
A* args = _args.get();
|
||||
memcpy(args, in, args_size);
|
||||
args->nonce_values.api_major_version = ODK_MAJOR_VERSION;
|
||||
args->nonce_values.api_minor_version = ODK_MINOR_VERSION;
|
||||
/*
|
||||
* Input random bytes from autofuzz are interpreted by this script as
|
||||
* [function args][data to parse]. Odk deserialize functions
|
||||
* expect the nonce values in function args to match with those
|
||||
* in data to parse which is not possible with random bytes.
|
||||
* We follow two pass approach.
|
||||
*
|
||||
* 1st pass - We copy random bytes into struct t and call kdo serialize
|
||||
* with function args which will create oemcrypto core message using nonce
|
||||
* from function args. Now we have a valid oemcrypto core message which is
|
||||
* formed using nonce_values from function args which acts as input bytes
|
||||
* for 2nd pass
|
||||
*
|
||||
* 2nd pass - oemcrypto core message from 1st pass guarantees that
|
||||
* nonce_values in [function args] and core message match. we call
|
||||
* odk_deserialize using nonce from function args and oemcrypto core message
|
||||
* from 1st pass. Then we call kdo function which generates oemcrypto core
|
||||
* message2, which should be equal to oemcrypto_core_message which was input
|
||||
* to 2nd pass
|
||||
*/
|
||||
// TODO(ellurubharath): Use structure aware fuzzing
|
||||
// 1st pass
|
||||
memcpy(&t, buf, sizeof(t));
|
||||
std::string oemcrypto_core_message;
|
||||
if (!kdo_fun(args, t, &oemcrypto_core_message)) {
|
||||
return;
|
||||
}
|
||||
assert(oemcrypto_core_message.size() <= size);
|
||||
|
||||
// 2nd pass
|
||||
ODK_NonceValues nonce_values = args->nonce_values;
|
||||
OEMCryptoResult result =
|
||||
odk_fun(reinterpret_cast<const uint8_t*>(oemcrypto_core_message.data()),
|
||||
oemcrypto_core_message.size(), args, &nonce_values, &t);
|
||||
if (result != OEMCrypto_SUCCESS) {
|
||||
return;
|
||||
}
|
||||
std::string oemcrypto_core_message2;
|
||||
if (!kdo_fun(args, t, &oemcrypto_core_message2)) {
|
||||
return;
|
||||
}
|
||||
assert(oemcrypto_core_message == oemcrypto_core_message2);
|
||||
};
|
||||
return roundtrip;
|
||||
}
|
||||
|
||||
// @ fuzz raw -> parsed -> raw
|
||||
static void verify_roundtrip(const uint8_t* in, size_t size,
|
||||
roundtrip_fun roundtrip, size_t args_size) {
|
||||
std::vector<uint8_t> _out(size);
|
||||
auto out = _out.data();
|
||||
roundtrip(in, out, size, args_size);
|
||||
}
|
||||
|
||||
// Entry point for fuzzer, data is random bytes program gets from autofuzzer
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
|
||||
verify_roundtrip(data, size,
|
||||
kdo_odk<ODK_LicenseRequest>(CoreLicenseRequestFromMessage,
|
||||
odk_serialize_LicenseRequest),
|
||||
0);
|
||||
verify_roundtrip(data, size,
|
||||
kdo_odk<ODK_RenewalRequest>(CoreRenewalRequestFromMessage,
|
||||
odk_serialize_RenewalRequest),
|
||||
sizeof(ODK_ClockValues));
|
||||
verify_roundtrip(
|
||||
data, size,
|
||||
kdo_odk<ODK_ProvisioningRequest>(CoreProvisioningRequestFromMessage,
|
||||
odk_serialize_ProvisioningRequest),
|
||||
0);
|
||||
verify_roundtrip(
|
||||
data, size,
|
||||
odk_kdo<ODK_ParseLicense_Args, ODK_ParsedLicense>(
|
||||
odk_deserialize_LicenseResponse, kdo_serialize_LicenseResponse),
|
||||
sizeof(ODK_ParseLicense_Args));
|
||||
verify_roundtrip(
|
||||
data, size,
|
||||
odk_kdo<ODK_ParseRenewal_Args, ODK_PreparedRenewalRequest>(
|
||||
odk_deserialize_RenewalResponse, kdo_serialize_RenewalResponse),
|
||||
sizeof(ODK_ParseRenewal_Args));
|
||||
verify_roundtrip(data, size,
|
||||
odk_kdo<ODK_ParseProvisioning_Args, ODK_ParsedProvisioning>(
|
||||
odk_deserialize_ProvisioningResponse,
|
||||
kdo_serialize_ProvisioningResponse),
|
||||
sizeof(ODK_ParseProvisioning_Args));
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
# Copyright 2019 Google LLC. All rights reserved. This file and proprietary
|
||||
# source code may only be used and distributed under the Widevine Master License
|
||||
# Agreement.
|
||||
|
||||
{
|
||||
'targets': [
|
||||
{
|
||||
'target_name': 'odk_fuzz',
|
||||
'type': 'executable',
|
||||
'includes': [
|
||||
'../src/odk.gypi',
|
||||
'../kdo/oec_util.gypi',
|
||||
],
|
||||
'include_dirs': [
|
||||
'../../include',
|
||||
'../include',
|
||||
'../src',
|
||||
'../kdo/include',
|
||||
],
|
||||
'cflags_cc': [
|
||||
'-std=c++11',
|
||||
'-g3',
|
||||
'-O0',
|
||||
'-fsanitize=fuzzer,address,undefined',
|
||||
'-fno-omit-frame-pointer',
|
||||
],
|
||||
'ldflags': [
|
||||
'-fPIC',
|
||||
'-fsanitize=fuzzer,address,undefined',
|
||||
],
|
||||
'sources': [
|
||||
'odk_fuzz.cpp',
|
||||
],
|
||||
'dependencies': [
|
||||
'../../../cdm/cdm.gyp:license_protocol'
|
||||
],
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -177,6 +177,7 @@ void ODK_SetDefaultProvisioningResponseParams(
|
||||
memset(params->device_id + params->device_id_length, 0,
|
||||
ODK_DEVICE_ID_LEN_MAX - params->device_id_length);
|
||||
params->parsed_provisioning = {
|
||||
.key_type = OEMCrypto_RSA_Private_Key,
|
||||
.enc_private_key = {.offset = 0, .length = 1},
|
||||
.enc_private_key_iv = {.offset = 2, .length = 3},
|
||||
.encrypted_message_key = {.offset = 4, .length = 5},
|
||||
|
||||
Binary file not shown.
@@ -1 +0,0 @@
|
||||
(c020:0d112d7ea200;
|
||||
@@ -1 +0,0 @@
|
||||
(e2!0;u
|
||||
Binary file not shown.
@@ -1 +0,0 @@
|
||||
0a4c08001248000000020000101907d9ffde13aa95c122678053362136bdf8408f8276e4c2d87ec52b61aa1b9f646e58734930acebe899b3e464189a14a87202fb02570640bd22ef44b2d7e3912250a230a14080112100915007caa9b5931b76a3a85f046523e10011a09393837363534333231180120002a0c313838363738373430350000
|
||||
@@ -1 +0,0 @@
|
||||
(2dea200;u
|
||||
@@ -1 +0,0 @@
|
||||
0a4c020:0d1190d79fef02570640bd22ef44b2d7e3912250a200
|
||||
@@ -1 +0,0 @@
|
||||
e2!0;u
|
||||
@@ -1 +0,0 @@
|
||||
e
|
||||
@@ -1 +0,0 @@
|
||||
(ea200;u
|
||||
@@ -1 +0,0 @@
|
||||
0a(c020:0d112d7ea200;
|
||||
@@ -1 +0,0 @@
|
||||
0a4c000000200:0101907d9ffde02570640bd22ef44b2d7e3912250a230a1407363534333231180120002a0c313838363738373430350000
|
||||
Binary file not shown.
Binary file not shown.
@@ -1 +0,0 @@
|
||||
0a4c000000220:01019dd79fef02570640bd22ef44b2d7e3912250a200
|
||||
Binary file not shown.
@@ -1 +0,0 @@
|
||||
e;
|
||||
@@ -1 +0,0 @@
|
||||
0a4c000000200:010197d9ffde02570640bd22ef44b2d7e3912250a230a1407363534333231180120002a0c313838363738373430350000
|
||||
@@ -1 +0,0 @@
|
||||
0a4c00000020000101907d9ffde02570640bd22ef44b2d7e3912250a230a14080112100915007caa9b5931b76a3a85f046523e10011a09393837363534333231180120002a0c313838363738373430350000
|
||||
@@ -1 +0,0 @@
|
||||
0a4c020:0d112d7e3912250a200;
|
||||
@@ -1 +0,0 @@
|
||||
0a4c08001248000000020000101907d9ffde02570640bd22ef44b2d7e3912250a230a14080112100915007caa9b5931b76a3a85f046523e10011a09393837363534333231180120002a0c313838363738373430350000
|
||||
@@ -1 +0,0 @@
|
||||
)
|
||||
@@ -1 +0,0 @@
|
||||
e2;
|
||||
Binary file not shown.
@@ -1 +0,0 @@
|
||||
0a4c08001248000000020000101907d9ffde13aa95c122678053362136bdf8408f8276e4c2d87ec52b61aa1b9f646e58734930acebe899b3e464189a14a87202fb02574e70640bd22ef44b2d7e3912250a230a14080112100915007caa9b5931b76a3a85f046523e10011a09393837363534333231180120002a0c313838363738373430350000
|
||||
@@ -1,5 +1,9 @@
|
||||
# OEMCRYPTO Fuzzing
|
||||
|
||||
Refer to [Setting up Clusterfuzz](build_clusterfuzz.md) if you are interested
|
||||
in setting up a local instance of cluster fuzz to run fuzzing on your own
|
||||
OEMCrypto implementations on linux.
|
||||
|
||||
## Objective
|
||||
|
||||
* Run fuzzing on OEMCrypto public APIs on linux using google supported
|
||||
@@ -76,13 +80,31 @@
|
||||
$ export PATH_TO_CDM_DIR=..
|
||||
$ gyp --format=ninja --depth=$(pwd) oemcrypto/oemcrypto_unittests.gyp
|
||||
$ ninja -C out/Default/
|
||||
$ ./out/Default/oemcrypto_unittests --generate_corpus
|
||||
$ mkdir oemcrypto/test/fuzz_tests/corpus/<fuzzername>_seed_corpus
|
||||
# Generate corpus by excluding buffer overflow tests.
|
||||
$ ./out/Default/oemcrypto_unittests --generate_corpus \
|
||||
--gtest_filter=-"*Huge*"
|
||||
```
|
||||
|
||||
* There can be lot of duplicate corpus files that are generated from unit
|
||||
tests. We can minimize the corpus files to only a subset of files that
|
||||
cover unique paths within the API when run using fuzzer. Run following
|
||||
command to minimize corpus.
|
||||
|
||||
```shell
|
||||
$ cd /path/to/cdm/repo
|
||||
# build fuzzer binaries
|
||||
$ ./oemcrypto/test/fuzz_tests/build_oemcrypto_fuzztests
|
||||
$ mkdir /tmp/minimized_corpus
|
||||
# minimize corpus
|
||||
$ ./out/Default/<fuzz_target_binary> -merge=1 /tmp/minimized_corpus \
|
||||
<FULL_CORPUS_DIR>
|
||||
```
|
||||
|
||||
* To avoid uploading huge binary files to git repository, the corpus files
|
||||
will be saved in fuzzername_seed_corpus.zip format in blockbuster project's
|
||||
oemcrypto_fuzzing_corpus GCS bucket using gsutil. If you need permissions
|
||||
for blockbuster project, contact widevine-engprod@google.com.
|
||||
* To avoid uploading huge binary files to git repository, the minimized corpus
|
||||
files will be saved in fuzzername_seed_corpus.zip format in blockbuster
|
||||
project's oemcrypto_fuzzing_corpus GCS bucket using gsutil. If you need
|
||||
permissions for blockbuster project, contact widevine-engprod@google.com.
|
||||
|
||||
```shell
|
||||
$ gsutil cp gs://oemcrypto_fuzzing_corpus/<fuzzername_seed_corpus.zip> \
|
||||
|
||||
174
oemcrypto/test/fuzz_tests/build_clusterfuzz.md
Normal file
174
oemcrypto/test/fuzz_tests/build_clusterfuzz.md
Normal file
@@ -0,0 +1,174 @@
|
||||
# OEMCRYPTO Fuzzing - Build clustefuzz and run fuzzing
|
||||
|
||||
## Objective
|
||||
|
||||
* Run fuzzing on OEMCrypto public APIs on linux by building open sourced
|
||||
clusterfuzz source code in order to find security vulnerabilities.
|
||||
|
||||
[Clusterfuzz][1]
|
||||
|
||||
* Partners who implement OEMCrypto can follow these instructions to build
|
||||
clusterfuzz, the fuzzing framework and run fuzzing using fuzzer scripts
|
||||
provided by the Widevine team at Google.
|
||||
|
||||
## Glossary
|
||||
|
||||
* Fuzzing - Fuzzing is a methodology where random, interesting, unexpected
|
||||
inputs are fed to APIs in order to crash those, thereby catching any
|
||||
security vulnerabilities with the code.
|
||||
|
||||
* Fuzzing engines - [libfuzzer][4], afl, honggfuzz are the actual fuzzing
|
||||
engines that get the coverage information from API, use that to generate
|
||||
more interesting inputs which can be passed to fuzzer.
|
||||
|
||||
* Seed corpus - Fuzzing engine trying to generate interesting inputs from an
|
||||
empty file is not efficient. Seed corpus is the initial input that a fuzzer
|
||||
can accept and call the API with that. Fuzzing engine can then mutate this
|
||||
seed corpus to generate more inputs to fuzzer.
|
||||
|
||||
* Clusterfuzz - ClusterFuzz is a scalable fuzzing infrastructure that finds
|
||||
security and stability issues in software. Google uses ClusterFuzz to fuzz
|
||||
all Google products. Clusterfuzz provides us with the capability, tools to
|
||||
upload fuzz binaries and make use of the fuzzing engines to run fuzzing,
|
||||
find crashes and organizes the information. Clusterfuzz framework is open
|
||||
sourced, the source code can be downloaded and framework can be built
|
||||
locally or by using google cloud.
|
||||
|
||||
* Fuzzing output - Fuzzing is used to pass random inputs to API in order to
|
||||
ensure that API is crash resistant. We are not testing functionality via
|
||||
fuzzing. Fuzz scripts run continuously until they find a crash with the API
|
||||
under test.
|
||||
|
||||
## Building fuzz scripts
|
||||
|
||||
This section outlines the steps to build fuzz binaries that can be run
|
||||
continuously using clusterfuzz.
|
||||
|
||||
> **Note:** All the directories mentioned below are relative to cdm repository
|
||||
> root directory.
|
||||
|
||||
1. Fuzz scripts for OEMCrypto APIs are provided by the Widevine team at Google
|
||||
located under `oemcrypto/test/fuzz_tests` directory.
|
||||
|
||||
> **Note:** Prerequisites to run the following step are [here][10]. We also need
|
||||
> to install ninja.
|
||||
|
||||
2. Build a static library of your OEMCrypto implementation.
|
||||
* Compile and link your OEMCrypto implementation source with
|
||||
`-fsanitize=address,fuzzer` flag as per these [instructions][9] when
|
||||
building a static library.
|
||||
|
||||
* Run `./oemcrypto/test/fuzz_tests/build_partner_oemcrypto_fuzztests
|
||||
<oemcrypto_static_library_path>` script from cdm repository root
|
||||
directory.
|
||||
|
||||
* This will generate fuzz binaries under the `out/Default` directory.
|
||||
|
||||
|
||||
|
||||
> **Note:** Alternatively, you can use your own build systems, for which you
|
||||
> will need to define your own build files with the OEMCrypto fuzz source files
|
||||
> included. You can find the the fuzz source files in
|
||||
> `oemcrypto/test/fuzz_tests/partner_oemcrypto_fuzztests.gyp` and
|
||||
> `oemcrypto/test/fuzz_tests/partner_oemcrypto_fuzztests.gypi`.
|
||||
|
||||
3. Seed corpus for each fuzz script can be found under
|
||||
`oemcrypto/test/fuzz_tests/corpus` directory. Some fuzzers are simple and do
|
||||
not have seed corpus associated with them.
|
||||
|
||||
4. Create a zip file `oemcrypto_fuzzers_yyyymmddhhmmss.zip` with fuzz binaries
|
||||
and respective seed corpus zip files. Structure of a sample zip file with
|
||||
fuzzer binaries and seed corpus would look like following:
|
||||
|
||||
```
|
||||
* fuzzerA
|
||||
* fuzzerA_seed_corpus.zip
|
||||
* fuzzerB
|
||||
* fuzzerB_seed_corpus.zip
|
||||
* fuzzerC (fuzzerC doesn't have seed corpus associated with it)
|
||||
```
|
||||
|
||||
## Building clusterfuzz
|
||||
|
||||
* OEMCrypto implementation can be fuzzed by building clusterfuzz code which is
|
||||
open sourced and using it to run fuzzing. Use a Linux VM to build
|
||||
clusterfuzz.
|
||||
|
||||
> **Note:** You may see some issues with python modules missing, please install
|
||||
> those modules if you see errors. If you have multiple versions of python on
|
||||
> the VM, then use `python<version> -m pipenv shell` when you are at [this][3]
|
||||
> step.
|
||||
|
||||
* Follow these [instructions][2] in order to download clusterfuzz repository,
|
||||
build it locally or create a continuous fuzz infrastructure setup using
|
||||
google cloud.
|
||||
|
||||
## Running fuzzers on local clusterfuzz instance
|
||||
|
||||
* If you prefer to run fuzzing on a local machine instead of having a
|
||||
production setup using google cloud, then follow these [instructions][6] to
|
||||
add a job to the local clusterfuzz instance.
|
||||
|
||||
> **Note:** Job name should have a fuzzing engine and sanitizer as part of it. A
|
||||
> libfuzzer and asan jobs should have libfuzzer_asan in the job name.
|
||||
|
||||
* Create a job e:g:`libfuzzer_asan_oemcrypto` and upload previously created
|
||||
`oemcrypto_fuzzers_yyyymmddhhmmss.zip` as a custom build. Future uploads of
|
||||
zip file should have a name greater than current name. Following the above
|
||||
naming standard will ensure zip file names are always in ascending order.
|
||||
|
||||
* Once the job is added and clusterfuzz bot is running, fuzzing should be up
|
||||
and running. Results can be monitored as mentioned [here][6].
|
||||
|
||||
* On a local clusterfuzz instance, only one fuzzer is being fuzzed at a time.
|
||||
|
||||
> **Note:** Fuzzing is time consuming. Finding issues as well as clusterfuzz
|
||||
> regressing and fixing the issues can take time. We need fuzzing to run at
|
||||
> least for a couple of weeks to have good coverage.
|
||||
|
||||
## Finding fuzz crashes
|
||||
|
||||
Once the clusterfuzz finds an issue, it logs crash information such as the
|
||||
build, test case and stack trace for the crash.
|
||||
|
||||
* Test cases tab should show the fuzz crash and test case that caused the
|
||||
crash. Run `./fuzz_binary <test_case>` in order to debug the crash locally.
|
||||
|
||||
More information about different types of logs is as below:
|
||||
|
||||
* [Bot logs][7] will show information related to fuzzing, number of crashes
|
||||
that a particular fuzzer finds, number of new crashes, number of known
|
||||
crashes etc.
|
||||
|
||||
* [Local GCS][8] in your clusterfuzz checkout folder will store the fuzz
|
||||
binaries that are being fuzzed, seed corpus etc.
|
||||
|
||||
* `local_gcs/test-fuzz-logs-bucket` will store information related to fuzz
|
||||
crashes if any were found by the fuzzing engine. It will store crash
|
||||
information categorized by fuzzer and by each day. It will also store test
|
||||
case that caused the crash.
|
||||
|
||||
* `/path/to/my-bot/clusterfuzz/log.txt` will have any log information from
|
||||
fuzzer script and OEMCrypto implementation.
|
||||
|
||||
## Fixing issues
|
||||
|
||||
* Once you are able to debug using the crash test case, apply fix to the
|
||||
implementation, create `oemcrypto_fuzzers_yyyymmddhhmmss.zip` with latest
|
||||
fuzz binaries.
|
||||
|
||||
* Upload the latest fuzz binary to the fuzz job that was created earlier.
|
||||
Fuzzer will recognize the fix and mark the crash as fixed in test cases tab
|
||||
once the regression finishes. You do not need to update crashes as fixed,
|
||||
clusterfuzz will do that.
|
||||
|
||||
[1]: https://google.github.io/clusterfuzz/
|
||||
[2]: https://google.github.io/clusterfuzz/getting-started/
|
||||
[3]: https://google.github.io/clusterfuzz/getting-started/prerequisites/#loading-pipenv
|
||||
[4]: https://llvm.org/docs/LibFuzzer.html
|
||||
[5]: https://google.github.io/clusterfuzz/setting-up-fuzzing/libfuzzer-and-afl/
|
||||
[6]: https://google.github.io/clusterfuzz/setting-up-fuzzing/libfuzzer-and-afl/#checking-results
|
||||
[7]: https://google.github.io/clusterfuzz/getting-started/local-instance/#viewing-logs
|
||||
[8]: https://google.github.io/clusterfuzz/getting-started/local-instance/#local-google-cloud-storage
|
||||
[9]: https://google.github.io/clusterfuzz/setting-up-fuzzing/libfuzzer-and-afl/#libfuzzer
|
||||
[10]: https://google.github.io/clusterfuzz/setting-up-fuzzing/libfuzzer-and-afl/#prerequisites
|
||||
@@ -4,13 +4,9 @@ set -ex
|
||||
export CXX=clang++
|
||||
export CC=clang
|
||||
export GYP_DEFINES="$GYP_DEFINES clang=1"
|
||||
|
||||
export PATH_TO_CDM_DIR=.
|
||||
gyp --format=ninja --depth=$(pwd) oemcrypto/test/fuzz_tests/oemcrypto_fuzztests.gyp
|
||||
ninja -C out/Default
|
||||
# oemcrypto_odkitee_fuzztests.gypi has flags to instrument all the gyp targets
|
||||
# with fuzzer flags.
|
||||
gyp --format=ninja --depth=$(pwd) \
|
||||
--include=oemcrypto/test/fuzz_tests/oemcrypto_odkitee_fuzztests.gypi \
|
||||
oemcrypto/test/fuzz_tests/oemcrypto_odkitee_fuzztests.gyp
|
||||
export PYTHONPATH="$PYTHONPATH:$PATH_TO_CDM_DIR/third_party"
|
||||
|
||||
python3 $PATH_TO_CDM_DIR/third_party/gyp/__init__.py --format=ninja \
|
||||
--depth=$(pwd) oemcrypto/test/fuzz_tests/oemcrypto_fuzztests.gyp
|
||||
ninja -C out/Default
|
||||
15
oemcrypto/test/fuzz_tests/build_partner_oemcrypto_fuzztests
Normal file
15
oemcrypto/test/fuzz_tests/build_partner_oemcrypto_fuzztests
Normal file
@@ -0,0 +1,15 @@
|
||||
#!/bin/bash
|
||||
set -ex
|
||||
|
||||
# For use by partners to generate fuzz binaries for their OEMCrypto
|
||||
# implementation on linux.
|
||||
|
||||
export CXX=clang++
|
||||
export CC=clang
|
||||
export GYP_DEFINES="$GYP_DEFINES clang=1 oemcrypto_static_library=$1"
|
||||
export PATH_TO_CDM_DIR=.
|
||||
export PYTHONPATH="$PYTHONPATH:$PATH_TO_CDM_DIR/third_party"
|
||||
|
||||
python3 $PATH_TO_CDM_DIR/third_party/gyp/__init__.py --format=ninja \
|
||||
--depth=$(pwd) oemcrypto/test/fuzz_tests/partner_oemcrypto_fuzztests.gyp
|
||||
ninja -C out/Default
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user