Add Dawn Wire Server LPM Fuzzer [4/N]
Implements C++ serializer implementation that translates protobuf objects into Dawn serial data. 1) A generator that builds a fuzzing harness that converts LPM structured data into serialized bytes, that are then sent to Dawn Wire Server. 2) Object store for dawn objects that are allocated and freed. Bug: chromium:1374747 Change-Id: I09c1be6cdc2eccf4a91de808f19494d97d01b3d6 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/114720 Commit-Queue: Brendon Tiszka <tiszka@chromium.org> Kokoro: Kokoro <noreply+kokoro@google.com> Reviewed-by: Austin Eng <enga@chromium.org>
This commit is contained in:
parent
8e42cfa7ea
commit
27521c6b91
|
@ -19,21 +19,21 @@
|
||||||
|
|
||||||
"commands": {
|
"commands": {
|
||||||
"buffer map async": [
|
"buffer map async": [
|
||||||
{ "name": "buffer id", "type": "ObjectId" },
|
{ "name": "buffer id", "type": "ObjectId", "id_type": "buffer" },
|
||||||
{ "name": "request serial", "type": "uint64_t" },
|
{ "name": "request serial", "type": "uint64_t" },
|
||||||
{ "name": "mode", "type": "map mode" },
|
{ "name": "mode", "type": "map mode" },
|
||||||
{ "name": "offset", "type": "uint64_t"},
|
{ "name": "offset", "type": "uint64_t"},
|
||||||
{ "name": "size", "type": "uint64_t"}
|
{ "name": "size", "type": "uint64_t"}
|
||||||
],
|
],
|
||||||
"buffer update mapped data": [
|
"buffer update mapped data": [
|
||||||
{ "name": "buffer id", "type": "ObjectId" },
|
{ "name": "buffer id", "type": "ObjectId", "id_type": "buffer" },
|
||||||
{ "name": "write data update info length", "type": "uint64_t" },
|
{ "name": "write data update info length", "type": "uint64_t" },
|
||||||
{ "name": "write data update info", "type": "uint8_t", "annotation": "const*", "length": "write data update info length", "skip_serialize": true},
|
{ "name": "write data update info", "type": "uint8_t", "annotation": "const*", "length": "write data update info length", "skip_serialize": true},
|
||||||
{ "name": "offset", "type": "uint64_t"},
|
{ "name": "offset", "type": "uint64_t"},
|
||||||
{ "name": "size", "type": "uint64_t"}
|
{ "name": "size", "type": "uint64_t"}
|
||||||
],
|
],
|
||||||
"device create buffer": [
|
"device create buffer": [
|
||||||
{ "name": "device id", "type": "ObjectId" },
|
{ "name": "device id", "type": "ObjectId", "id_type": "device" },
|
||||||
{ "name": "descriptor", "type": "buffer descriptor", "annotation": "const*" },
|
{ "name": "descriptor", "type": "buffer descriptor", "annotation": "const*" },
|
||||||
{ "name": "result", "type": "ObjectHandle", "handle_type": "buffer" },
|
{ "name": "result", "type": "ObjectHandle", "handle_type": "buffer" },
|
||||||
{ "name": "read handle create info length", "type": "uint64_t" },
|
{ "name": "read handle create info length", "type": "uint64_t" },
|
||||||
|
@ -42,19 +42,19 @@
|
||||||
{ "name": "write handle create info", "type": "uint8_t", "annotation": "const*", "length": "write handle create info length", "skip_serialize": true}
|
{ "name": "write handle create info", "type": "uint8_t", "annotation": "const*", "length": "write handle create info length", "skip_serialize": true}
|
||||||
],
|
],
|
||||||
"device create compute pipeline async": [
|
"device create compute pipeline async": [
|
||||||
{ "name": "device id", "type": "ObjectId" },
|
{ "name": "device id", "type": "ObjectId", "id_type": "device"},
|
||||||
{ "name": "request serial", "type": "uint64_t" },
|
{ "name": "request serial", "type": "uint64_t" },
|
||||||
{ "name": "pipeline object handle", "type": "ObjectHandle", "handle_type": "compute pipeline"},
|
{ "name": "pipeline object handle", "type": "ObjectHandle", "handle_type": "compute pipeline"},
|
||||||
{ "name": "descriptor", "type": "compute pipeline descriptor", "annotation": "const*"}
|
{ "name": "descriptor", "type": "compute pipeline descriptor", "annotation": "const*"}
|
||||||
],
|
],
|
||||||
"device create render pipeline async": [
|
"device create render pipeline async": [
|
||||||
{ "name": "device id", "type": "ObjectId" },
|
{ "name": "device id", "type": "ObjectId", "id_type": "device" },
|
||||||
{ "name": "request serial", "type": "uint64_t" },
|
{ "name": "request serial", "type": "uint64_t" },
|
||||||
{ "name": "pipeline object handle", "type": "ObjectHandle", "handle_type": "render pipeline"},
|
{ "name": "pipeline object handle", "type": "ObjectHandle", "handle_type": "render pipeline"},
|
||||||
{ "name": "descriptor", "type": "render pipeline descriptor", "annotation": "const*"}
|
{ "name": "descriptor", "type": "render pipeline descriptor", "annotation": "const*"}
|
||||||
],
|
],
|
||||||
"device pop error scope": [
|
"device pop error scope": [
|
||||||
{ "name": "device id", "type": "ObjectId" },
|
{ "name": "device id", "type": "ObjectId", "id_type": "device" },
|
||||||
{ "name": "request serial", "type": "uint64_t" }
|
{ "name": "request serial", "type": "uint64_t" }
|
||||||
],
|
],
|
||||||
"destroy object": [
|
"destroy object": [
|
||||||
|
@ -62,19 +62,19 @@
|
||||||
{ "name": "object id", "type": "ObjectId" }
|
{ "name": "object id", "type": "ObjectId" }
|
||||||
],
|
],
|
||||||
"queue on submitted work done": [
|
"queue on submitted work done": [
|
||||||
{ "name": "queue id", "type": "ObjectId" },
|
{ "name": "queue id", "type": "ObjectId", "id_type": "queue" },
|
||||||
{ "name": "signal value", "type": "uint64_t" },
|
{ "name": "signal value", "type": "uint64_t" },
|
||||||
{ "name": "request serial", "type": "uint64_t" }
|
{ "name": "request serial", "type": "uint64_t" }
|
||||||
],
|
],
|
||||||
"queue write buffer": [
|
"queue write buffer": [
|
||||||
{"name": "queue id", "type": "ObjectId" },
|
{"name": "queue id", "type": "ObjectId", "id_type": "queue" },
|
||||||
{"name": "buffer id", "type": "ObjectId" },
|
{"name": "buffer id", "type": "ObjectId", "id_type": "buffer" },
|
||||||
{"name": "buffer offset", "type": "uint64_t"},
|
{"name": "buffer offset", "type": "uint64_t"},
|
||||||
{"name": "data", "type": "uint8_t", "annotation": "const*", "length": "size", "wire_is_data_only": true},
|
{"name": "data", "type": "uint8_t", "annotation": "const*", "length": "size", "wire_is_data_only": true},
|
||||||
{"name": "size", "type": "uint64_t"}
|
{"name": "size", "type": "uint64_t"}
|
||||||
],
|
],
|
||||||
"queue write texture": [
|
"queue write texture": [
|
||||||
{"name": "queue id", "type": "ObjectId" },
|
{"name": "queue id", "type": "ObjectId", "id_type": "queue" },
|
||||||
{"name": "destination", "type": "image copy texture", "annotation": "const*"},
|
{"name": "destination", "type": "image copy texture", "annotation": "const*"},
|
||||||
{"name": "data", "type": "uint8_t", "annotation": "const*", "length": "data size", "wire_is_data_only": true},
|
{"name": "data", "type": "uint8_t", "annotation": "const*", "length": "data size", "wire_is_data_only": true},
|
||||||
{"name": "data size", "type": "uint64_t"},
|
{"name": "data size", "type": "uint64_t"},
|
||||||
|
@ -82,17 +82,17 @@
|
||||||
{"name": "writeSize", "type": "extent 3D", "annotation": "const*"}
|
{"name": "writeSize", "type": "extent 3D", "annotation": "const*"}
|
||||||
],
|
],
|
||||||
"shader module get compilation info": [
|
"shader module get compilation info": [
|
||||||
{ "name": "shader module id", "type": "ObjectId" },
|
{ "name": "shader module id", "type": "ObjectId", "id_type": "shader module" },
|
||||||
{ "name": "request serial", "type": "uint64_t" }
|
{ "name": "request serial", "type": "uint64_t" }
|
||||||
],
|
],
|
||||||
"instance request adapter": [
|
"instance request adapter": [
|
||||||
{ "name": "instance id", "type": "ObjectId" },
|
{ "name": "instance id", "type": "ObjectId", "id_type": "instance" },
|
||||||
{ "name": "request serial", "type": "uint64_t" },
|
{ "name": "request serial", "type": "uint64_t" },
|
||||||
{ "name": "adapter object handle", "type": "ObjectHandle", "handle_type": "adapter"},
|
{ "name": "adapter object handle", "type": "ObjectHandle", "handle_type": "adapter"},
|
||||||
{ "name": "options", "type": "request adapter options", "annotation": "const*" }
|
{ "name": "options", "type": "request adapter options", "annotation": "const*" }
|
||||||
],
|
],
|
||||||
"adapter request device": [
|
"adapter request device": [
|
||||||
{ "name": "adapter id", "type": "ObjectId" },
|
{ "name": "adapter id", "type": "ObjectId", "id_type": "adapter" },
|
||||||
{ "name": "request serial", "type": "uint64_t" },
|
{ "name": "request serial", "type": "uint64_t" },
|
||||||
{ "name": "device object handle", "type": "ObjectHandle", "handle_type": "device"},
|
{ "name": "device object handle", "type": "ObjectHandle", "handle_type": "device"},
|
||||||
{ "name": "descriptor", "type": "device descriptor", "annotation": "const*" }
|
{ "name": "descriptor", "type": "device descriptor", "annotation": "const*" }
|
||||||
|
|
|
@ -181,6 +181,7 @@ class RecordMember:
|
||||||
self.optional = optional
|
self.optional = optional
|
||||||
self.is_return_value = is_return_value
|
self.is_return_value = is_return_value
|
||||||
self.handle_type = None
|
self.handle_type = None
|
||||||
|
self.id_type = None
|
||||||
self.default_value = default_value
|
self.default_value = default_value
|
||||||
self.skip_serialize = skip_serialize
|
self.skip_serialize = skip_serialize
|
||||||
|
|
||||||
|
@ -188,6 +189,10 @@ class RecordMember:
|
||||||
assert self.type.dict_name == "ObjectHandle"
|
assert self.type.dict_name == "ObjectHandle"
|
||||||
self.handle_type = handle_type
|
self.handle_type = handle_type
|
||||||
|
|
||||||
|
def set_id_type(self, id_type):
|
||||||
|
assert self.type.dict_name == "ObjectId"
|
||||||
|
self.id_type = id_type
|
||||||
|
|
||||||
|
|
||||||
Method = namedtuple(
|
Method = namedtuple(
|
||||||
'Method', ['name', 'return_type', 'arguments', 'autolock', 'json_data'])
|
'Method', ['name', 'return_type', 'arguments', 'autolock', 'json_data'])
|
||||||
|
@ -308,6 +313,9 @@ def linked_record_members(json_data, types):
|
||||||
handle_type = m.get('handle_type')
|
handle_type = m.get('handle_type')
|
||||||
if handle_type:
|
if handle_type:
|
||||||
member.set_handle_type(types[handle_type])
|
member.set_handle_type(types[handle_type])
|
||||||
|
id_type = m.get('id_type')
|
||||||
|
if id_type:
|
||||||
|
member.set_id_type(types[id_type])
|
||||||
members.append(member)
|
members.append(member)
|
||||||
members_by_name[member.name.canonical_case()] = member
|
members_by_name[member.name.canonical_case()] = member
|
||||||
|
|
||||||
|
@ -331,6 +339,18 @@ def linked_record_members(json_data, types):
|
||||||
return members
|
return members
|
||||||
|
|
||||||
|
|
||||||
|
def mark_lengths_non_serializable_lpm(record_members):
|
||||||
|
# Remove member length values from command metadata,
|
||||||
|
# these are set to the length of the protobuf array.
|
||||||
|
for record_member in record_members:
|
||||||
|
lengths = set()
|
||||||
|
for member in record_member.members:
|
||||||
|
lengths.add(member.length)
|
||||||
|
|
||||||
|
for member in record_member.members:
|
||||||
|
if member in lengths:
|
||||||
|
member.skip_serialize = True
|
||||||
|
|
||||||
############################################################
|
############################################################
|
||||||
# PARSE
|
# PARSE
|
||||||
############################################################
|
############################################################
|
||||||
|
@ -376,7 +396,6 @@ def link_function(function, types):
|
||||||
function.arguments = linked_record_members(function.json_data['args'],
|
function.arguments = linked_record_members(function.json_data['args'],
|
||||||
types)
|
types)
|
||||||
|
|
||||||
|
|
||||||
# Sort structures so that if struct A has struct B as a member, then B is
|
# Sort structures so that if struct A has struct B as a member, then B is
|
||||||
# listed before A.
|
# listed before A.
|
||||||
#
|
#
|
||||||
|
@ -601,10 +620,21 @@ def compute_lpm_params(api_and_wire_params, lpm_json):
|
||||||
generated_commands.append(command)
|
generated_commands.append(command)
|
||||||
all_commands.append(command)
|
all_commands.append(command)
|
||||||
|
|
||||||
|
# Set all fields that are marked as the "length" of another field to
|
||||||
|
# skip_serialize. The values passed by libprotobuf-mutator will cause
|
||||||
|
# an instant crash during serialization if these don't match the length
|
||||||
|
# of the data they are passing. These values aren't used in
|
||||||
|
# deserialization.
|
||||||
|
mark_lengths_non_serializable_lpm(
|
||||||
|
api_and_wire_params['cmd_records']['command'])
|
||||||
|
mark_lengths_non_serializable_lpm(
|
||||||
|
api_and_wire_params['by_category']['structure'])
|
||||||
|
|
||||||
lpm_params['cmd_records'] = {
|
lpm_params['cmd_records'] = {
|
||||||
'proto_generated_commands': generated_commands,
|
'proto_generated_commands': generated_commands,
|
||||||
'proto_all_commands': all_commands,
|
'proto_all_commands': all_commands,
|
||||||
'cpp_generated_commands': generated_commands
|
'cpp_generated_commands': generated_commands,
|
||||||
|
'lpm_info': lpm_json.get("lpm_info")
|
||||||
}
|
}
|
||||||
|
|
||||||
return lpm_params
|
return lpm_params
|
||||||
|
@ -636,16 +666,28 @@ def as_protobufTypeLPM(member):
|
||||||
return member.type.name.CamelCase()
|
return member.type.name.CamelCase()
|
||||||
|
|
||||||
|
|
||||||
|
# Helper that generates names for protobuf grammars from contents
|
||||||
|
# of dawn*.json like files. example: membera
|
||||||
def as_protobufNameLPM(*names):
|
def as_protobufNameLPM(*names):
|
||||||
# `descriptor` is a reserved keyword in lpm
|
# `descriptor` is a reserved keyword in lib-protobuf-mutator
|
||||||
if (names[0].concatcase() == "descriptor"):
|
if (names[0].concatcase() == "descriptor"):
|
||||||
return "desc"
|
return "desc"
|
||||||
return as_varName(*names)
|
return as_varName(*names)
|
||||||
|
|
||||||
|
|
||||||
|
# Helper to generate member accesses within C++ of protobuf objects
|
||||||
|
# example: cmd.membera().memberb()
|
||||||
|
def as_protobufMemberNameLPM(*names):
|
||||||
|
# `descriptor` is a reserved keyword in lib-protobuf-mutator
|
||||||
|
if (names[0].concatcase() == "descriptor"):
|
||||||
|
return "desc"
|
||||||
|
return ''.join([name.concatcase().lower() for name in names])
|
||||||
|
|
||||||
|
|
||||||
def unreachable_code():
|
def unreachable_code():
|
||||||
assert False
|
assert False
|
||||||
|
|
||||||
|
|
||||||
#############################################################
|
#############################################################
|
||||||
# Generator
|
# Generator
|
||||||
#############################################################
|
#############################################################
|
||||||
|
@ -1152,8 +1194,10 @@ class MultiGeneratorFromDawnJSON(Generator):
|
||||||
fuzzer_params = compute_lpm_params(api_and_wire_params, lpm_json)
|
fuzzer_params = compute_lpm_params(api_and_wire_params, lpm_json)
|
||||||
|
|
||||||
lpm_params = [
|
lpm_params = [
|
||||||
RENDER_PARAMS_BASE, params_dawn_wire, {}, api_and_wire_params,
|
RENDER_PARAMS_BASE, params_dawn_wire, {
|
||||||
fuzzer_params
|
'as_protobufMemberName': as_protobufMemberNameLPM,
|
||||||
|
'unreachable_code': unreachable_code
|
||||||
|
}, api_and_wire_params, fuzzer_params
|
||||||
]
|
]
|
||||||
|
|
||||||
renders.append(
|
renders.append(
|
||||||
|
|
|
@ -12,8 +12,20 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
namespace DawnLPMFuzzer {
|
namespace DawnLPMFuzzer {
|
||||||
|
|
||||||
|
|
||||||
static constexpr int kInstanceObjectId = 1;
|
static constexpr int kInstanceObjectId = 1;
|
||||||
|
static constexpr uint32_t kInvalidObjectId = {{ cmd_records["lpm_info"]["invalid object id"] }};
|
||||||
|
|
||||||
|
{% for type in by_category["object"] %}
|
||||||
|
{% if type.name.get() in cmd_records["lpm_info"]["limits"] %}
|
||||||
|
static constexpr int k{{ type.name.CamelCase() }}Limit = {{ cmd_records["lpm_info"]["limits"][type.name.get()] }};
|
||||||
|
{% else %}
|
||||||
|
static constexpr int k{{ type.name.CamelCase() }}Limit = {{ cmd_records["lpm_info"]["limits"]["default"] }};
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
} // namespace DawnLPMFuzzer
|
} // namespace DawnLPMFuzzer
|
||||||
|
|
|
@ -12,31 +12,246 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
#include "dawn/fuzzers/lpmfuzz/DawnLPMSerializer_autogen.h"
|
#include "dawn/fuzzers/lpmfuzz/DawnLPMConstants_autogen.h"
|
||||||
#include "dawn/fuzzers/lpmfuzz/DawnLPMFuzzer.h"
|
#include "dawn/fuzzers/lpmfuzz/DawnLPMFuzzer.h"
|
||||||
|
#include "dawn/fuzzers/lpmfuzz/DawnLPMObjectStore.h"
|
||||||
|
#include "dawn/fuzzers/lpmfuzz/DawnLPMSerializer_autogen.h"
|
||||||
|
#include "dawn/webgpu.h"
|
||||||
|
#include "dawn/wire/BufferConsumer_impl.h"
|
||||||
|
#include "dawn/wire/ObjectHandle.h"
|
||||||
#include "dawn/wire/Wire.h"
|
#include "dawn/wire/Wire.h"
|
||||||
#include "dawn/wire/WireClient.h"
|
#include "dawn/wire/WireClient.h"
|
||||||
#include "dawn/wire/WireCmd_autogen.h"
|
#include "dawn/wire/WireCmd_autogen.h"
|
||||||
|
#include "dawn/wire/WireResult.h"
|
||||||
#include "dawn/wire/client/ApiObjects_autogen.h"
|
#include "dawn/wire/client/ApiObjects_autogen.h"
|
||||||
#include "dawn/webgpu.h"
|
|
||||||
#include "dawn/wire/client/Client.h"
|
|
||||||
|
|
||||||
|
|
||||||
namespace dawn::wire {
|
namespace dawn::wire {
|
||||||
|
|
||||||
void SerializedData(const fuzzing::Program& program, dawn::wire::ChunkedCommandSerializer serializer) {
|
//* Outputs an rvalue that's the number of elements a pointer member points to.
|
||||||
|
{% macro member_length(member, proto_accessor) -%}
|
||||||
|
{%- if member.length == "constant" -%}
|
||||||
|
{{member.constant_length}}u
|
||||||
|
{%- else -%}
|
||||||
|
{{proto_accessor}}().size()
|
||||||
|
{%- endif -%}
|
||||||
|
{%- endmacro %}
|
||||||
|
|
||||||
|
//* Outputs the type that will be used on the wire for the member
|
||||||
|
{% macro member_type(member) -%}
|
||||||
|
{{ assert(as_cType(member.type.name) != "size_t") }}
|
||||||
|
{{as_cType(member.type.name)}}
|
||||||
|
{%- endmacro %}
|
||||||
|
|
||||||
|
//* Outputs the conversion code to put `in` in `out`
|
||||||
|
{% macro convert_member(member, in, out, in_access="") %}
|
||||||
|
{% if member.type in by_category["structure"] %}
|
||||||
|
{{ convert_structure(member, in, out, in_access) }}
|
||||||
|
{% elif member.type in by_category["bitmask"] %}
|
||||||
|
{{ convert_bitmask(member, in, out, in_access) }}
|
||||||
|
{% elif member.type in by_category["enum"] %}
|
||||||
|
{{ convert_enum(member, in, out, in_access) }}
|
||||||
|
{% elif member.type in by_category["object"] %}
|
||||||
|
{{ convert_object(member, in, out, in_access) }}
|
||||||
|
{% elif member.type.name.get() == "ObjectId" %}
|
||||||
|
{{ convert_objectid(member, in, out, access) }}
|
||||||
|
{% elif member.type.name.get() == "ObjectHandle" %}
|
||||||
|
{{ convert_objecthandle(member, in, out, in_access) }}
|
||||||
|
{% else %}
|
||||||
|
{{out}} = {{in}}({{in_access}});
|
||||||
|
{% endif %}
|
||||||
|
{% endmacro %}
|
||||||
|
|
||||||
|
//* Helper functions for converting protobufs to specific types
|
||||||
|
{% macro convert_enum(member, in, out, in_access) %}
|
||||||
|
{{out}} = static_cast<{{ member_type(member) }}>(
|
||||||
|
{{in}}({{in_access}})
|
||||||
|
);
|
||||||
|
{% endmacro %}
|
||||||
|
|
||||||
|
{% macro convert_object(member, in, out, in_access) -%}
|
||||||
|
{{ out }} = reinterpret_cast<{{ as_cType(member.type.name) }}>(
|
||||||
|
objectStores[ObjectType::{{ member.type.name.CamelCase() }}].Lookup(
|
||||||
|
static_cast<ObjectId>(
|
||||||
|
{{in}}({{in_access}})
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
{%- endmacro %}
|
||||||
|
|
||||||
|
{% macro convert_objectid(member, in, out, in_access) -%}
|
||||||
|
{{ out }} = objectStores[ObjectType::{{ member.id_type.name.CamelCase() }}].Lookup(
|
||||||
|
static_cast<ObjectId>(
|
||||||
|
{{in}}({{ in_access}})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
{%- endmacro %}
|
||||||
|
|
||||||
|
{% macro convert_objecthandle(member, in, out, in_access) -%}
|
||||||
|
if (objectStores[ObjectType::{{ member.handle_type.name.CamelCase() }}].Size() < DawnLPMFuzzer::k{{ member.handle_type.name.CamelCase() }}Limit) {
|
||||||
|
{{ out }} = objectStores[ObjectType::{{ member.handle_type.name.CamelCase() }}].ReserveHandle();
|
||||||
|
} else {
|
||||||
|
{{ out }} = {0, 0};
|
||||||
|
}
|
||||||
|
{%- endmacro %}
|
||||||
|
|
||||||
|
{% macro convert_bitmask(member, in, out, in_access) -%}
|
||||||
|
{{ out }} = 0;
|
||||||
|
for (size_t bm = 0; bm < static_cast<size_t>({{ in }}().size()); bm++) {
|
||||||
|
{{ out }} |=
|
||||||
|
static_cast<{{ member_type(member) }}>(
|
||||||
|
{{ in }}(bm)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
{%- endmacro %}
|
||||||
|
|
||||||
|
{% macro convert_structure(member, in, out, in_access) -%}
|
||||||
|
// Serializing a Structure Recursively
|
||||||
|
WIRE_TRY({{member_type(member)}}ProtoConvert({{in}}({{in_access}}), &{{out}}, serializeBuffer, objectStores));
|
||||||
|
{%- endmacro %}
|
||||||
|
|
||||||
|
{% macro write_record_conversion_helpers(record, name, members, is_cmd) %}
|
||||||
|
{% set overrides = cmd_records["lpm_info"]["overrides"] %}
|
||||||
|
{% set overrides_key = record.name.canonical_case() %}
|
||||||
|
{% set name = record.name.CamelCase() %}
|
||||||
|
{% set Cmd = "Cmd" if is_cmd else "" %}
|
||||||
|
{% set WGPU = "WGPU" if not is_cmd else "" %}
|
||||||
|
|
||||||
|
WireResult {{WGPU}}{{name}}ProtoConvert(fuzzing::{{ name }} proto_record, {{WGPU}}{{ name }}{{ Cmd }} const *record, SerializeBuffer* serializeBuffer, PerObjectType<DawnLPMObjectStore> &objectStores) {
|
||||||
|
|
||||||
|
{{WGPU}}{{ name }}{{ Cmd }} *mutable_record = const_cast<{{WGPU}}{{ name }}{{ Cmd }} *>(record);
|
||||||
|
|
||||||
|
//* Some commands don't set any members.
|
||||||
|
DAWN_UNUSED(mutable_record);
|
||||||
|
|
||||||
|
//* Zero out any non-serializable values as they won't be set
|
||||||
|
{% for member in members if member.skip_serialize %}
|
||||||
|
{% set memberName = as_varName(member.name) %}
|
||||||
|
memset(&mutable_record->{{ memberName }}, 0, sizeof(mutable_record->{{ memberName }}));
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
//* Pass by Value handling. This mirrors WireCmd with some differences between
|
||||||
|
//* convert_member and serialize_member
|
||||||
|
{% for member in members if member.annotation == "value" if not member.skip_serialize %}
|
||||||
|
{% set memberName = as_varName(member.name) %}
|
||||||
|
{% set protoMember = as_protobufMemberName(member.name) %}
|
||||||
|
|
||||||
|
//* Major WireCmd Divergence: Some member values are hardcoded in dawn_lpm.json
|
||||||
|
{% if overrides_key in overrides and member.name.canonical_case() in overrides[overrides_key] %}
|
||||||
|
mutable_record->{{ memberName }} = {{ overrides[overrides_key][member.name.canonical_case()] }};
|
||||||
|
{% else %}
|
||||||
|
{{ convert_member(member, 'proto_record.' + protoMember, "mutable_record->" + memberName) }}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
//* Chained structures are currently not supported.
|
||||||
|
{% if record.extensible %}
|
||||||
|
mutable_record->nextInChain = nullptr;
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
//* Special handling for strings for now.
|
||||||
|
{% for member in members if member.length == "strlen" %}
|
||||||
|
{% set memberName = as_varName(member.name) %}
|
||||||
|
{
|
||||||
|
mutable_record->{{ memberName }} = "main";
|
||||||
|
}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
//* Pass by Pointer handling. This mirrors WireCmd with some divergences when handling
|
||||||
|
//* byte arrays.
|
||||||
|
{% for member in members if member.annotation != "value" and member.length != "strlen" and not member.skip_serialize %}
|
||||||
|
{% set memberName = as_varName(member.name) %}
|
||||||
|
{% set protoMember = as_protobufMemberName(member.name) %}
|
||||||
|
{% set protoAccess = "i" if member.length != "constant" or member.constant_length > 1 else "" %}
|
||||||
|
|
||||||
|
//* Major WireCmd Divergence: DawnLPM handles raw byte arrays uniquely
|
||||||
|
//* as they don't lead to new coverage, lead to OOMs when allocated with
|
||||||
|
//* an arbitrary size, and are difficult to work with in protobuf.
|
||||||
|
{% if member.type.name.get() == 'uint8_t' %}
|
||||||
|
{
|
||||||
|
const size_t kDataBufferLength = 128;
|
||||||
|
auto memberLength = kDataBufferLength;
|
||||||
|
|
||||||
|
{{member_type(member)}}* memberBuffer;
|
||||||
|
WIRE_TRY(serializeBuffer->NextN(memberLength, &memberBuffer));
|
||||||
|
memset(memberBuffer, 0, kDataBufferLength);
|
||||||
|
mutable_record->{{ memberName }} = memberBuffer;
|
||||||
|
|
||||||
|
{% if member.length != "constant" -%}
|
||||||
|
mutable_record->{{ member.length.name.camelCase() }} = memberLength;
|
||||||
|
{%- endif %}
|
||||||
|
}
|
||||||
|
{% else %}
|
||||||
|
{
|
||||||
|
auto memberLength = static_cast<unsigned int>({{member_length(member, "proto_record." + protoMember)}});
|
||||||
|
|
||||||
|
//* Needed for the edge cases in "external texture descriptor"
|
||||||
|
//* where we want to fuzzer to fill the fixed-length float arrays
|
||||||
|
//* with values, but the length of the protobuf buffer might not
|
||||||
|
//* be large enough for "src transfer function parameters".
|
||||||
|
{% if member.length == "constant" and member.constant_length > 1 %}
|
||||||
|
memberLength = std::min(memberLength, static_cast<unsigned int>({{"proto_record." + protoMember}}().size()));
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{{member_type(member)}}* memberBuffer;
|
||||||
|
WIRE_TRY(serializeBuffer->NextN(memberLength, &memberBuffer));
|
||||||
|
|
||||||
|
for (decltype(memberLength) i = 0; i < memberLength; ++i) {
|
||||||
|
{{convert_member(member, "proto_record." + protoMember, "memberBuffer[i]", protoAccess )}}
|
||||||
|
}
|
||||||
|
|
||||||
|
mutable_record->{{ memberName }} = memberBuffer;
|
||||||
|
|
||||||
|
//* Major WireCmd Divergence: Within the serializer the length member is
|
||||||
|
//* set by using record.length. Here we aren't receiving any data
|
||||||
|
//* and set it to the number of protobuf objects in proto_record.
|
||||||
|
{% if member.length != "constant" -%}
|
||||||
|
mutable_record->{{ member.length.name.camelCase() }} = memberLength;
|
||||||
|
{%- endif %}
|
||||||
|
}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
return WireResult::Success;
|
||||||
|
}
|
||||||
|
{% endmacro %}
|
||||||
|
|
||||||
|
//* Output structure conversion first because it is used by commands.
|
||||||
|
{% for type in by_category["structure"] %}
|
||||||
|
{% set name = as_cType(type.name) %}
|
||||||
|
{% if type.name.CamelCase() not in client_side_structures %}
|
||||||
|
{{ write_record_conversion_helpers(type, name, type.members, False) }}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
//* Output command conversion functions.
|
||||||
|
{% for command in cmd_records["cpp_generated_commands"] %}
|
||||||
|
{% set name = command.name.CamelCase() %}
|
||||||
|
{{ write_record_conversion_helpers(command, name, command.members, True) }}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
WireResult SerializedData(const fuzzing::Program& program, dawn::wire::ChunkedCommandSerializer serializer) {
|
||||||
DawnLPMObjectIdProvider provider;
|
DawnLPMObjectIdProvider provider;
|
||||||
|
PerObjectType<DawnLPMObjectStore> objectStores, &objectStoresRef = objectStores;
|
||||||
|
|
||||||
|
// Allocate a scoped buffer allocation
|
||||||
|
const size_t kMaxSerializeBufferSize = 65536;
|
||||||
|
std::unique_ptr<char[]> allocatedBuffer(
|
||||||
|
new char[kMaxSerializeBufferSize]()
|
||||||
|
);
|
||||||
|
|
||||||
for (const fuzzing::Command& command : program.commands()) {
|
for (const fuzzing::Command& command : program.commands()) {
|
||||||
switch (command.command_case()) {
|
switch (command.command_case()) {
|
||||||
|
|
||||||
{% for command in cmd_records["cpp_generated_commands"] %}
|
{% for command in cmd_records["cpp_generated_commands"] %}
|
||||||
case fuzzing::Command::k{{command.name.CamelCase()}}: {
|
{% set name = command.name.CamelCase() %}
|
||||||
{{ command.name.CamelCase() }}Cmd {{ 'cmd' }};
|
case fuzzing::Command::k{{name}}: {
|
||||||
// TODO(chromium:1374747): Populate command buffer with serialized code from generated
|
SerializeBuffer serializeBuffer(allocatedBuffer.get(), kMaxSerializeBufferSize);
|
||||||
// protobuf structures. Currently, this will nullptr-deref constantly.
|
{{ name }}Cmd *cmd = nullptr;
|
||||||
memset(&{{ 'cmd' }}, 0, sizeof({{ command.name.CamelCase() }}Cmd));
|
WIRE_TRY(serializeBuffer.Next(&cmd));
|
||||||
serializer.SerializeCommand(cmd, provider);
|
|
||||||
|
WIRE_TRY({{name}}ProtoConvert(command.{{ command.name.concatcase() }}(), cmd, &serializeBuffer, objectStoresRef));
|
||||||
|
|
||||||
|
serializer.SerializeCommand(*cmd, provider);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
@ -45,6 +260,8 @@ void SerializedData(const fuzzing::Program& program, dawn::wire::ChunkedCommandS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return WireResult::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace dawn::wire
|
} // namespace dawn::wire
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#include "dawn/fuzzers/lpmfuzz/dawn_lpm_autogen.pb.h"
|
#include "dawn/fuzzers/lpmfuzz/dawn_lpm_autogen.pb.h"
|
||||||
#include "dawn/wire/ChunkedCommandSerializer.h"
|
#include "dawn/wire/ChunkedCommandSerializer.h"
|
||||||
#include "dawn/wire/WireCmd_autogen.h"
|
#include "dawn/wire/WireCmd_autogen.h"
|
||||||
|
#include "dawn/wire/WireResult.h"
|
||||||
|
|
||||||
namespace dawn::wire {
|
namespace dawn::wire {
|
||||||
|
|
||||||
|
@ -38,7 +39,7 @@ class DawnLPMObjectIdProvider : public ObjectIdProvider {
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
void SerializedData(const fuzzing::Program& program,
|
WireResult SerializedData(const fuzzing::Program& program,
|
||||||
dawn::wire::ChunkedCommandSerializer serializer);
|
dawn::wire::ChunkedCommandSerializer serializer);
|
||||||
|
|
||||||
} // namespace dawn::wire
|
} // namespace dawn::wire
|
||||||
|
|
|
@ -17,6 +17,25 @@ package fuzzing;
|
||||||
|
|
||||||
import "third_party/dawn/src/dawn/fuzzers/lpmfuzz/dawn_custom_lpm.proto";
|
import "third_party/dawn/src/dawn/fuzzers/lpmfuzz/dawn_custom_lpm.proto";
|
||||||
|
|
||||||
|
|
||||||
|
// These are hardcoded limits for Dawn Object allocations based on type to help
|
||||||
|
// guide the fuzzer towards reusing existing objects.
|
||||||
|
{% for type in by_category["object"] %}
|
||||||
|
{% set type_key = type.name.canonical_case() %}
|
||||||
|
enum {{ type.name.CamelCase() }}Id {
|
||||||
|
{% if type_key in cmd_records["lpm_info"]["limits"] %}
|
||||||
|
{% for n in range(cmd_records["lpm_info"]["limits"][type_key]) %}
|
||||||
|
{{ type.name.SNAKE_CASE() }}_{{ loop.index }} = {{ loop.index }};
|
||||||
|
{% endfor %}
|
||||||
|
{% else %}
|
||||||
|
{% for n in range(cmd_records["lpm_info"]["limits"]["default"]) %}
|
||||||
|
{{ type.name.SNAKE_CASE() }}_{{ loop.index }} = {{ loop.index }};
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
INVALID_{{ type.name.SNAKE_CASE() }} = {{ cmd_records["lpm_info"]["invalid object id"] }};
|
||||||
|
};
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
{% for type in by_category["enum"] %}
|
{% for type in by_category["enum"] %}
|
||||||
enum {{as_cppType(type.name)}} {
|
enum {{as_cppType(type.name)}} {
|
||||||
{% for value in type.values %}
|
{% for value in type.values %}
|
||||||
|
@ -38,23 +57,33 @@ import "third_party/dawn/src/dawn/fuzzers/lpmfuzz/dawn_custom_lpm.proto";
|
||||||
{% macro lift_string_proto_member(member, count) -%}
|
{% macro lift_string_proto_member(member, count) -%}
|
||||||
required string {{ as_protobufNameLPM(member.name) }} = {{ count.value }};
|
required string {{ as_protobufNameLPM(member.name) }} = {{ count.value }};
|
||||||
{% set count.value = count.value + 1 %}
|
{% set count.value = count.value + 1 %}
|
||||||
|
|
||||||
{%- endmacro %}
|
{%- endmacro %}
|
||||||
|
|
||||||
|
|
||||||
{% macro lift_float_array_proto_member(member, count) -%}
|
{% macro lift_float_array_proto_member(member, count) -%}
|
||||||
repeated float {{ as_varName(member.name) }} = {{ count.value }};
|
repeated float {{ as_varName(member.name) }} = {{ count.value }};
|
||||||
{% set count.value = count.value + 1 %}
|
{% set count.value = count.value + 1 %}
|
||||||
{%- endmacro %}
|
{%- endmacro %}
|
||||||
|
|
||||||
|
|
||||||
|
{% macro lift_object_member(member, count) %}
|
||||||
|
{{ member.type.name.CamelCase() }}Id {{ as_protobufNameLPM(member.name) }}
|
||||||
|
{% endmacro %}
|
||||||
|
|
||||||
|
|
||||||
|
{% macro lift_objectid_member(member, count) %}
|
||||||
|
{{ member.id_type.name.CamelCase() }}Id {{ as_protobufNameLPM(member.name) }}
|
||||||
|
{% endmacro %}
|
||||||
|
|
||||||
|
|
||||||
{% macro lift_varlength_proto_member(member, count) -%}
|
{% macro lift_varlength_proto_member(member, count) -%}
|
||||||
{% if member.type in by_category["object"] %}
|
{% if member.type in by_category["object"] %}
|
||||||
repeated uint32 {{ as_protobufNameLPM(member.name) }} = {{ count.value }};
|
repeated {{ lift_object_member(member, count) }} = {{ count.value }};
|
||||||
{% set count.value = count.value + 1 %}
|
{% set count.value = count.value + 1 %}
|
||||||
{% elif member.type.name.get() == "object id" %}
|
{% elif member.type.name.get() == "object id" %}
|
||||||
repeated uint32 {{ as_protobufNameLPM(member.name) }} = {{ count.value }};
|
repeated {{ lift_objectid_member(member, count) }} = {{ count.value }};
|
||||||
{% set count.value = count.value + 1 %}
|
{% set count.value = count.value + 1 %}
|
||||||
{% elif member.type.name.get() == "uint8_t" %}
|
|
||||||
// Skip over byte arrays in protobuf, handled by DawnSerializer
|
|
||||||
{% else %}
|
{% else %}
|
||||||
repeated {{ as_protobufTypeLPM(member) }} {{ as_protobufNameLPM(member.name) }} = {{ count.value }};
|
repeated {{ as_protobufTypeLPM(member) }} {{ as_protobufNameLPM(member.name) }} = {{ count.value }};
|
||||||
{% set count.value = count.value + 1 %}
|
{% set count.value = count.value + 1 %}
|
||||||
|
@ -73,10 +102,10 @@ import "third_party/dawn/src/dawn/fuzzers/lpmfuzz/dawn_custom_lpm.proto";
|
||||||
required {{ as_protobufTypeLPM(member) }} {{ as_protobufNameLPM(member.name) }} = {{ count.value }};
|
required {{ as_protobufTypeLPM(member) }} {{ as_protobufNameLPM(member.name) }} = {{ count.value }};
|
||||||
{% set count.value = count.value + 1 %}
|
{% set count.value = count.value + 1 %}
|
||||||
{% elif member.type in by_category["object"] %}
|
{% elif member.type in by_category["object"] %}
|
||||||
required uint32 {{ as_protobufNameLPM(member.name) }} = {{ count.value }};
|
required {{ lift_object_member(member, count) }} = {{ count.value }};
|
||||||
{% set count.value = count.value + 1 %}
|
{% set count.value = count.value + 1 %}
|
||||||
{% elif member.type.name.get() == "ObjectId" %}
|
{% elif member.type.name.get() == "ObjectId" %}
|
||||||
required uint32 {{ as_protobufNameLPM(member.name) }} = {{ count.value }};
|
required {{ lift_objectid_member(member, count) }} = {{ count.value }};
|
||||||
{% set count.value = count.value + 1 %}
|
{% set count.value = count.value + 1 %}
|
||||||
{% elif member.type.name.get() == "ObjectHandle" %}
|
{% elif member.type.name.get() == "ObjectHandle" %}
|
||||||
// Skips object handles while lifting dawn.json to protobuf because
|
// Skips object handles while lifting dawn.json to protobuf because
|
||||||
|
@ -105,7 +134,7 @@ import "third_party/dawn/src/dawn/fuzzers/lpmfuzz/dawn_custom_lpm.proto";
|
||||||
{% else %}
|
{% else %}
|
||||||
// There shouldn't be any other pass-by-pointer types in
|
// 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
|
// dawn*.json, if any are added we would like to know at compile time
|
||||||
{{ unreachable_code }}
|
{{ unreachable_code() }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
|
|
|
@ -19,4 +19,4 @@ enum ObjectType {
|
||||||
{% for type in by_category["object"] %}
|
{% for type in by_category["object"] %}
|
||||||
{{ type.name.CamelCase() }} = {{ loop.index - 1}};
|
{{ type.name.CamelCase() }} = {{ loop.index - 1}};
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
};
|
};
|
||||||
|
|
|
@ -175,6 +175,8 @@ if (is_dawn_lpm_fuzzer && build_with_chromium && dawn_use_swiftshader) {
|
||||||
"lpmfuzz/DawnLPMFuzzer.cpp",
|
"lpmfuzz/DawnLPMFuzzer.cpp",
|
||||||
"lpmfuzz/DawnLPMFuzzer.h",
|
"lpmfuzz/DawnLPMFuzzer.h",
|
||||||
"lpmfuzz/DawnLPMFuzzerAndVulkanBackend.cpp",
|
"lpmfuzz/DawnLPMFuzzerAndVulkanBackend.cpp",
|
||||||
|
"lpmfuzz/DawnLPMObjectStore.cpp",
|
||||||
|
"lpmfuzz/DawnLPMObjectStore.h",
|
||||||
]
|
]
|
||||||
|
|
||||||
deps = [
|
deps = [
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
#include "dawn/webgpu_cpp.h"
|
#include "dawn/webgpu_cpp.h"
|
||||||
#include "dawn/wire/ChunkedCommandSerializer.h"
|
#include "dawn/wire/ChunkedCommandSerializer.h"
|
||||||
#include "dawn/wire/WireClient.h"
|
#include "dawn/wire/WireClient.h"
|
||||||
|
#include "dawn/wire/WireResult.h"
|
||||||
#include "dawn/wire/WireServer.h"
|
#include "dawn/wire/WireServer.h"
|
||||||
#include "testing/libfuzzer/libfuzzer_exports.h"
|
#include "testing/libfuzzer/libfuzzer_exports.h"
|
||||||
|
|
||||||
|
@ -106,14 +107,14 @@ int Run(const fuzzing::Program& program, bool (*AdapterSupported)(const dawn::na
|
||||||
dawn::wire::ChunkedCommandSerializer(mCommandBuffer);
|
dawn::wire::ChunkedCommandSerializer(mCommandBuffer);
|
||||||
mCommandBuffer->SetHandler(wireServer.get());
|
mCommandBuffer->SetHandler(wireServer.get());
|
||||||
|
|
||||||
dawn::wire::SerializedData(program, mSerializer);
|
dawn::wire::WireResult result = dawn::wire::SerializedData(program, mSerializer);
|
||||||
|
|
||||||
mCommandBuffer->Flush();
|
mCommandBuffer->Flush();
|
||||||
|
|
||||||
// Note: Deleting the server will release all created objects.
|
// Note: Deleting the server will release all created objects.
|
||||||
// Deleted devices will wait for idle on destruction.
|
// Deleted devices will wait for idle on destruction.
|
||||||
wireServer = nullptr;
|
wireServer = nullptr;
|
||||||
return 0;
|
return result == dawn::wire::WireResult::FatalError;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace DawnLPMFuzzer
|
} // namespace DawnLPMFuzzer
|
||||||
|
|
|
@ -0,0 +1,100 @@
|
||||||
|
// Copyright 2023 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 <algorithm>
|
||||||
|
#include <functional>
|
||||||
|
#include <limits>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "dawn/fuzzers/lpmfuzz/DawnLPMConstants_autogen.h"
|
||||||
|
#include "dawn/fuzzers/lpmfuzz/DawnLPMObjectStore.h"
|
||||||
|
#include "dawn/fuzzers/lpmfuzz/DawnLPMSerializer_autogen.h"
|
||||||
|
#include "dawn/wire/ObjectHandle.h"
|
||||||
|
|
||||||
|
namespace dawn::wire {
|
||||||
|
|
||||||
|
DawnLPMObjectStore::DawnLPMObjectStore() {
|
||||||
|
mCurrentId = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ObjectHandle DawnLPMObjectStore::ReserveHandle() {
|
||||||
|
if (mFreeHandles.empty()) {
|
||||||
|
Insert(mCurrentId);
|
||||||
|
return {mCurrentId++, 0};
|
||||||
|
}
|
||||||
|
ObjectHandle handle = mFreeHandles.back();
|
||||||
|
mFreeHandles.pop_back();
|
||||||
|
Insert(handle.id);
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DawnLPMObjectStore::Insert(ObjectId id) {
|
||||||
|
std::vector<ObjectId>::iterator iter =
|
||||||
|
std::lower_bound(mObjects.begin(), mObjects.end(), id, std::greater<ObjectId>());
|
||||||
|
mObjects.insert(iter, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DawnLPMObjectStore::Free(ObjectId id) {
|
||||||
|
if (id == DawnLPMFuzzer::kInvalidObjectId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < mObjects.size(); i++) {
|
||||||
|
if (mObjects[i] == id) {
|
||||||
|
mFreeHandles.push_back({id, 0});
|
||||||
|
mObjects.erase(mObjects.begin() + i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Consistent hashing inspired map for fuzzer state.
|
||||||
|
* If we store the Dawn objects in a hash table mapping FuzzInt -> ObjectId
|
||||||
|
* then it would be highly unlikely that any subsequence DestroyObject command
|
||||||
|
* would come up with an ID that would correspond to a valid ObjectId in the
|
||||||
|
* hash table.
|
||||||
|
*
|
||||||
|
* One solution is to modulo the FuzzInt with the length of the hash table, but
|
||||||
|
* it does not work well with libfuzzer's minimization techniques because
|
||||||
|
* deleting a single ObjectId from the hash table changes the index of every
|
||||||
|
* entry from then on.
|
||||||
|
*
|
||||||
|
* So we use consistent hashing. we take the entry in the table that
|
||||||
|
* has the next highest id (wrapping when there is no higher entry).
|
||||||
|
*/
|
||||||
|
ObjectId DawnLPMObjectStore::Lookup(uint32_t id) const {
|
||||||
|
// CreateBindGroup relies on sending invalid object ids
|
||||||
|
if (id == DawnLPMFuzzer::kInvalidObjectId) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto iter = std::lower_bound(mObjects.begin(), mObjects.end(), id, std::greater<ObjectId>());
|
||||||
|
if (iter != mObjects.end()) {
|
||||||
|
return *iter;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wrap to 0
|
||||||
|
iter = std::lower_bound(mObjects.begin(), mObjects.end(), 0, std::greater<ObjectId>());
|
||||||
|
if (iter != mObjects.end()) {
|
||||||
|
return *iter;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t DawnLPMObjectStore::Size() const {
|
||||||
|
return mObjects.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace dawn::wire
|
|
@ -0,0 +1,43 @@
|
||||||
|
// Copyright 2023 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.
|
||||||
|
|
||||||
|
#ifndef SRC_DAWN_WIRE_CLIENT_DAWNLPMOBJECTSTORE_H_
|
||||||
|
#define SRC_DAWN_WIRE_CLIENT_DAWNLPMOBJECTSTORE_H_
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "dawn/wire/client/ObjectBase.h"
|
||||||
|
|
||||||
|
namespace dawn::wire {
|
||||||
|
|
||||||
|
class DawnLPMObjectStore {
|
||||||
|
public:
|
||||||
|
DawnLPMObjectStore();
|
||||||
|
|
||||||
|
ObjectHandle ReserveHandle();
|
||||||
|
void Free(ObjectId handle);
|
||||||
|
void Insert(ObjectId handle);
|
||||||
|
ObjectId Lookup(ObjectId id) const;
|
||||||
|
size_t Size() const;
|
||||||
|
|
||||||
|
uint32_t mCurrentId;
|
||||||
|
std::vector<ObjectHandle> mFreeHandles;
|
||||||
|
|
||||||
|
// TODO(tiszka): refactor into a vector of ObjectHandles
|
||||||
|
std::vector<ObjectId> mObjects;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace dawn::wire
|
||||||
|
|
||||||
|
#endif // SRC_DAWN_WIRE_CLIENT_DAWNLPMOBJECTSTORE_H_
|
|
@ -23,10 +23,26 @@
|
||||||
],
|
],
|
||||||
|
|
||||||
"blocklisted_cmds": [
|
"blocklisted_cmds": [
|
||||||
"device create render pipeline",
|
|
||||||
"device create render pipeline async",
|
|
||||||
"surface descriptor from windows core window",
|
"surface descriptor from windows core window",
|
||||||
"surface descriptor from windows swap chain panel",
|
"surface descriptor from windows swap chain panel",
|
||||||
"surface descriptor from canvas html selector"
|
"surface descriptor from canvas html selector"
|
||||||
]
|
],
|
||||||
|
|
||||||
|
"lpm_info": {
|
||||||
|
"overrides": {
|
||||||
|
"instance request adapter": {
|
||||||
|
"instance id": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"limits": {
|
||||||
|
"adapter": 2,
|
||||||
|
"bind group": 512,
|
||||||
|
"bind group layout": 512,
|
||||||
|
"device": 2,
|
||||||
|
"default": 16
|
||||||
|
},
|
||||||
|
|
||||||
|
"invalid object id": 2147483647
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue