/* * Copyright (c) 2026-present, the Ladybird developers. * * SPDX-License-Identifier: BSD-2-Clause */ #include #include #include #include #include #include #include namespace JS::Bytecode { static StringView validation_error_kind_to_string(JS::FFI::ValidationErrorKind kind) { switch (kind) { case JS::FFI::ValidationErrorKind::Ok: return "Ok"sv; case JS::FFI::ValidationErrorKind::BufferNotAligned: return "BufferNotAligned"sv; case JS::FFI::ValidationErrorKind::InstructionMisaligned: return "InstructionMisaligned"sv; case JS::FFI::ValidationErrorKind::UnknownOpcode: return "UnknownOpcode"sv; case JS::FFI::ValidationErrorKind::TruncatedInstruction: return "TruncatedInstruction"sv; case JS::FFI::ValidationErrorKind::InvalidLength: return "InvalidLength"sv; case JS::FFI::ValidationErrorKind::OperandOutOfRange: return "OperandOutOfRange"sv; case JS::FFI::ValidationErrorKind::OperandInvalid: return "OperandInvalid"sv; case JS::FFI::ValidationErrorKind::LabelNotAtInstructionBoundary: return "LabelNotAtInstructionBoundary"sv; case JS::FFI::ValidationErrorKind::IdentifierIndexOutOfRange: return "IdentifierIndexOutOfRange"sv; case JS::FFI::ValidationErrorKind::StringIndexOutOfRange: return "StringIndexOutOfRange"sv; case JS::FFI::ValidationErrorKind::PropertyKeyIndexOutOfRange: return "PropertyKeyIndexOutOfRange"sv; case JS::FFI::ValidationErrorKind::RegexIndexOutOfRange: return "RegexIndexOutOfRange"sv; case JS::FFI::ValidationErrorKind::PropertyLookupCacheIndexOutOfRange: return "PropertyLookupCacheIndexOutOfRange"sv; case JS::FFI::ValidationErrorKind::GlobalVariableCacheIndexOutOfRange: return "GlobalVariableCacheIndexOutOfRange"sv; case JS::FFI::ValidationErrorKind::TemplateObjectCacheIndexOutOfRange: return "TemplateObjectCacheIndexOutOfRange"sv; case JS::FFI::ValidationErrorKind::ObjectShapeCacheIndexOutOfRange: return "ObjectShapeCacheIndexOutOfRange"sv; case JS::FFI::ValidationErrorKind::ObjectPropertyIteratorCacheIndexOutOfRange: return "ObjectPropertyIteratorCacheIndexOutOfRange"sv; case JS::FFI::ValidationErrorKind::SharedFunctionDataIndexOutOfRange: return "SharedFunctionDataIndexOutOfRange"sv; case JS::FFI::ValidationErrorKind::ClassBlueprintIndexOutOfRange: return "ClassBlueprintIndexOutOfRange"sv; case JS::FFI::ValidationErrorKind::EnumOutOfRange: return "EnumOutOfRange"sv; case JS::FFI::ValidationErrorKind::BasicBlockOffsetInvalid: return "BasicBlockOffsetInvalid"sv; case JS::FFI::ValidationErrorKind::ExceptionHandlerStartInvalid: return "ExceptionHandlerStartInvalid"sv; case JS::FFI::ValidationErrorKind::ExceptionHandlerEndInvalid: return "ExceptionHandlerEndInvalid"sv; case JS::FFI::ValidationErrorKind::ExceptionHandlerHandlerInvalid: return "ExceptionHandlerHandlerInvalid"sv; case JS::FFI::ValidationErrorKind::ExceptionHandlerRangeInvalid: return "ExceptionHandlerRangeInvalid"sv; case JS::FFI::ValidationErrorKind::SourceMapOffsetInvalid: return "SourceMapOffsetInvalid"sv; } VERIFY_NOT_REACHED(); } ErrorOr validate_bytecode(Executable const& executable, CacheState cache_state) { JS::FFI::FFIValidatorBounds bounds { .number_of_registers = executable.number_of_registers, .number_of_locals = static_cast(executable.local_variable_names.size()), .number_of_constants = static_cast(executable.constants.size()), .number_of_arguments = executable.number_of_arguments, .identifier_table_size = static_cast(executable.identifier_table->identifiers().size()), .string_table_size = static_cast(executable.string_table->size()), .property_key_table_size = static_cast(executable.property_key_table->property_keys().size()), // The regex table is not consulted at runtime; m_regex_index fields // are skipped during validation. .regex_table_size = 0, .property_lookup_cache_count = static_cast(executable.property_lookup_caches.size()), .global_variable_cache_count = static_cast(executable.global_variable_caches.size()), .template_object_cache_count = static_cast(executable.template_object_caches.size()), .object_shape_cache_count = static_cast(executable.object_shape_caches.size()), .object_property_iterator_cache_count = static_cast(executable.object_property_iterator_caches.size()), .class_blueprint_count = static_cast(executable.class_blueprints.size()), .shared_function_data_count = static_cast(executable.shared_function_data.size()), .before_cache_fixup = cache_state == CacheState::BeforeFixup, }; // Project Executable's exception handlers down to plain offsets; the // structural metadata's source-position parts aren't validated here. Vector handler_offsets; handler_offsets.ensure_capacity(executable.exception_handlers.size()); for (auto const& h : executable.exception_handlers) { handler_offsets.append({ .start = static_cast(h.start_offset), .end = static_cast(h.end_offset), .handler = static_cast(h.handler_offset), }); } Vector basic_block_offsets; basic_block_offsets.ensure_capacity(executable.basic_block_start_offsets.size()); for (auto offset : executable.basic_block_start_offsets) basic_block_offsets.append(static_cast(offset)); Vector source_map_offsets; source_map_offsets.ensure_capacity(executable.source_map.size()); for (auto const& entry : executable.source_map) source_map_offsets.append(entry.bytecode_offset); JS::FFI::FFIValidatorExtras extras { .basic_block_offsets = basic_block_offsets.data(), .basic_block_count = basic_block_offsets.size(), .exception_handlers = handler_offsets.data(), .exception_handler_count = handler_offsets.size(), .source_map_offsets = source_map_offsets.data(), .source_map_count = source_map_offsets.size(), }; JS::FFI::FFIValidationError error {}; auto ok = rust_validate_bytecode( executable.bytecode.data(), executable.bytecode.size(), &bounds, &extras, &error); if (ok) return {}; auto kind = validation_error_kind_to_string(error.kind); dbgln("Bytecode validation failed at offset {} (opcode {}): {}", error.offset, error.opcode, kind); return AK::Error::from_string_view(kind); } }