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>
diff --git a/generator/dawn_json_generator.py b/generator/dawn_json_generator.py index 8eb0d6c..705948c 100644 --- a/generator/dawn_json_generator.py +++ b/generator/dawn_json_generator.py
@@ -566,6 +566,76 @@ 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 @@ 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 @@ 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 @@ '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):
diff --git a/src/dawn/fuzzers/lpmfuzz/DawnLPMConstants.h b/generator/templates/dawn/fuzzers/lpmfuzz/DawnLPMConstants.h similarity index 85% rename from src/dawn/fuzzers/lpmfuzz/DawnLPMConstants.h rename to generator/templates/dawn/fuzzers/lpmfuzz/DawnLPMConstants.h index 2d173da..f8cc150 100644 --- a/src/dawn/fuzzers/lpmfuzz/DawnLPMConstants.h +++ b/generator/templates/dawn/fuzzers/lpmfuzz/DawnLPMConstants.h
@@ -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
diff --git a/generator/templates/dawn/fuzzers/lpmfuzz/dawn_lpm.proto b/generator/templates/dawn/fuzzers/lpmfuzz/dawn_lpm.proto index 3c30312..e9ada2e 100644 --- a/generator/templates/dawn/fuzzers/lpmfuzz/dawn_lpm.proto +++ b/generator/templates/dawn/fuzzers/lpmfuzz/dawn_lpm.proto
@@ -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 Program { repeated Command commands = 1; -} \ No newline at end of file +}
diff --git a/src/dawn/fuzzers/BUILD.gn b/src/dawn/fuzzers/BUILD.gn index 875a115..161bbc4 100644 --- a/src/dawn/fuzzers/BUILD.gn +++ b/src/dawn/fuzzers/BUILD.gn
@@ -122,6 +122,7 @@ outputs = [ "src/dawn/fuzzers/lpmfuzz/DawnLPMSerializer_autogen.cpp", "src/dawn/fuzzers/lpmfuzz/DawnLPMSerializer_autogen.h", + "src/dawn/fuzzers/lpmfuzz/DawnLPMConstants_autogen.h", ] }
diff --git a/src/dawn/fuzzers/lpmfuzz/DawnLPMFuzzer.cpp b/src/dawn/fuzzers/lpmfuzz/DawnLPMFuzzer.cpp index 3b7533d..930cd5a 100644 --- a/src/dawn/fuzzers/lpmfuzz/DawnLPMFuzzer.cpp +++ b/src/dawn/fuzzers/lpmfuzz/DawnLPMFuzzer.cpp
@@ -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 @@ } // 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 @@ 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 @@ 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 @@ wireServer = nullptr; return 0; } + +} // namespace DawnLPMFuzzer
diff --git a/src/dawn/fuzzers/lpmfuzz/dawn_lpm.json b/src/dawn/fuzzers/lpmfuzz/dawn_lpm.json index 992426c..a22ca12 100644 --- a/src/dawn/fuzzers/lpmfuzz/dawn_lpm.json +++ b/src/dawn/fuzzers/lpmfuzz/dawn_lpm.json
@@ -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" + ] + } \ No newline at end of file