diff options
author | Oliver Chang <ochang@chromium.org> | 2018-04-27 10:42:12 +0900 |
---|---|---|
committer | Qijiang Fan <fqj@google.com> | 2020-06-05 09:11:57 +0900 |
commit | 69300be5b4e1491b05e1a925087d7e5d2538acd8 (patch) | |
tree | 1d51eba930bdaa4c02bf32136ece59362d41d010 | |
parent | f80e945bfb6470ae787dbf6258d7dc2bee263139 (diff) | |
download | libchrome-69300be5b4e1491b05e1a925087d7e5d2538acd8.tar.gz |
Mojo JS bindings: Generate additional methods to aid fuzzing.
This generates additional methods on each struct/union to allow
mutation/generation, and to get/set any handles and interfaces that
are part of the struct/union.
These are only generated when |enable_ipc_fuzzer| is set. When this is
set, |cpp_only| is also ignored.
Bug: 607649
Change-Id: I2097610ced9de186c098232207afff11ff0a3745
Reviewed-on: https://chromium-review.googlesource.com/1011882
Reviewed-by: Ken Rockot <rockot@chromium.org>
Commit-Queue: Oliver Chang <ochang@chromium.org>
Cr-Commit-Position: refs/heads/master@{#554258}
CrOS-Libchrome-Original-Commit: bc38c9eab0eb99690a2926b9f943b6dcc17740de
9 files changed, 340 insertions, 5 deletions
diff --git a/mojo/public/tools/bindings/generators/js_templates/fuzzing.tmpl b/mojo/public/tools/bindings/generators/js_templates/fuzzing.tmpl new file mode 100644 index 0000000000..fb535fe03a --- /dev/null +++ b/mojo/public/tools/bindings/generators/js_templates/fuzzing.tmpl @@ -0,0 +1,125 @@ +{%- macro get_handle_deps(kind, name) -%} +{%- if kind|is_struct_kind or kind|is_union_kind -%} +{{name}}.getHandleDeps() +{%- elif kind|is_array_kind -%} +[].concat.apply([], {{name}}.map(function(val) { + if (val) { + return {{get_handle_deps(kind.kind, 'val')|indent(4)}}; + } + return []; +})) +{%- elif kind|is_map_kind -%} +[].concat.apply([], Array.from({{name}}.values()).map(function(val) { + if (val) { + return {{get_handle_deps(kind.value_kind, 'val')|indent(4)}}; + } + return []; +})) +{%- elif kind|is_any_handle_or_interface_kind -%} + ["{{kind|fuzz_handle_name}}"] +{%- else -%} + [] +{%- endif -%} +{%- endmacro -%} + +{%- macro set_handles(kind, name) -%} +{%- if kind|is_struct_kind or kind|is_union_kind -%} +idx = {{name}}.setHandlesInternal_(handles, idx) +{%- elif kind|is_array_kind -%} +{{name}}.forEach(function(val) { + {{set_handles(kind.kind, 'val')|indent(2)}}; +}) +{%- elif kind|is_map_kind -%} +{{name}}.forEach(function(val) { + {{set_handles(kind.value_kind, 'val')|indent(2)}}; +}) +{%- elif kind|is_any_handle_or_interface_kind -%} +{{name}} = handles[idx++]; +{%- endif -%} +{%- endmacro -%} + +{%- macro build_call(obj, operation, type, name) -%} +{%- if name -%} +{{obj}}.{{operation}}{{type}}({{((name,) + varargs)|join(', ')}}) +{%- else -%} +{{obj}}.{{operation}}{{type}}({{varargs|join(', ')}}) +{%- endif -%} +{%- endmacro -%} + +{%- macro generate_or_mutate_enum(obj, operation, kind, name) -%} +{%- if kind.max_value is not none -%} +{{build_call(obj, operation, 'Enum', name, '0', kind.max_value)}} +{%- else -%} +{{build_call(obj, operation, 'Enum', name)}} +{%- endif %} +{%- endmacro -%} + +{%- macro generate_or_mutate_array(obj, operation, kind, name) -%} +{%- if operation == 'mutate' -%} +{{obj}}.{{operation}}Array({{name}}, function(val) { + return {{generate_or_mutate(obj, operation, kind.kind, 'val')|indent(2)}}; +}) +{%- else -%} +{{obj}}.{{operation}}Array(function() { + return {{generate_or_mutate(obj, operation, kind.kind)|indent(2)}}; +}) +{%- endif -%} +{%- endmacro -%} + +{%- macro generate_or_mutate_map(obj, operation, kind, name) -%} +{%- if operation == 'mutate' -%} +{{obj}}.{{operation}}Map({{name}}, + function(val) { + return {{generate_or_mutate(obj, operation, kind.key_kind, 'val')|indent(4)}}; + }, + function(val) { + return {{generate_or_mutate(obj, operation, kind.value_kind, 'val')|indent(4)}}; + }) +{%- else -%} +{{obj}}.{{operation}}Map( + function() { + return {{generate_or_mutate(obj, operation, kind.key_kind)|indent(4)}}; + }, + function() { + return {{generate_or_mutate(obj, operation, kind.value_kind)|indent(4)}}; + }) +{%- endif -%} +{%- endmacro -%} + +{%- macro generate_or_mutate_primitive(obj, operation, kind, name) -%} +{%- if kind|is_reference_kind -%} +{{build_call(obj, operation, kind|primitive_to_fuzz_type, name, kind.is_nullable|to_js_boolean)}} +{%- else -%} +{{build_call(obj, operation, kind|primitive_to_fuzz_type, name)}} +{%- endif -%} +{%- endmacro -%} + +{%- macro generate_or_mutate_interface(obj, operation, kind, name) -%} +{%- if kind|is_interface_request_kind -%} +{{build_call(obj, operation, 'InterfaceRequest', name, '"' ~ kind.kind.module.namespace ~ '.' ~ kind.kind.name ~ '"', kind.is_nullable|to_js_boolean)}} +{%- elif kind|is_interface_kind -%} +{{build_call(obj, operation, 'Interface', name, '"' ~ kind.module.namespace ~ '.' ~ kind.name ~ '"', kind.is_nullable|to_js_boolean)}} +{%- elif kind|is_associated_interface_request_kind -%} +{{build_call(obj, operation, 'AssociatedInterfaceRequest', name, '"' ~ kind.kind.module.namespace ~ '.' ~ kind.kind.name ~ '"', kind.is_nullable|to_js_boolean)}} +{%- elif kind|is_associated_interface_kind -%} +{{build_call(obj, operation, 'AssociatedInterface', name, '"' ~ kind.kind.module.namespace ~ '.' ~ kind.kind.name ~ '"', kind.is_nullable|to_js_boolean)}} +{%- endif -%} +{%- endmacro -%} + +{%- macro generate_or_mutate(obj, operation, kind, name='') -%} +{%- if kind|is_primitive_kind -%} +{{generate_or_mutate_primitive(obj, operation, kind, name)}} +{%- elif kind|is_any_interface_kind -%} +{{generate_or_mutate_interface(obj, operation, kind, name)}} +{%- elif kind|is_enum_kind -%} +{{generate_or_mutate_enum(obj, operation, kind, name)}} +{%- elif kind|is_struct_kind -%} +{{build_call(obj, operation, 'Struct', kind.module.namespace ~ '.' ~ kind.name, kind.is_nullable|to_js_boolean)}} +{%- elif kind|is_union_kind -%} +{{build_call(obj, operation, 'Union', kind.module.namespace ~ '.' ~ kind.name, kind.is_nullable|to_js_boolean)}} +{%- elif kind|is_array_kind -%} +{{generate_or_mutate_array(obj, operation, kind, name)}} +{%- elif kind|is_map_kind -%} +{{generate_or_mutate_map(obj, operation, kind, name)}} +{%- endif -%} +{%- endmacro -%} diff --git a/mojo/public/tools/bindings/generators/js_templates/interface_definition.tmpl b/mojo/public/tools/bindings/generators/js_templates/interface_definition.tmpl index 988ca0af4d..356fc5bb12 100644 --- a/mojo/public/tools/bindings/generators/js_templates/interface_definition.tmpl +++ b/mojo/public/tools/bindings/generators/js_templates/interface_definition.tmpl @@ -226,6 +226,18 @@ params.{{parameter.name}}{% if not loop.last %}, {% endif -%} {%- else %} validateResponse: null, {%- endif %} +{%- if generate_fuzzing %} + mojomId: '{{module.path}}', + fuzzMethods: { + {%- for method in interface.methods %} + {%- set interface_method_id = + interface.mojom_name ~ "_" ~ method.mojom_name %} + {{ method.name }}: { + params: {{interface_method_id}}_Params, + }, + {%- endfor %} + }, +{%- endif %} }; {#--- Interface Constants #} {%- for constant in interface.constants %} diff --git a/mojo/public/tools/bindings/generators/js_templates/module_definition.tmpl b/mojo/public/tools/bindings/generators/js_templates/module_definition.tmpl index 21165ddcad..a6736b5658 100644 --- a/mojo/public/tools/bindings/generators/js_templates/module_definition.tmpl +++ b/mojo/public/tools/bindings/generators/js_templates/module_definition.tmpl @@ -17,7 +17,7 @@ {#--- Union definitions #} {%- from "union_definition.tmpl" import union_def %} {%- for union in unions %} -{{union_def(union)|indent(2)}} +{{union_def(union, generate_fuzzing)|indent(2)}} {%- endfor %} {#--- Interface definitions #} diff --git a/mojo/public/tools/bindings/generators/js_templates/struct_definition.tmpl b/mojo/public/tools/bindings/generators/js_templates/struct_definition.tmpl index 7bffe60885..176f1b8909 100644 --- a/mojo/public/tools/bindings/generators/js_templates/struct_definition.tmpl +++ b/mojo/public/tools/bindings/generators/js_templates/struct_definition.tmpl @@ -30,6 +30,59 @@ } }; +{#--- Fuzzing #} +{%- if generate_fuzzing %} + {{struct.name}}.generate = function(generator_) { + var generated = new {{struct.name}}; +{%- for field in struct.fields %} +{%- if not field.kind|is_any_handle_or_interface_kind %} +{%- from "fuzzing.tmpl" import generate_or_mutate %} + generated.{{field.name}} = {{generate_or_mutate('generator_', 'generate', field.kind)|indent(4)}}; +{%- endif %} +{%- endfor %} + return generated; + }; + + {{struct.name}}.prototype.mutate = function(mutator_) { +{%- for field in struct.fields %} +{%- if not field.kind|is_any_handle_or_interface_kind %} + if (mutator_.chooseMutateField()) { +{%- from "fuzzing.tmpl" import generate_or_mutate %} + this.{{field.name}} = {{generate_or_mutate('mutator_', 'mutate', field.kind, 'this.' ~ field.name)|indent(6)}}; + } +{%- endif %} +{%- endfor %} + return this; + }; + + {{struct.name}}.prototype.getHandleDeps = function() { + var handles = []; +{%- for field in struct.fields %} +{%- if field.kind|contains_handles_or_interfaces %} + if (this.{{field.name}} !== null) { +{%- from "fuzzing.tmpl" import get_handle_deps %} + Array.prototype.push.apply(handles, {{get_handle_deps(field.kind, 'this.' ~ field.name)|indent(6)}}); + } +{%- endif %} +{%- endfor %} + return handles; + }; + + {{struct.name}}.prototype.setHandles = function() { + this.setHandlesInternal_(arguments, 0); + }; + + {{struct.name}}.prototype.setHandlesInternal_ = function(handles, idx) { +{%- for field in struct.fields %} +{%- if field.kind|contains_handles_or_interfaces %} +{%- from "fuzzing.tmpl" import set_handles %} + {{set_handles(field.kind, 'this.' ~ field.name)|indent(4)}}; +{%- endif %} +{%- endfor %} + return idx; + }; +{%- endif %} + {#--- Validation #} {{struct.name}}.validate = function(messageValidator, offset) { diff --git a/mojo/public/tools/bindings/generators/js_templates/union_definition.tmpl b/mojo/public/tools/bindings/generators/js_templates/union_definition.tmpl index dde69aeffd..d00e641db6 100644 --- a/mojo/public/tools/bindings/generators/js_templates/union_definition.tmpl +++ b/mojo/public/tools/bindings/generators/js_templates/union_definition.tmpl @@ -1,4 +1,4 @@ -{%- macro union_def(union) %} +{%- macro union_def(union, generate_fuzzing=false) %} function {{union.name}}(value) { this.initDefault_(); this.initValue_(value); @@ -39,6 +39,71 @@ function {{union.name}}(value) { this[keys[0]] = value[keys[0]]; } +{% if generate_fuzzing %} +{{union.name}}.generate = function(generator_) { + var generated = new {{union.name}}; + var generators = [ +{%- for field in union.fields %} + { + field: "{{field.name}}", + +{%- if not field.kind|is_any_handle_or_interface_kind %} +{%- from "fuzzing.tmpl" import generate_or_mutate %} + generator: function() { return {{generate_or_mutate('generator_', 'generate', field.kind)|indent(6)}}; }, +{%- endif %} + }, +{%- endfor %} + ]; + + var result = generator_.generateUnionField(generators); + generated[result.field] = result.value; + return generated; +} + +{{union.name}}.prototype.mutate = function(mutator_) { + var mutators = [ +{%- for field in union.fields %} + { + field: "{{field.name}}", + +{%- if not field.kind|is_any_handle_or_interface_kind %} +{%- from "fuzzing.tmpl" import generate_or_mutate %} + mutator: function() { return {{generate_or_mutate('mutator_', 'mutate', field.kind, 'this.' ~ field.name)|indent(6)}}; }, +{%- endif %} + }, +{%- endfor %} + ]; + + var result = mutator_.mutateUnionField(this, mutators); + generated[result.field] = result.value; + return this; +} + +{{union.name}}.prototype.getHandleDeps = function() { +{%- for field in union.fields %} +{%- if field.kind|contains_handles_or_interfaces %} + if (this.$tag == {{union.name}}.Tags.{{field.name}}) { +{%- from "fuzzing.tmpl" import get_handle_deps %} + return {{get_handle_deps(field.kind, 'this.' ~ field.name)}}; + } +{%- endif %} +{%- endfor %} + return []; +} + +{{union.name}}.prototype.setHandles = function() { +{%- for field in union.fields %} +{%- if field.kind|contains_handles_or_interfaces %} + if (this.$tag == {{union.name}}.Tags.{{field.name}}) { +{%- from "fuzzing.tmpl" import set_handles %} + return {{set_handles(field.kind, 'this.' ~ field.name)}}; + } +{%- endif %} +{%- endfor %} + return []; +} +{%- endif %} + {%- for field in union.fields %} Object.defineProperty({{union.name}}.prototype, "{{field.name}}", { get: function() { diff --git a/mojo/public/tools/bindings/generators/mojom_js_generator.py b/mojo/public/tools/bindings/generators/mojom_js_generator.py index 5a4de762ec..724268d665 100644 --- a/mojo/public/tools/bindings/generators/mojom_js_generator.py +++ b/mojo/public/tools/bindings/generators/mojom_js_generator.py @@ -136,6 +136,32 @@ _js_reserved_keywords = [ 'yield', ] +_primitive_kind_to_fuzz_type = { + mojom.BOOL: "Bool", + mojom.INT8: "Int8", + mojom.UINT8: "Uint8", + mojom.INT16: "Int16", + mojom.UINT16: "Uint16", + mojom.INT32: "Int32", + mojom.UINT32: "Uint32", + mojom.FLOAT: "Float", + mojom.INT64: "Int64", + mojom.UINT64: "Uint64", + mojom.DOUBLE: "Double", + mojom.STRING: "String", + mojom.NULLABLE_STRING: "String", + mojom.HANDLE: "Handle", + mojom.DCPIPE: "DataPipeConsumer", + mojom.DPPIPE: "DataPipeProducer", + mojom.MSGPIPE: "MessagePipe", + mojom.SHAREDBUFFER: "SharedBuffer", + mojom.NULLABLE_HANDLE: "Handle", + mojom.NULLABLE_DCPIPE: "DataPipeConsumer", + mojom.NULLABLE_DPPIPE: "DataPipeProducer", + mojom.NULLABLE_MSGPIPE: "MessagePipe", + mojom.NULLABLE_SHAREDBUFFER: "SharedBuffer", +} + def JavaScriptPayloadSize(packed): packed_fields = packed.packed_fields @@ -207,6 +233,7 @@ class Generator(generator.Generator): "module": self.module, "structs": self.module.structs + self._GetStructsFromMethods(), "unions": self.module.unions, + "generate_fuzzing": self.generate_fuzzing, } @staticmethod @@ -231,10 +258,12 @@ class Generator(generator.Generator): "is_bool_kind": mojom.IsBoolKind, "is_enum_kind": mojom.IsEnumKind, "is_any_handle_kind": mojom.IsAnyHandleKind, + "is_any_interface_kind": mojom.IsAnyInterfaceKind, "is_interface_kind": mojom.IsInterfaceKind, "is_interface_request_kind": mojom.IsInterfaceRequestKind, "is_map_kind": mojom.IsMapKind, "is_object_kind": mojom.IsObjectKind, + "is_reference_kind": mojom.IsReferenceKind, "is_string_kind": mojom.IsStringKind, "is_struct_kind": mojom.IsStructKind, "is_union_kind": mojom.IsUnionKind, @@ -253,6 +282,11 @@ class Generator(generator.Generator): "validate_struct_params": self._JavaScriptValidateStructParams, "validate_union_params": self._JavaScriptValidateUnionParams, "sanitize_identifier": self._JavaScriptSanitizeIdentifier, + "contains_handles_or_interfaces": mojom.ContainsHandlesOrInterfaces, + "fuzz_handle_name": self._FuzzHandleName, + "is_primitive_kind": self._IsPrimitiveKind, + "primitive_to_fuzz_type": self._PrimitiveToFuzzType, + "to_js_boolean": self._ToJsBoolean, } return js_filters @@ -540,3 +574,37 @@ class Generator(generator.Generator): if method.response_param_struct is not None: result.append(method.response_param_struct) return result + + def _FuzzHandleName(self, kind): + if mojom.IsInterfaceRequestKind(kind): + return '{0}.{1}Request'.format(kind.kind.module.namespace, + kind.kind.name) + elif mojom.IsInterfaceKind(kind): + return '{0}.{1}Ptr'.format(kind.module.namespace, + kind.name) + elif mojom.IsAssociatedInterfaceRequestKind(kind): + return '{0}.{1}AssociatedRequest'.format(kind.kind.module.namespace, + kind.kind.name) + elif mojom.IsAssociatedInterfaceKind(kind): + return '{0}.{1}AssociatedPtr'.format(kind.kind.module.namespace, + kind.kind.name) + elif mojom.IsSharedBufferKind(kind): + return 'handle<shared_buffer>' + elif mojom.IsDataPipeConsumerKind(kind): + return 'handle<data_pipe_consumer>' + elif mojom.IsDataPipeProducerKind(kind): + return 'handle<data_pipe_producer>' + elif mojom.IsMessagePipeKind(kind): + return 'handle<message_pipe>' + + def _ToJsBoolean(self, value): + if value: + return 'true' + + return 'false' + + def _IsPrimitiveKind(self, kind): + return kind in mojom.PRIMITIVES + + def _PrimitiveToFuzzType(self, kind): + return _primitive_kind_to_fuzz_type[kind] diff --git a/mojo/public/tools/bindings/mojom.gni b/mojo/public/tools/bindings/mojom.gni index 317d8c0fd3..11f6395591 100644 --- a/mojo/public/tools/bindings/mojom.gni +++ b/mojo/public/tools/bindings/mojom.gni @@ -13,6 +13,7 @@ import("//build/config/chrome_build.gni") import("//build/config/nacl/config.gni") import("//components/nacl/features.gni") import("//third_party/jinja2/jinja2.gni") +import("//tools/ipc_fuzzer/ipc_fuzzer.gni") declare_args() { # Indicates whether typemapping should be supported in this build @@ -1128,7 +1129,7 @@ template("mojom") { } } - if (!defined(invoker.cpp_only) || !invoker.cpp_only) { + if (enable_ipc_fuzzer || !defined(invoker.cpp_only) || !invoker.cpp_only) { if (defined(invoker.sources)) { generator_js_target_name = "${target_name}_js__generator" generator_js_outputs = [ @@ -1170,6 +1171,10 @@ template("mojom") { inputs += message_scrambling_inputs args += message_scrambling_args } + + if (enable_ipc_fuzzer) { + args += [ "--generate_fuzzing" ] + } } } diff --git a/mojo/public/tools/bindings/mojom_bindings_generator.py b/mojo/public/tools/bindings/mojom_bindings_generator.py index 4117629e99..affbe79d91 100755 --- a/mojo/public/tools/bindings/mojom_bindings_generator.py +++ b/mojo/public/tools/bindings/mojom_bindings_generator.py @@ -214,7 +214,8 @@ class MojomProcessor(object): support_lazy_serialization=args.support_lazy_serialization, disallow_native_types=args.disallow_native_types, disallow_interfaces=args.disallow_interfaces, - generate_message_ids=args.generate_message_ids) + generate_message_ids=args.generate_message_ids, + generate_fuzzing=args.generate_fuzzing) filtered_args = [] if hasattr(generator_module, 'GENERATOR_PREFIX'): prefix = '--' + generator_module.GENERATOR_PREFIX + '_' @@ -466,6 +467,10 @@ def main(): help="Generates only the message IDs header for C++ bindings. Note that " "this flag only matters if --generate_non_variant_code is also " "specified.", action="store_true") + generate_parser.add_argument( + "--generate_fuzzing", + action="store_true", + help="Generates additional bindings for fuzzing in JS.") generate_parser.set_defaults(func=_Generate) precompile_parser = subparsers.add_parser("precompile", diff --git a/mojo/public/tools/bindings/pylib/mojom/generate/generator.py b/mojo/public/tools/bindings/pylib/mojom/generate/generator.py index c7acbb791b..acf029f6a1 100644 --- a/mojo/public/tools/bindings/pylib/mojom/generate/generator.py +++ b/mojo/public/tools/bindings/pylib/mojom/generate/generator.py @@ -164,7 +164,8 @@ class Generator(object): js_bindings_mode="new", export_attribute=None, export_header=None, generate_non_variant_code=False, support_lazy_serialization=False, disallow_native_types=False, - disallow_interfaces=False, generate_message_ids=False): + disallow_interfaces=False, generate_message_ids=False, + generate_fuzzing=False): self.module = module self.output_dir = output_dir self.typemap = typemap or {} @@ -180,6 +181,7 @@ class Generator(object): self.disallow_native_types = disallow_native_types self.disallow_interfaces = disallow_interfaces self.generate_message_ids = generate_message_ids + self.generate_fuzzing = generate_fuzzing def Write(self, contents, filename): if self.output_dir is None: |