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