Add Dawn Wire Server LPM Fuzzer [2/N]

Add generators for protobuf files.

This CL contains the basic logic required to generate the protobuf
files from dawn.json and the newly added dawn_lpm.json for
libprotobuf-mutator.

Bug: chromium:1374747
Change-Id: I5dd207ed94ecdac365306c26e79b6cc18d3978f6
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/114640
Reviewed-by: Austin Eng <enga@chromium.org>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Brendon Tiszka <tiszka@chromium.org>
This commit is contained in:
Brendon Tiszka 2023-03-23 16:02:20 +00:00 committed by Dawn LUCI CQ
parent 73570d8f48
commit a045643361
6 changed files with 241 additions and 17 deletions

View File

@ -566,6 +566,76 @@ def compute_wire_params(api_params, wire_json):
return wire_params
############################################################
# DAWN LPM FUZZ STUFF
############################################################
def compute_lpm_params(api_and_wire_params, lpm_json):
# Start with all commands in dawn.json and dawn_wire.json
lpm_params = api_and_wire_params.copy()
# Commands that are built through generation
proto_generated_commands = []
# All commands, including hand written commands that we can't generate
# through codegen
proto_all_commands = []
# Remove blocklisted commands from protobuf generation params
blocklisted_cmds_proto = lpm_json.get('blocklisted_cmds')
custom_cmds_proto = lpm_json.get('custom_cmds')
for command in lpm_params['cmd_records']['command']:
blocklisted = command.name.get() in blocklisted_cmds_proto
custom = command.name.get() in custom_cmds_proto
if not blocklisted and not custom:
proto_generated_commands.append(command)
proto_all_commands.append(command)
lpm_params['cmd_records'] = {
'proto_generated_commands': proto_generated_commands,
'proto_all_commands': proto_all_commands,
}
return lpm_params
def as_protobufTypeLPM(member):
assert 'type' in member.json_data
if member.type.name.native:
typ = member.json_data['type']
cpp_to_protobuf_type = {
"bool": "bool",
"float": "float",
"double": "double",
"int8_t": "int32",
"int16_t": "int32",
"int32_t": "int32",
"int64_t": "int64",
"uint8_t": "uint32",
"uint16_t": "uint32",
"uint32_t": "uint32",
"uint64_t": "uint64",
}
assert typ in cpp_to_protobuf_type
return cpp_to_protobuf_type[typ]
return member.type.name.CamelCase()
def as_protobufNameLPM(*names):
# `descriptor` is a reserved keyword in lpm
if (names[0].concatcase() == "descriptor"):
return "desc"
return as_varName(*names)
def unreachable_code():
assert False
#############################################################
# Generator
@ -1039,11 +1109,17 @@ class MultiGeneratorFromDawnJSON(Generator):
params_dawn_wire = parse_json(loaded_json,
enabled_tags=['dawn', 'deprecated'],
disabled_tags=['native'])
additional_params = compute_wire_params(params_dawn_wire,
wire_json)
api_and_wire_params = compute_wire_params(params_dawn_wire,
wire_json)
fuzzer_params = compute_lpm_params(api_and_wire_params, lpm_json)
lpm_params = [
RENDER_PARAMS_BASE, params_dawn_wire, {}, additional_params
RENDER_PARAMS_BASE, params_dawn_wire, {
'as_protobufTypeLPM': as_protobufTypeLPM,
'as_protobufNameLPM': as_protobufNameLPM,
'unreachable': unreachable_code
}, api_and_wire_params, fuzzer_params
]
renders.append(
@ -1055,11 +1131,11 @@ class MultiGeneratorFromDawnJSON(Generator):
params_dawn_wire = parse_json(loaded_json,
enabled_tags=['dawn', 'deprecated'],
disabled_tags=['native'])
additional_params = compute_wire_params(params_dawn_wire,
wire_json)
api_and_wire_params = compute_wire_params(params_dawn_wire,
wire_json)
lpm_params = [
RENDER_PARAMS_BASE, params_dawn_wire, {}, additional_params
RENDER_PARAMS_BASE, params_dawn_wire, {}, api_and_wire_params
]
renders.append(
@ -1074,6 +1150,12 @@ class MultiGeneratorFromDawnJSON(Generator):
'src/dawn/fuzzers/lpmfuzz/DawnLPMSerializer_autogen.h',
lpm_params))
renders.append(
FileRender(
'dawn/fuzzers/lpmfuzz/DawnLPMConstants.h',
'src/dawn/fuzzers/lpmfuzz/DawnLPMConstants_autogen.h',
lpm_params))
return renders
def get_dependencies(self, args):

View File

@ -12,4 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#define INSTANCE_OBJECT_ID 1
namespace DawnLPMFuzzer {
static constexpr int kInstanceObjectId = 1;
} // namespace DawnLPMFuzzer

View File

@ -15,13 +15,134 @@
syntax = "proto2";
package fuzzing;
{% for command in cmd_records["command"] %}
message {{command.name.CamelCase()}} {}
{% for type in by_category["enum"] %}
enum {{as_cppType(type.name)}} {
{% for value in type.values %}
{{ as_cppType(type.name) }}{{as_cppEnum(value.name)}} = {{ value.value }};
{% endfor %}
};
{% endfor %}
{% for type in by_category["bitmask"] %}
enum {{as_cppType(type.name)}} {
{% for value in type.values %}
{{ as_cppType(type.name) }}{{as_cppEnum(value.name)}} = {{ value.value }};
{% endfor %}
};
{% endfor %}
{% macro lift_string_proto_member(member, count) -%}
required string {{ as_protobufNameLPM(member.name) }} = {{ count.value }};
{% set count.value = count.value + 1 %}
{%- endmacro %}
{% macro lift_float_array_proto_member(member, count) -%}
repeated float {{ as_varName(member.name) }} = {{ count.value }};
{% set count.value = count.value + 1 %}
{%- endmacro %}
{% macro lift_varlength_proto_member(member, count) -%}
{% if member.type in by_category["object"] %}
repeated uint32 {{ as_protobufNameLPM(member.name) }} = {{ count.value }};
{% set count.value = count.value + 1 %}
{% elif member.type.name.get() == "object id" %}
repeated uint32 {{ as_protobufNameLPM(member.name) }} = {{ count.value }};
{% set count.value = count.value + 1 %}
{% elif member.type.name.get() == "uint8_t" %}
// Skip over byte arrays in protobuf, handled by DawnSerializer
{% else %}
repeated {{ as_protobufTypeLPM(member) }} {{ as_protobufNameLPM(member.name) }} = {{ count.value }};
{% set count.value = count.value + 1 %}
{% endif %}
{%- endmacro %}
{% macro lift_dawn_member_pass_by_value(record, name, member, count) %}
{% if member.type in by_category["structure"] %}
required {{ as_protobufTypeLPM(member) }} {{ as_protobufNameLPM(member.name) }} = {{ count.value }};
{% set count.value = count.value + 1 %}
{% elif member.type in by_category["bitmask"] %}
repeated {{ as_protobufTypeLPM(member) }} {{ as_protobufNameLPM(member.name) }} = {{ count.value }};
{% set count.value = count.value + 1 %}
{% elif member.type in by_category["enum"] %}
required {{ as_protobufTypeLPM(member) }} {{ as_protobufNameLPM(member.name) }} = {{ count.value }};
{% set count.value = count.value + 1 %}
{% elif member.type in by_category["object"] %}
required uint32 {{ as_protobufNameLPM(member.name) }} = {{ count.value }};
{% set count.value = count.value + 1 %}
{% elif member.type.name.get() == "ObjectId" %}
required uint32 {{ as_protobufNameLPM(member.name) }} = {{ count.value }};
{% set count.value = count.value + 1 %}
{% elif member.type.name.get() == "ObjectHandle" %}
// Skips object handles while lifting dawn.json to protobuf because
// ObjectHandles are created and managed in DawnLPMSerializer. Passing
// arbitrary ObjectHandles from the fuzzer's bytestream isn't the
// strategy for this fuzzer.
{% else %}
required {{ as_protobufTypeLPM(member) }} {{ as_protobufNameLPM(member.name) }} = {{ count.value }};
{% set count.value = count.value + 1 %}
{% endif %}
{% endmacro %}
{% macro lift_dawn_member_pass_by_pointer(record, name, member, count) %}
{% if member.type in by_category["structure"] and member.length == "constant" and member.constant_length == 1 %}
required {{ as_protobufTypeLPM(member) }} {{ as_protobufNameLPM(member.name) }} = {{ count.value }};
{% set count.value = count.value + 1 %}
{% elif member.type.name.get() == "char" and member.length == 'strlen' %}
{{ lift_string_proto_member(member, count) }}
{% elif member.type.name.get() == "float" %}
{{ lift_float_array_proto_member(member, count) }}
{% elif member.type.name.get() == "uint8_t" %}
// Skip over byte arrays in protobuf, handled by DawnLPMSerializer
// with a hardcoded bytes and length.
{% elif member.length != 'constant' %}
{{ lift_varlength_proto_member(member, count) }}
{% else %}
// There shouldn't be any other pass-by-pointer types in
// dawn*.json, if any are added we would like to know at compile time
{{ unreachable_code }}
{% endif %}
{% endmacro %}
{% macro lift_proto_members_helper(record, name, members) %}
{% set count = namespace(value=1) %}
{% for member in members %}
{% if member.skip_serialize == True %}
// {{ member.name.camelCase()}}.skip_serialize
{% elif member.annotation == 'value' %}
{{ lift_dawn_member_pass_by_value(record, name, member, count) }}
{% elif member.annotation == 'const*' %}
{{ lift_dawn_member_pass_by_pointer(record, name, member, count) }}
{% endif %}
{% endfor %}
{% endmacro %}
{% for structure in by_category["structure"] %}
message {{structure.name.CamelCase()}} {
{{ lift_proto_members_helper(structure, structure.name, structure.members) }}
}
{% endfor %}
{% for command in cmd_records["proto_all_commands"] %}
{% if command not in cmd_records["proto_generated_commands"] %}
message {{command.name.CamelCase()}} {}
{% else %}
message {{command.name.CamelCase()}} {
{{ lift_proto_members_helper(command, command.name, command.members) }}
}
{% endif %}
{% endfor %}
message Command {
oneof command {
{% for command in cmd_records["command"] %}
{% for command in cmd_records["proto_all_commands"] %}
{{command.name.CamelCase()}} {{command.name.camelCase()}} = {{ loop.index }};
{% endfor %}
}
@ -29,4 +150,4 @@ message Command {
message Program {
repeated Command commands = 1;
}
}

View File

@ -122,6 +122,7 @@ if (is_dawn_lpm_fuzzer && build_with_chromium && dawn_use_swiftshader) {
outputs = [
"src/dawn/fuzzers/lpmfuzz/DawnLPMSerializer_autogen.cpp",
"src/dawn/fuzzers/lpmfuzz/DawnLPMSerializer_autogen.h",
"src/dawn/fuzzers/lpmfuzz/DawnLPMConstants_autogen.h",
]
}

View File

@ -20,7 +20,7 @@
#include "dawn/common/Log.h"
#include "dawn/common/SystemUtils.h"
#include "dawn/dawn_proc.h"
#include "dawn/fuzzers/lpmfuzz/DawnLPMConstants.h"
#include "dawn/fuzzers/lpmfuzz/DawnLPMConstants_autogen.h"
#include "dawn/fuzzers/lpmfuzz/DawnLPMFuzzer.h"
#include "dawn/fuzzers/lpmfuzz/DawnLPMSerializer_autogen.h"
#include "dawn/fuzzers/lpmfuzz/dawn_lpm_autogen.pb.h"
@ -68,7 +68,9 @@ WGPUSwapChain ErrorDeviceCreateSwapChain(WGPUDevice device,
} // namespace
int DawnLPMFuzzer::Initialize(int* argc, char*** argv) {
namespace DawnLPMFuzzer {
int Initialize(int* argc, char*** argv) {
// TODO(crbug.com/1038952): The Instance must be static because destructing the vkInstance with
// Swiftshader crashes libFuzzer. When this is fixed, move this into Run so that error injection
// for adapter discovery can be fuzzed.
@ -78,8 +80,7 @@ int DawnLPMFuzzer::Initialize(int* argc, char*** argv) {
return 0;
}
int DawnLPMFuzzer::Run(const fuzzing::Program& program,
bool (*AdapterSupported)(const dawn::native::Adapter&)) {
int Run(const fuzzing::Program& program, bool (*AdapterSupported)(const dawn::native::Adapter&)) {
sAdapterSupported = AdapterSupported;
DawnProcTable procs = dawn::native::GetProcs();
@ -115,7 +116,7 @@ int DawnLPMFuzzer::Run(const fuzzing::Program& program,
serverDesc.serializer = &devNull;
std::unique_ptr<dawn::wire::WireServer> wireServer(new dawn_wire::WireServer(serverDesc));
wireServer->InjectInstance(sInstance->Get(), INSTANCE_OBJECT_ID, 0);
wireServer->InjectInstance(sInstance->Get(), kInstanceObjectId, 0);
static utils::TerribleCommandBuffer* mCommandBuffer = new utils::TerribleCommandBuffer();
static dawn::wire::ChunkedCommandSerializer mSerializer =
@ -131,3 +132,5 @@ int DawnLPMFuzzer::Run(const fuzzing::Program& program,
wireServer = nullptr;
return 0;
}
} // namespace DawnLPMFuzzer

View File

@ -15,5 +15,18 @@
"limitations under the License."
],
"_doc": "See docs/dawn/codegen.md"
"_doc": "See docs/dawn/codegen.md",
"custom_cmds": [],
"blocklisted_cmds": [
"surface descriptor from windows core window",
"surface descriptor from windows swap chain panel",
"surface descriptor from canvas html selector",
"device create render pipeline",
"device create render pipeline async",
"device create shader module",
"destroy object"
]
}