mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-09-09 19:16:02 +00:00
LibWasm: Tighten validation algorithm
The big improvement included in this commit is stack height mismatch validation. There are other minor improvements included (related to the validation algorithm). The method of supporting stack polymorphism has changed to be more like the spec, which was necessary for confidently handling stack height mismatches. See: https://webassembly.github.io/spec/core/appendix/algorithm.html
This commit is contained in:
parent
8217a77728
commit
6e419db26c
Notes:
sideshowbarker
2024-07-18 04:38:32 +09:00
Author: https://github.com/dzfrias
Commit: 6e419db26c
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/147
Reviewed-by: https://github.com/alimpfard ✅
2 changed files with 155 additions and 264 deletions
|
@ -132,6 +132,27 @@ public:
|
|||
return Errors::invalid("TableIndex"sv);
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
};
|
||||
|
||||
// Instructions
|
||||
struct StackEntry {
|
||||
StackEntry(ValueType type)
|
||||
|
@ -181,66 +202,58 @@ public:
|
|||
friend struct AK::Formatter;
|
||||
|
||||
public:
|
||||
// The unknown entry will never be popped off, so we can safely use the original `is_empty`.
|
||||
Stack(Vector<Frame> const& frames)
|
||||
: m_frames(frames)
|
||||
{
|
||||
}
|
||||
|
||||
using Vector<StackEntry>::is_empty;
|
||||
using Vector<StackEntry>::last;
|
||||
using Vector<StackEntry>::at;
|
||||
using Vector<StackEntry>::size;
|
||||
using Vector<StackEntry>::resize;
|
||||
|
||||
StackEntry take_last()
|
||||
ErrorOr<StackEntry, ValidationError> take_last()
|
||||
{
|
||||
if (last().is_known)
|
||||
return Vector<StackEntry>::take_last();
|
||||
return last();
|
||||
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);
|
||||
return Vector<StackEntry>::take_last();
|
||||
}
|
||||
void append(StackEntry entry)
|
||||
{
|
||||
if (!entry.is_known)
|
||||
m_did_insert_unknown_entry = true;
|
||||
Vector<StackEntry>::append(entry);
|
||||
}
|
||||
|
||||
ErrorOr<void, ValidationError> take(ValueType type, SourceLocation location = SourceLocation::current())
|
||||
ErrorOr<StackEntry, ValidationError> take(ValueType type, SourceLocation location = SourceLocation::current())
|
||||
{
|
||||
if (is_empty())
|
||||
return Errors::invalid("stack state"sv, type, "<nothing>"sv, location);
|
||||
|
||||
auto type_on_stack = take_last();
|
||||
auto type_on_stack = TRY(take_last());
|
||||
if (type_on_stack != type)
|
||||
return Errors::invalid("stack state"sv, type, type_on_stack, location);
|
||||
|
||||
return {};
|
||||
return type_on_stack;
|
||||
}
|
||||
|
||||
template<auto... kinds>
|
||||
ErrorOr<void, ValidationError> take(SourceLocation location = SourceLocation::current())
|
||||
{
|
||||
ErrorOr<void, ValidationError> result;
|
||||
if (((result = take(Wasm::ValueType(kinds), location)).is_error(), ...)) {
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
for (auto kind : { kinds... })
|
||||
TRY(take(Wasm::ValueType(kind), location));
|
||||
return {};
|
||||
}
|
||||
|
||||
template<auto... kinds>
|
||||
ErrorOr<void, ValidationError> take_and_put(Wasm::ValueType::Kind kind, SourceLocation location = SourceLocation::current())
|
||||
{
|
||||
ErrorOr<void, ValidationError> result;
|
||||
if (((result = take(Wasm::ValueType(kinds), location)).is_error(), ...)) {
|
||||
return result;
|
||||
}
|
||||
TRY(take<kinds...>(location));
|
||||
append(Wasm::ValueType(kind));
|
||||
return result;
|
||||
return {};
|
||||
}
|
||||
|
||||
size_t actual_size() const { return Vector<StackEntry>::size(); }
|
||||
size_t size() const { return m_did_insert_unknown_entry ? static_cast<size_t>(-1) : actual_size(); }
|
||||
|
||||
Vector<StackEntry> release_vector() { return exchange(static_cast<Vector<StackEntry>&>(*this), Vector<StackEntry> {}); }
|
||||
|
||||
bool operator==(Stack const& other) const;
|
||||
|
||||
private:
|
||||
bool m_did_insert_unknown_entry { false };
|
||||
Vector<Frame> const& m_frames;
|
||||
};
|
||||
|
||||
struct ExpressionTypeResult {
|
||||
|
@ -286,6 +299,7 @@ private:
|
|||
|
||||
static ValidationError duplicate_export_name(StringView name) { return ByteString::formatted("Duplicate exported name '{}'", name); }
|
||||
static ValidationError multiple_start_sections() { return ByteString("Found multiple start sections"sv); }
|
||||
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); }
|
||||
|
||||
template<typename T, typename U, typename V>
|
||||
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); }
|
||||
|
@ -308,7 +322,7 @@ private:
|
|||
|
||||
builder.append("], but found [ "sv);
|
||||
|
||||
auto actual_size = stack.actual_size();
|
||||
auto actual_size = stack.size();
|
||||
for (size_t i = 1; i <= min(count, actual_size); ++i) {
|
||||
auto& entry = stack.at(actual_size - i);
|
||||
if (entry.is_known) {
|
||||
|
@ -326,34 +340,6 @@ private:
|
|||
static ByteString find_instruction_name(SourceLocation const&);
|
||||
};
|
||||
|
||||
struct BlockDetails {
|
||||
size_t initial_stack_size { 0 };
|
||||
struct IfDetails {
|
||||
Stack initial_stack;
|
||||
};
|
||||
Variant<IfDetails, Empty> details;
|
||||
};
|
||||
|
||||
enum class FrameKind {
|
||||
Block,
|
||||
Loop,
|
||||
If,
|
||||
Else,
|
||||
Function,
|
||||
};
|
||||
|
||||
struct Frame {
|
||||
FunctionType type;
|
||||
FrameKind kind;
|
||||
size_t init_size;
|
||||
bool unreachable { false };
|
||||
|
||||
Vector<ValueType> const& labels() const
|
||||
{
|
||||
return kind != FrameKind::Loop ? type.results() : type.parameters();
|
||||
}
|
||||
};
|
||||
|
||||
Context m_context;
|
||||
Vector<Frame> m_frames;
|
||||
COWVector<GlobalType> m_globals_without_internal_globals;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue