Add fuzzers for SPIRV-Cross

This CL adds in fuzzers for SPIRV-Cross for HLSL, GLSL, and MSL
outputs. These fuzzers live in Dawn because there is not appropriate
location in the Chromium source repo for them and it is unlikely they
would be land-able in the SPIRV-Cross repo, because it is not coupled
with Chromium's build system and thus Clusterfuzz so would be
effectively dead code. Dawn depends on this code, but it is also
integrated into the Chromium build system, so this was the best place
I could find for them

The code under fuzz unfortunately uses exceptions/aborting as its
error reporting mechanism. This is an acknowledge short coming and
there are efforts to remove this behaviour. To work around this and
reduce the number of false positives found by the fuzzers, a signal
trap has been implemented which will be removed once the code under
fuzz has been updated.

The trap replaces the existing signal handler and silencing signals
while running the code under test. This allows the code under test to
call abort() and not crash the fuzzing process. Theoretically, only
SIGABRT should need to be trapped, but something is causing the signal
from abort() to be converted to SIGSEGV when running under ASAN.

This signal trap has been tested with the fuzzing/sanitizers by
intentionally inserting bad calls that will occur after a few thousand
test cases. It was confirmed that the fuzzer detected the issue and
stops fuzzing.

The alternate to implementing this signal trap would be to turn on
exceptions for the fuzzer. This was attempted, but proved to be
fruitless due to what was reported as an ODR issue, but couldn't
couldn't be silenced. The likely underlying issue was a pre-built
library or other object being built without exceptions was causing
different versions of symbols or the exception version of the standard
library not being instrumented by ASAN. Given the majority of Chromium
eco-system turns off exceptions, fixing this issue would not be
helpful to the larger community and was looking like it would require
significant effort.

BUG=chromium:903380

Change-Id: I63a5595383f99b7a0e150d72bb04c89b8d722631
Reviewed-on: https://dawn-review.googlesource.com/c/2260
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Max Moroz <mmoroz@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
This commit is contained in:
Ryan Harrison 2018-11-12 12:20:21 +00:00 committed by Commit Bot service account
parent 6f86f176a9
commit 9854295ff6
13 changed files with 510 additions and 3 deletions

View File

@ -17,6 +17,9 @@ import("scripts/dawn_features.gni")
import("//build_overrides/build.gni")
import("//testing/test.gni")
if (build_with_chromium) {
import("//testing/libfuzzer/fuzzer_test.gni")
}
###############################################################################
# Template to wrap the Dawn code generator
@ -941,3 +944,99 @@ if (dawn_standalone) {
]
}
}
###############################################################################
# Fuzzers
###############################################################################
if (build_with_chromium) {
group("dawn_fuzzers") {
testonly = true
deps = [
":dawn_spirv_cross_glsl_fast_fuzzer",
":dawn_spirv_cross_glsl_full_fuzzer",
":dawn_spirv_cross_hlsl_fast_fuzzer",
":dawn_spirv_cross_hlsl_full_fuzzer",
":dawn_spirv_cross_msl_fast_fuzzer",
":dawn_spirv_cross_msl_full_fuzzer",
]
}
config("dawn_spirv_cross_fuzzers_config") {
defines = [ "SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS" ]
include_dirs = [
"src/fuzzers",
"third_party/",
]
}
static_library("dawn_spirv_cross_fuzzer_common") {
public_configs = [ ":dawn_spirv_cross_fuzzers_config" ]
sources = [
"src/fuzzers/DawnSPIRVCrossFuzzer.cpp",
"src/fuzzers/DawnSPIRVCrossFuzzer.h",
]
deps = [
"third_party:spirv_cross",
]
}
# Uses Dawn specific options and varies input data
fuzzer_test("dawn_spirv_cross_glsl_fast_fuzzer") {
sources = [
"src/fuzzers/DawnSPIRVCrossGLSLFastFuzzer.cpp",
]
deps = [
":dawn_spirv_cross_fuzzer_common",
]
}
# Varies bother the options and input data
fuzzer_test("dawn_spirv_cross_glsl_full_fuzzer") {
sources = [
"src/fuzzers/DawnSPIRVCrossGLSLFullFuzzer.cpp",
]
deps = [
":dawn_spirv_cross_fuzzer_common",
]
}
# Uses Dawn specific options and varies input data
fuzzer_test("dawn_spirv_cross_hlsl_fast_fuzzer") {
sources = [
"src/fuzzers/DawnSPIRVCrossGLSLFastFuzzer.cpp",
]
deps = [
":dawn_spirv_cross_fuzzer_common",
]
}
# Varies bother the options and input data
fuzzer_test("dawn_spirv_cross_hlsl_full_fuzzer") {
sources = [
"src/fuzzers/DawnSPIRVCrossGLSLFullFuzzer.cpp",
]
deps = [
":dawn_spirv_cross_fuzzer_common",
]
}
# Uses Dawn specific options and varies input data
fuzzer_test("dawn_spirv_cross_msl_fast_fuzzer") {
sources = [
"src/fuzzers/DawnSPIRVCrossGLSLFastFuzzer.cpp",
]
deps = [
":dawn_spirv_cross_fuzzer_common",
]
}
# Varies bother the options and input data
fuzzer_test("dawn_spirv_cross_msl_full_fuzzer") {
sources = [
"src/fuzzers/DawnSPIRVCrossGLSLFullFuzzer.cpp",
]
deps = [
":dawn_spirv_cross_fuzzer_common",
]
}
}

View File

@ -33,6 +33,8 @@ namespace dawn_native { namespace d3d12 {
const std::string ShaderModule::GetHLSLSource(PipelineLayout* layout) const {
spirv_cross::CompilerHLSL compiler(mSpirv);
// If these options are changed, the values in DawnSPIRVCrossHLSLFastFuzzer.cpp need to be
// updated.
spirv_cross::CompilerGLSL::Options options_glsl;
options_glsl.vertex.fixup_clipspace = true;
options_glsl.vertex.flip_vert_y = true;

View File

@ -52,6 +52,8 @@ namespace dawn_native { namespace metal {
const PipelineLayout* layout) const {
spirv_cross::CompilerMSL compiler(mSpirv);
// If these options are changed, the values in DawnSPIRVCrossMSLFastFuzzer.cpp need to be
// updated.
spirv_cross::CompilerGLSL::Options options_glsl;
options_glsl.vertex.flip_vert_y = true;
compiler.spirv_cross::CompilerGLSL::set_common_options(options_glsl);

View File

@ -50,6 +50,8 @@ namespace dawn_native { namespace opengl {
ShaderModule::ShaderModule(Device* device, const ShaderModuleDescriptor* descriptor)
: ShaderModuleBase(device, descriptor) {
spirv_cross::CompilerGLSL compiler(descriptor->code, descriptor->codeSize);
// If these options are changed, the values in DawnSPIRVCrossGLSLFastFuzzer.cpp need to be
// updated.
spirv_cross::CompilerGLSL::Options options;
// TODO(cwallez@chromium.org): discover the backing context version and use that.

View File

@ -0,0 +1,112 @@
// Copyright 2018 The Dawn 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
//
// 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 <csetjmp>
#include <csignal>
#include <cstdint>
#include <string>
#include <vector>
#include "DawnSPIRVCrossFuzzer.h"
namespace {
std::jmp_buf jump_buffer;
void (*old_signal_handler)(int);
// Handler to trap signals, so that it doesn't crash the fuzzer when running
// the code under test. Currently the
// code being fuzzed uses abort() to report errors like bad input instead of
// returning an error code. This will be changing in the future.
//
// TODO(rharrison): Remove all of this signal trapping once SPIRV-Cross has
// been changed to not use abort() for reporting errors.
[[noreturn]] static void sigabrt_trap(int sig) {
std::longjmp(jump_buffer, 1);
}
// Setup the SIGABRT trap
void BeginSIGABRTTrap() {
old_signal_handler = signal(SIGABRT, sigabrt_trap);
if (old_signal_handler == SIG_ERR)
abort();
}
// Restore the previous signal handler
void EndSIGABRTTrap() {
signal(SIGABRT, old_signal_handler);
}
} // namespace
namespace DawnSPIRVCrossFuzzer {
int Run(const uint8_t* data, size_t size, std::function<int(std::vector<uint32_t>)> task) {
if (!data || size < 1)
return 0;
std::vector<uint32_t> input(data, data + (4 * (size / 4)));
BeginSIGABRTTrap();
// On the first pass through setjmp will return 0, if returning here
// from the longjmp in the signal handler it will return 1.
if (setjmp(jump_buffer) == 0) {
task(input);
}
EndSIGABRTTrap();
return 0;
}
template <class Options>
int RunWithOptions(const uint8_t* data,
size_t size,
std::function<int(std::vector<uint32_t>, Options)> task) {
if (!data || size < sizeof(Options) + 1)
return 0;
Options options = *reinterpret_cast<const Options*>(data);
data += sizeof(options);
size -= sizeof(options);
std::vector<uint32_t> input(data, data + (4 * (size / 4)));
BeginSIGABRTTrap();
// On the first pass through setjmp will return 0, if returning here
// from the longjmp in the signal handler it will return 1.
if (setjmp(jump_buffer) == 0) {
task(input, options);
}
EndSIGABRTTrap();
return 0;
}
template int RunWithOptions<spirv_cross::CompilerGLSL::Options>(
const uint8_t*,
size_t,
TaskWithOptions<spirv_cross::CompilerGLSL::Options>);
template int RunWithOptions<spirv_cross::CompilerHLSL::Options>(
const uint8_t*,
size_t,
TaskWithOptions<spirv_cross::CompilerHLSL::Options>);
template int RunWithOptions<CombinedOptions>(const uint8_t*,
size_t,
TaskWithOptions<CombinedOptions>);
} // namespace DawnSPIRVCrossFuzzer

View File

@ -0,0 +1,40 @@
// Copyright 2018 The Dawn 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
//
// 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 <cstdint>
#include <functional>
#include <vector>
#include "spirv-cross/spirv_glsl.hpp"
#include "spirv-cross/spirv_hlsl.hpp"
namespace DawnSPIRVCrossFuzzer {
struct CombinedOptions {
spirv_cross::CompilerGLSL::Options glsl;
spirv_cross::CompilerHLSL::Options hlsl;
};
using Task = std::function<int(std::vector<uint32_t>)>;
template <class Options>
using TaskWithOptions = std::function<int(std::vector<uint32_t>, Options)>;
// Used to fuzz by mutating the input data, but with fixed options to the compiler
int Run(const uint8_t* data, size_t size, Task task);
// Used to fuzz by mutating both the input data and options to the compiler
template <class Options>
int RunWithOptions(const uint8_t* data, size_t size, TaskWithOptions<Options> task);
} // namespace DawnSPIRVCrossFuzzer

View File

@ -0,0 +1,39 @@
// Copyright 2018 The Dawn 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
//
// 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 <cstdint>
#include <string>
#include <vector>
#include "DawnSPIRVCrossFuzzer.h"
namespace {
int GLSLFastFuzzTask(std::vector<uint32_t> input) {
spirv_cross::CompilerGLSL compiler(input);
// Using the options that are used by Dawn, they appear in ShaderModuleGL.cpp
spirv_cross::CompilerGLSL::Options options;
options.version = 440;
compiler.set_common_options(options);
std::string result = compiler.compile();
return 0;
}
} // namespace
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
return DawnSPIRVCrossFuzzer::Run(data, size, GLSLFastFuzzTask);
}

View File

@ -0,0 +1,37 @@
// Copyright 2018 The Dawn 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
//
// 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 <cstdint>
#include <string>
#include <vector>
#include "DawnSPIRVCrossFuzzer.h"
namespace {
int GLSLFullFuzzTask(std::vector<uint32_t> input, spirv_cross::CompilerGLSL::Options options) {
spirv_cross::CompilerGLSL compiler(input);
compiler.set_common_options(options);
std::string result = compiler.compile();
return 0;
}
} // namespace
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
return DawnSPIRVCrossFuzzer::RunWithOptions<spirv_cross::CompilerGLSL::Options>(
data, size, GLSLFullFuzzTask);
}

View File

@ -0,0 +1,46 @@
// Copyright 2018 The Dawn 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
//
// 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 <cstdint>
#include <string>
#include <vector>
#include "DawnSPIRVCrossFuzzer.h"
#include "spirv-cross/spirv_hlsl.hpp"
namespace {
int FuzzTask(std::vector<uint32_t> input) {
spirv_cross::CompilerHLSL compiler(input);
// Using the options that are used by Dawn, they appear in ShaderModuleD3D12.cpp
spirv_cross::CompilerGLSL::Options options_glsl;
options_glsl.vertex.fixup_clipspace = true;
options_glsl.vertex.flip_vert_y = true;
compiler.set_common_options(options_glsl);
spirv_cross::CompilerHLSL::Options options_hlsl;
options_hlsl.shader_model = 51;
compiler.set_hlsl_options(options_hlsl);
std::string result = compiler.compile();
return 0;
}
} // namespace
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
return DawnSPIRVCrossFuzzer::Run(data, size, FuzzTask);
}

View File

@ -0,0 +1,39 @@
// Copyright 2018 The Dawn 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
//
// 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 <cstdint>
#include <string>
#include <vector>
#include "DawnSPIRVCrossFuzzer.h"
#include "spirv-cross/spirv_hlsl.hpp"
namespace {
int FuzzTask(std::vector<uint32_t> input, DawnSPIRVCrossFuzzer::CombinedOptions options) {
spirv_cross::CompilerHLSL compiler(input);
compiler.set_common_options(options.glsl);
compiler.set_hlsl_options(options.hlsl);
std::string result = compiler.compile();
return 0;
}
} // namespace
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
return DawnSPIRVCrossFuzzer::RunWithOptions<DawnSPIRVCrossFuzzer::CombinedOptions>(data, size,
FuzzTask);
}

View File

@ -0,0 +1,41 @@
// Copyright 2018 The Dawn 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
//
// 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 <cstdint>
#include <string>
#include <vector>
#include "DawnSPIRVCrossFuzzer.h"
#include "spirv-cross/spirv_msl.hpp"
namespace {
int FuzzTask(std::vector<uint32_t> input) {
spirv_cross::CompilerMSL compiler(input);
// Using the options that are used by Dawn, they appear in ShaderModuleMTL.mm
spirv_cross::CompilerGLSL::Options options;
options.vertex.flip_vert_y = true;
compiler.spirv_cross::CompilerGLSL::set_common_options(options);
std::string result = compiler.compile();
return 0;
}
} // namespace
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
return DawnSPIRVCrossFuzzer::Run(data, size, FuzzTask);
}

View File

@ -0,0 +1,39 @@
// Copyright 2018 The Dawn 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
//
// 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 <cstdint>
#include <string>
#include <vector>
#include "DawnSPIRVCrossFuzzer.h"
#include "spirv-cross/spirv_msl.hpp"
namespace {
int FuzzTask(std::vector<uint32_t> input, spirv_cross::CompilerGLSL::Options options) {
spirv_cross::CompilerMSL compiler(input);
compiler.spirv_cross::CompilerGLSL::set_common_options(options);
std::string result = compiler.compile();
return 0;
}
} // namespace
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
return DawnSPIRVCrossFuzzer::RunWithOptions<spirv_cross::CompilerGLSL::Options>(data, size,
FuzzTask);
}

15
third_party/BUILD.gn vendored
View File

@ -16,6 +16,10 @@ import("../scripts/dawn_overrides_with_defaults.gni")
import("../scripts/dawn_features.gni")
import("//build_overrides/build.gni")
if (build_with_chromium) {
import("//testing/libfuzzer/fuzzer_test.gni")
}
is_msvc = is_win && !is_clang
###############################################################################
@ -68,7 +72,12 @@ static_library("spirv_cross") {
need_glsl_cross = dawn_enable_opengl
if (dawn_enable_d3d12) {
is_fuzzing = false
if (build_with_chromium) {
is_fuzzing = use_fuzzing_engine
}
if (dawn_enable_d3d12 || is_fuzzing) {
sources += [
"${spirv_cross_dir}/spirv_hlsl.cpp",
"${spirv_cross_dir}/spirv_hlsl.hpp",
@ -76,7 +85,7 @@ static_library("spirv_cross") {
need_glsl_cross = true
}
if (dawn_enable_metal) {
if (dawn_enable_metal || is_fuzzing) {
sources += [
"${spirv_cross_dir}/spirv_msl.cpp",
"${spirv_cross_dir}/spirv_msl.hpp",
@ -84,7 +93,7 @@ static_library("spirv_cross") {
need_glsl_cross = true
}
if (need_glsl_cross) {
if (need_glsl_cross || is_fuzzing) {
sources += [
"${spirv_cross_dir}/spirv_glsl.cpp",
"${spirv_cross_dir}/spirv_glsl.hpp",