2021-11-01 01:36:35 +03:30
/*
* Copyright ( c ) 2021 , Ali Mohammad Pur < mpfard @ serenityos . org >
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
# pragma once
2024-03-11 16:54:37 +01:00
# include <AK/COWVector.h>
2023-01-02 21:07:18 +01:00
# include <AK/Debug.h>
2024-07-18 03:41:06 +02:00
# include <AK/RedBlackTree.h>
2022-03-19 22:27:35 +03:30
# include <AK/SourceLocation.h>
# include <AK/Tuple.h>
2023-03-06 17:39:59 +01:00
# include <AK/Vector.h>
2021-11-01 01:36:35 +03:30
# include <LibWasm/Forward.h>
# include <LibWasm/Types.h>
namespace Wasm {
struct Context {
2024-07-18 03:41:06 +02:00
struct RefRBTree : RefCounted < RefRBTree > {
RedBlackTree < size_t , FunctionIndex > tree ;
} ;
2024-03-11 16:54:37 +01:00
COWVector < FunctionType > types ;
COWVector < FunctionType > functions ;
COWVector < TableType > tables ;
COWVector < MemoryType > memories ;
COWVector < GlobalType > globals ;
COWVector < ValueType > elements ;
COWVector < bool > datas ;
COWVector < ValueType > locals ;
2024-06-10 12:42:13 -07:00
Optional < u32 > data_count ;
2024-07-18 03:41:06 +02:00
RefPtr < RefRBTree > references { make_ref_counted < RefRBTree > ( ) } ;
2021-11-01 01:36:35 +03:30
size_t imported_function_count { 0 } ;
} ;
struct ValidationError : public Error {
2023-12-16 17:49:34 +03:30
ValidationError ( ByteString error )
2023-04-27 21:21:19 +03:30
: Error ( Error : : from_string_view ( error . view ( ) ) )
2021-11-01 01:36:35 +03:30
, error_string ( move ( error ) )
{
}
2023-12-16 17:49:34 +03:30
ByteString error_string ;
2021-11-01 01:36:35 +03:30
} ;
class Validator {
AK_MAKE_NONCOPYABLE ( Validator ) ;
AK_MAKE_NONMOVABLE ( Validator ) ;
public :
Validator ( ) = default ;
[ [ nodiscard ] ] Validator fork ( ) const
{
return Validator { m_context } ;
}
// Module
ErrorOr < void , ValidationError > validate ( Module & ) ;
ErrorOr < void , ValidationError > validate ( ImportSection const & ) ;
ErrorOr < void , ValidationError > validate ( ExportSection const & ) ;
ErrorOr < void , ValidationError > validate ( StartSection const & ) ;
ErrorOr < void , ValidationError > validate ( DataSection const & ) ;
ErrorOr < void , ValidationError > validate ( ElementSection const & ) ;
ErrorOr < void , ValidationError > validate ( GlobalSection const & ) ;
ErrorOr < void , ValidationError > validate ( MemorySection const & ) ;
ErrorOr < void , ValidationError > validate ( TableSection const & ) ;
ErrorOr < void , ValidationError > validate ( CodeSection const & ) ;
ErrorOr < void , ValidationError > validate ( FunctionSection const & ) { return { } ; }
ErrorOr < void , ValidationError > validate ( DataCountSection const & ) { return { } ; }
ErrorOr < void , ValidationError > validate ( TypeSection const & ) { return { } ; }
ErrorOr < void , ValidationError > validate ( CustomSection const & ) { return { } ; }
ErrorOr < void , ValidationError > validate ( TypeIndex index ) const
{
if ( index . value ( ) < m_context . types . size ( ) )
return { } ;
return Errors : : invalid ( " TypeIndex " sv ) ;
}
ErrorOr < void , ValidationError > validate ( FunctionIndex index ) const
{
if ( index . value ( ) < m_context . functions . size ( ) )
return { } ;
return Errors : : invalid ( " FunctionIndex " sv ) ;
}
ErrorOr < void , ValidationError > validate ( MemoryIndex index ) const
{
if ( index . value ( ) < m_context . memories . size ( ) )
return { } ;
return Errors : : invalid ( " MemoryIndex " sv ) ;
}
ErrorOr < void , ValidationError > validate ( ElementIndex index ) const
{
if ( index . value ( ) < m_context . elements . size ( ) )
return { } ;
return Errors : : invalid ( " ElementIndex " sv ) ;
}
ErrorOr < void , ValidationError > validate ( DataIndex index ) const
{
if ( index . value ( ) < m_context . datas . size ( ) )
return { } ;
return Errors : : invalid ( " DataIndex " sv ) ;
}
ErrorOr < void , ValidationError > validate ( GlobalIndex index ) const
{
if ( index . value ( ) < m_context . globals . size ( ) )
return { } ;
return Errors : : invalid ( " GlobalIndex " sv ) ;
}
ErrorOr < void , ValidationError > validate ( LabelIndex index ) const
{
2024-06-11 21:25:58 -07:00
if ( index . value ( ) < m_frames . size ( ) )
2021-11-01 01:36:35 +03:30
return { } ;
return Errors : : invalid ( " LabelIndex " sv ) ;
}
ErrorOr < void , ValidationError > validate ( LocalIndex index ) const
{
if ( index . value ( ) < m_context . locals . size ( ) )
return { } ;
return Errors : : invalid ( " LocalIndex " sv ) ;
}
ErrorOr < void , ValidationError > validate ( TableIndex index ) const
{
if ( index . value ( ) < m_context . tables . size ( ) )
return { } ;
return Errors : : invalid ( " TableIndex " sv ) ;
}
2024-06-13 07:02:57 -07:00
enum class FrameKind {
Block ,
Loop ,
If ,
Else ,
Function ,
} ;
struct Frame {
FunctionType type ;
FrameKind kind ;
size_t initial_size ;
// Stack polymorphism is handled with this field
bool unreachable { false } ;
Vector < ValueType > const & labels ( ) const
{
return kind ! = FrameKind : : Loop ? type . results ( ) : type . parameters ( ) ;
}
} ;
2021-11-01 01:36:35 +03:30
// Instructions
struct StackEntry {
StackEntry ( ValueType type )
: concrete_type ( type )
, is_known ( true )
{
}
explicit StackEntry ( )
: concrete_type ( ValueType : : I32 )
, is_known ( false )
{
}
bool is_of_kind ( ValueType : : Kind kind ) const
{
if ( is_known )
return concrete_type . kind ( ) = = kind ;
return true ;
}
bool is_numeric ( ) const { return ! is_known | | concrete_type . is_numeric ( ) ; }
bool is_reference ( ) const { return ! is_known | | concrete_type . is_reference ( ) ; }
bool operator = = ( ValueType const & other ) const
{
if ( is_known )
return concrete_type = = other ;
return true ;
}
bool operator = = ( StackEntry const & other ) const
{
if ( is_known & & other . is_known )
return other . concrete_type = = concrete_type ;
return true ;
}
ValueType concrete_type ;
bool is_known { true } ;
} ;
// This is a wrapper that can model "polymorphic" stacks,
// by treating unknown stack entries as a potentially infinite number of entries
2025-08-06 08:03:24 +02:00
class Stack {
2022-03-19 22:27:35 +03:30
template < typename , typename >
friend struct AK : : Formatter ;
2021-11-01 01:36:35 +03:30
public :
2025-08-06 08:03:24 +02:00
explicit Stack ( Vector < Frame > const & frames )
2024-06-13 07:02:57 -07:00
: m_frames ( frames )
{
}
2025-08-06 08:03:24 +02:00
bool is_empty ( ) const { return m_entries . is_empty ( ) ; }
auto & last ( ) const { return m_entries . last ( ) ; }
auto & last ( ) { return m_entries . last ( ) ; }
auto & at ( size_t index ) const { return m_entries . at ( index ) ; }
auto & at ( size_t index ) { return m_entries . at ( index ) ; }
size_t size ( ) const { return m_entries . size ( ) ; }
void resize ( size_t size )
{
m_entries . resize ( size ) ;
m_max_known_size = max ( m_max_known_size , size ) ;
}
2021-11-01 01:36:35 +03:30
2024-06-13 07:02:57 -07:00
ErrorOr < StackEntry , ValidationError > take_last ( )
2021-11-01 01:36:35 +03:30
{
2024-06-13 07:02:57 -07:00
if ( size ( ) = = m_frames . last ( ) . initial_size & & m_frames . last ( ) . unreachable )
return StackEntry ( ) ;
if ( size ( ) = = m_frames . last ( ) . initial_size )
return Errors : : invalid ( " stack state " sv , " <any> " sv , " <nothing> " sv ) ;
2025-08-06 08:03:24 +02:00
return m_entries . take_last ( ) ;
2021-11-01 01:36:35 +03:30
}
void append ( StackEntry entry )
{
2025-08-06 08:03:24 +02:00
m_entries . append ( entry ) ;
2025-06-06 14:12:28 +02:00
m_max_known_size = max ( m_max_known_size , size ( ) ) ;
2021-11-01 01:36:35 +03:30
}
2024-06-13 07:02:57 -07:00
ErrorOr < StackEntry , ValidationError > take ( ValueType type , SourceLocation location = SourceLocation : : current ( ) )
2022-03-19 22:27:35 +03:30
{
2024-06-13 07:02:57 -07:00
auto type_on_stack = TRY ( take_last ( ) ) ;
2022-03-19 22:27:35 +03:30
if ( type_on_stack ! = type )
2022-07-11 17:32:29 +00:00
return Errors : : invalid ( " stack state " sv , type , type_on_stack , location ) ;
2022-03-19 22:27:35 +03:30
2024-06-13 07:02:57 -07:00
return type_on_stack ;
2022-03-19 22:27:35 +03:30
}
template < auto . . . kinds >
ErrorOr < void , ValidationError > take ( SourceLocation location = SourceLocation : : current ( ) )
{
2024-06-13 07:02:57 -07:00
for ( auto kind : { kinds . . . } )
TRY ( take ( Wasm : : ValueType ( kind ) , location ) ) ;
return { } ;
2022-03-19 22:27:35 +03:30
}
2023-06-12 13:04:22 +03:30
template < auto . . . kinds >
ErrorOr < void , ValidationError > take_and_put ( Wasm : : ValueType : : Kind kind , SourceLocation location = SourceLocation : : current ( ) )
{
2024-06-13 07:02:57 -07:00
TRY ( take < kinds . . . > ( location ) ) ;
2023-06-12 13:04:22 +03:30
append ( Wasm : : ValueType ( kind ) ) ;
2024-06-13 07:02:57 -07:00
return { } ;
2023-06-12 13:04:22 +03:30
}
2025-06-06 14:12:28 +02:00
Vector < StackEntry > release_vector ( )
{
m_max_known_size = 0 ;
2025-08-06 08:03:24 +02:00
return exchange ( m_entries , Vector < StackEntry > { } ) ;
2025-06-06 14:12:28 +02:00
}
size_t max_known_size ( ) const { return m_max_known_size ; }
2021-11-01 01:36:35 +03:30
private :
2025-08-06 08:03:24 +02:00
Vector < StackEntry > m_entries ;
2024-06-13 07:02:57 -07:00
Vector < Frame > const & m_frames ;
2025-06-06 14:12:28 +02:00
size_t m_max_known_size { 0 } ;
2021-11-01 01:36:35 +03:30
} ;
struct ExpressionTypeResult {
Vector < StackEntry > result_types ;
bool is_constant { false } ;
} ;
ErrorOr < ExpressionTypeResult , ValidationError > validate ( Expression const & , Vector < ValueType > const & ) ;
ErrorOr < void , ValidationError > validate ( Instruction const & instruction , Stack & stack , bool & is_constant ) ;
2023-06-12 13:04:22 +03:30
template < u64 opcode >
2021-11-01 01:36:35 +03:30
ErrorOr < void , ValidationError > validate_instruction ( Instruction const & , Stack & stack , bool & is_constant ) ;
// Types
2024-05-30 21:51:26 -07:00
ErrorOr < void , ValidationError > validate ( Limits const & , u64 bound ) ; // n <= bound && m? <= bound
2021-11-01 01:36:35 +03:30
ErrorOr < FunctionType , ValidationError > validate ( BlockType const & ) ;
ErrorOr < void , ValidationError > validate ( FunctionType const & ) { return { } ; }
ErrorOr < void , ValidationError > validate ( TableType const & ) ;
ErrorOr < void , ValidationError > validate ( MemoryType const & ) ;
ErrorOr < void , ValidationError > validate ( GlobalType const & ) { return { } ; }
private :
explicit Validator ( Context context )
: m_context ( move ( context ) )
{
}
struct Errors {
2023-12-16 17:49:34 +03:30
static ValidationError invalid ( StringView name ) { return ByteString : : formatted ( " Invalid {} " , name ) ; }
2021-11-01 01:36:35 +03:30
template < typename Expected , typename Given >
2022-03-19 22:27:35 +03:30
static ValidationError invalid ( StringView name , Expected expected , Given given , SourceLocation location = SourceLocation : : current ( ) )
2021-11-01 01:36:35 +03:30
{
2022-03-19 22:27:35 +03:30
if constexpr ( WASM_VALIDATOR_DEBUG )
2023-12-16 17:49:34 +03:30
return ByteString : : formatted ( " Invalid {} in {}, expected {} but got {} " , name , find_instruction_name ( location ) , expected , given ) ;
2022-03-19 22:27:35 +03:30
else
2023-12-16 17:49:34 +03:30
return ByteString : : formatted ( " Invalid {}, expected {} but got {} " , name , expected , given ) ;
2021-11-01 01:36:35 +03:30
}
template < typename . . . Args >
static ValidationError non_conforming_types ( StringView name , Args . . . args )
{
2023-12-16 17:49:34 +03:30
return ByteString : : formatted ( " Non-conforming types for {}: {} " , name , Vector { args . . . } ) ;
2021-11-01 01:36:35 +03:30
}
2023-12-16 17:49:34 +03:30
static ValidationError duplicate_export_name ( StringView name ) { return ByteString : : formatted ( " Duplicate exported name '{}' " , name ) ; }
2024-06-10 15:56:33 -07:00
static ValidationError multiple_start_sections ( ) { return ByteString ( " Found multiple start sections " sv ) ; }
2024-06-13 07:02:57 -07:00
static ValidationError stack_height_mismatch ( Stack const & stack , size_t expected_height ) { return ByteString : : formatted ( " Stack height mismatch, got {} but expected length {} " , stack , expected_height ) ; }
2021-11-01 01:36:35 +03:30
template < typename T , typename U , typename V >
2023-12-16 17:49:34 +03:30
static ValidationError out_of_bounds ( StringView name , V value , T min , U max ) { return ByteString : : formatted ( " Value {} for {} is out of bounds ({},{}) " , value , name , min , max ) ; }
2021-11-01 01:36:35 +03:30
2022-03-19 22:27:35 +03:30
template < typename . . . Expected >
static ValidationError invalid_stack_state ( Stack const & stack , Tuple < Expected . . . > expected , SourceLocation location = SourceLocation : : current ( ) )
{
constexpr size_t count = expected . size ( ) ;
StringBuilder builder ;
if constexpr ( WASM_VALIDATOR_DEBUG )
builder . appendff ( " Invalid stack state in {}: " , find_instruction_name ( location ) ) ;
else
builder . appendff ( " Invalid stack state in <unknown>: " ) ;
2022-07-11 17:32:29 +00:00
builder . append ( " Expected [ " sv ) ;
2022-03-19 22:27:35 +03:30
expected . apply_as_args ( [ & ] < typename . . . Ts > ( Ts const & . . . args ) {
( builder . appendff ( " {} " , args ) , . . . ) ;
} ) ;
2022-07-11 17:32:29 +00:00
builder . append ( " ], but found [ " sv ) ;
2022-03-19 22:27:35 +03:30
2024-06-13 07:02:57 -07:00
auto actual_size = stack . size ( ) ;
2022-03-19 22:27:35 +03:30
for ( size_t i = 1 ; i < = min ( count , actual_size ) ; + + i ) {
auto & entry = stack . at ( actual_size - i ) ;
if ( entry . is_known ) {
builder . appendff ( " {} " , entry . concrete_type ) ;
} else {
builder . appendff ( " <polymorphic stack> " ) ;
break ;
}
}
2022-07-11 20:10:18 +00:00
builder . append ( ' ] ' ) ;
2023-12-16 17:49:34 +03:30
return { builder . to_byte_string ( ) } ;
2022-03-19 22:27:35 +03:30
}
private :
2023-12-16 17:49:34 +03:30
static ByteString find_instruction_name ( SourceLocation const & ) ;
2021-11-01 01:36:35 +03:30
} ;
Context m_context ;
2024-06-11 21:25:58 -07:00
Vector < Frame > m_frames ;
2024-03-11 16:54:37 +01:00
COWVector < GlobalType > m_globals_without_internal_globals ;
2021-11-01 01:36:35 +03:30
} ;
}
template < >
struct AK : : Formatter < Wasm : : Validator : : StackEntry > : public AK : : Formatter < StringView > {
2021-11-16 01:15:21 +01:00
ErrorOr < void > format ( FormatBuilder & builder , Wasm : : Validator : : StackEntry const & value )
2021-11-01 01:36:35 +03:30
{
if ( value . is_known )
return Formatter < StringView > : : format ( builder , Wasm : : ValueType : : kind_name ( value . concrete_type . kind ( ) ) ) ;
2021-11-16 01:15:21 +01:00
return Formatter < StringView > : : format ( builder , " <unknown> " sv ) ;
2021-11-01 01:36:35 +03:30
}
} ;
2022-03-19 22:27:35 +03:30
template < >
struct AK : : Formatter < Wasm : : Validator : : Stack > : public AK : : Formatter < Vector < Wasm : : Validator : : StackEntry > > {
ErrorOr < void > format ( FormatBuilder & builder , Wasm : : Validator : : Stack const & value )
{
2025-08-06 08:03:24 +02:00
return Formatter < Vector < Wasm : : Validator : : StackEntry > > : : format ( builder , value . m_entries ) ;
2022-03-19 22:27:35 +03:30
}
} ;
2021-11-01 01:36:35 +03:30
template < >
struct AK : : Formatter < Wasm : : ValueType > : public AK : : Formatter < StringView > {
2021-11-16 01:15:21 +01:00
ErrorOr < void > format ( FormatBuilder & builder , Wasm : : ValueType const & value )
2021-11-01 01:36:35 +03:30
{
2021-11-16 01:15:21 +01:00
return Formatter < StringView > : : format ( builder , Wasm : : ValueType : : kind_name ( value . kind ( ) ) ) ;
2021-11-01 01:36:35 +03:30
}
} ;
template < >
struct AK : : Formatter < Wasm : : ValidationError > : public AK : : Formatter < StringView > {
2021-11-16 01:15:21 +01:00
ErrorOr < void > format ( FormatBuilder & builder , Wasm : : ValidationError const & error )
2021-11-01 01:36:35 +03:30
{
2021-11-16 01:15:21 +01:00
return Formatter < StringView > : : format ( builder , error . error_string ) ;
2021-11-01 01:36:35 +03:30
}
} ;