Skip to content

Commit

Permalink
Add metering to execute
Browse files Browse the repository at this point in the history
  • Loading branch information
gumb0 committed Oct 27, 2020
1 parent 0dbf5ed commit 340309f
Show file tree
Hide file tree
Showing 23 changed files with 379 additions and 113 deletions.
6 changes: 3 additions & 3 deletions include/fizzy/fizzy.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ typedef struct FizzyExecutionResult
/// @param args Pointer to the argument array. Can be NULL iff function has no inputs.
/// @param depth Call stack depth.
typedef FizzyExecutionResult (*FizzyExternalFn)(
void* context, FizzyInstance* instance, const union FizzyValue* args, int depth);
void* context, FizzyInstance* instance, const union FizzyValue* args, int64_t gas, int depth);

/// Value type.
typedef uint8_t FizzyValueType;
Expand Down Expand Up @@ -189,8 +189,8 @@ size_t fizzy_get_instance_memory_size(FizzyInstance* instance);
/// No validation is done on the number of arguments passed in @p args, nor on their types.
/// When number of passed arguments or their types are different from the ones defined by the
/// function type, behaviour is undefined.
FizzyExecutionResult fizzy_execute(
FizzyInstance* instance, uint32_t func_idx, const union FizzyValue* args, int depth);
FizzyExecutionResult fizzy_execute(FizzyInstance* instance, uint32_t func_idx,
const union FizzyValue* args, int64_t gas, int depth);

#ifdef __cplusplus
}
Expand Down
8 changes: 4 additions & 4 deletions lib/fizzy/capi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -103,9 +103,9 @@ inline fizzy::ExecutionResult unwrap(const FizzyExecutionResult& result) noexcep

inline auto unwrap(FizzyExternalFn func, void* context) noexcept
{
return [func, context](fizzy::Instance& instance, const fizzy::Value* args,
return [func, context](fizzy::Instance& instance, const fizzy::Value* args, int64_t gas,
int depth) noexcept -> fizzy::ExecutionResult {
const auto result = func(context, wrap(&instance), wrap(args), depth);
const auto result = func(context, wrap(&instance), wrap(args), gas, depth);
return unwrap(result);
};
}
Expand Down Expand Up @@ -261,9 +261,9 @@ size_t fizzy_get_instance_memory_size(FizzyInstance* instance)
}

FizzyExecutionResult fizzy_execute(
FizzyInstance* instance, uint32_t func_idx, const FizzyValue* args, int depth)
FizzyInstance* instance, uint32_t func_idx, const FizzyValue* args, int64_t gas, int depth)
{
const auto result = fizzy::execute(*unwrap(instance), func_idx, unwrap(args), depth);
const auto result = fizzy::execute(*unwrap(instance), func_idx, unwrap(args), gas, depth);
return wrap(result);
}
}
30 changes: 19 additions & 11 deletions lib/fizzy/execute.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "execute.hpp"
#include "asserts.hpp"
#include "cxx20/bit.hpp"
#include "instructions.hpp"
#include "stack.hpp"
#include "trunc_boundaries.hpp"
#include "types.hpp"
Expand Down Expand Up @@ -478,14 +479,14 @@ void branch(const Code& code, OperandStack& stack, const uint8_t*& pc, const uin
}

template <class F>
inline bool invoke_function(
const FuncType& func_type, const F& func, Instance& instance, OperandStack& stack, int depth)
inline bool invoke_function(const FuncType& func_type, const F& func, Instance& instance,
OperandStack& stack, int64_t gas, int depth)
{
const auto num_args = func_type.inputs.size();
assert(stack.size() >= num_args);
const auto call_args = stack.rend() - num_args;

const auto ret = func(instance, call_args, depth + 1);
const auto ret = func(instance, call_args, gas, depth + 1);
// Bubble up traps
if (ret.trapped)
return false;
Expand All @@ -504,16 +505,17 @@ inline bool invoke_function(
}

inline bool invoke_function(const FuncType& func_type, uint32_t func_idx, Instance& instance,
OperandStack& stack, int depth)
OperandStack& stack, int64_t gas, int depth)
{
const auto func = [func_idx](Instance& _instance, const Value* args, int _depth) {
return execute(_instance, func_idx, args, _depth);
const auto func = [func_idx](Instance& _instance, const Value* args, int64_t _gas, int _depth) {
return execute(_instance, func_idx, args, _gas, _depth);
};
return invoke_function(func_type, func, instance, stack, depth);
return invoke_function(func_type, func, instance, stack, gas, depth);
}
} // namespace

ExecutionResult execute(Instance& instance, FuncIdx func_idx, const Value* args, int depth)
ExecutionResult execute(
Instance& instance, FuncIdx func_idx, const Value* args, int64_t gas, int depth)
{
assert(depth >= 0);
if (depth > CallStackLimit)
Expand All @@ -523,7 +525,7 @@ ExecutionResult execute(Instance& instance, FuncIdx func_idx, const Value* args,

assert(instance.module->imported_function_types.size() == instance.imported_functions.size());
if (func_idx < instance.imported_functions.size())
return instance.imported_functions[func_idx].function(instance, args, depth);
return instance.imported_functions[func_idx].function(instance, args, gas, depth);

const auto& code = instance.module->get_code(func_idx);
auto* const memory = instance.memory.get();
Expand All @@ -534,9 +536,15 @@ ExecutionResult execute(Instance& instance, FuncIdx func_idx, const Value* args,
const uint8_t* pc = code.instructions.data();
const uint8_t* immediates = code.immediates.data();

const auto cost_table = get_instruction_cost_table();

while (true)
{
const auto instruction = static_cast<Instr>(*pc++);

if ((gas -= cost_table[static_cast<uint8_t>(instruction)]) < 0)
goto trap;

switch (instruction)
{
case Instr::unreachable:
Expand Down Expand Up @@ -614,7 +622,7 @@ ExecutionResult execute(Instance& instance, FuncIdx func_idx, const Value* args,
const auto called_func_idx = read<uint32_t>(immediates);
const auto& called_func_type = instance.module->get_function_type(called_func_idx);

if (!invoke_function(called_func_type, called_func_idx, instance, stack, depth))
if (!invoke_function(called_func_type, called_func_idx, instance, stack, gas, depth))
goto trap;
break;
}
Expand All @@ -639,7 +647,7 @@ ExecutionResult execute(Instance& instance, FuncIdx func_idx, const Value* args,
if (expected_type != actual_type)
goto trap;

if (!invoke_function(actual_type, called_func->function, instance, stack, depth))
if (!invoke_function(actual_type, called_func->function, instance, stack, gas, depth))
goto trap;
break;
}
Expand Down
7 changes: 4 additions & 3 deletions lib/fizzy/execute.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,13 @@ constexpr ExecutionResult Void{true};
constexpr ExecutionResult Trap{false};

// Execute a function on an instance.
ExecutionResult execute(Instance& instance, FuncIdx func_idx, const Value* args, int depth = 0);
ExecutionResult execute(
Instance& instance, FuncIdx func_idx, const Value* args, int64_t gas, int depth = 0);

inline ExecutionResult execute(
Instance& instance, FuncIdx func_idx, std::initializer_list<Value> args)
Instance& instance, FuncIdx func_idx, std::initializer_list<Value> args, int64_t gas)
{
assert(args.size() == instance.module->get_function_type(func_idx).inputs.size());
return execute(instance, func_idx, args.begin());
return execute(instance, func_idx, args.begin(), gas);
}
} // namespace fizzy
13 changes: 8 additions & 5 deletions lib/fizzy/instantiate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,8 @@ std::unique_ptr<Instance> instantiate(std::unique_ptr<const Module> module,
for (const auto idx : instance->module->elementsec[i].init)
{
auto func = [idx, &instance_ref = *instance](fizzy::Instance&, const Value* args,
int depth) { return execute(instance_ref, idx, args, depth); };
int64_t gas,
int depth) { return execute(instance_ref, idx, args, gas, depth); };

*it_table++ =
ExternalFunction{std::move(func), instance->module->get_function_type(idx)};
Expand All @@ -343,7 +344,7 @@ std::unique_ptr<Instance> instantiate(std::unique_ptr<const Module> module,
{
const auto funcidx = *instance->module->startfunc;
assert(funcidx < instance->imported_functions.size() + instance->module->funcsec.size());
if (execute(*instance, funcidx, {}).trapped)
if (execute(*instance, funcidx, {}, 1000000).trapped)
{
// When element section modified imported table, and then start function trapped,
// modifications to the table are not rolled back.
Expand All @@ -364,7 +365,9 @@ std::unique_ptr<Instance> instantiate(std::unique_ptr<const Module> module,
auto& table_function = (*it_table)->function;
table_function = [shared_instance, func = std::move(table_function)](
fizzy::Instance& _instance, const Value* args,
int depth) { return func(_instance, args, depth); };
int64_t gas, int depth) {
return func(_instance, args, gas, depth);
};
++it_table;
}
}
Expand Down Expand Up @@ -435,8 +438,8 @@ std::optional<ExternalFunction> find_exported_function(Instance& instance, std::
return std::nullopt;

const auto idx = *opt_index;
auto func = [idx, &instance](fizzy::Instance&, const Value* args, int depth) {
return execute(instance, idx, args, depth);
auto func = [idx, &instance](fizzy::Instance&, const Value* args, int64_t gas, int depth) {
return execute(instance, idx, args, gas, depth);
};

return ExternalFunction{std::move(func), instance.module->get_function_type(idx)};
Expand Down
4 changes: 2 additions & 2 deletions lib/fizzy/instantiate.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ struct Instance;

struct ExternalFunction
{
std::function<ExecutionResult(Instance&, const Value*, int depth)> function;
std::function<ExecutionResult(Instance&, const Value*, int64_t gas, int depth)> function;
FuncType type;
};

Expand Down Expand Up @@ -101,7 +101,7 @@ struct ImportedFunction
std::string name;
std::vector<ValType> inputs;
std::optional<ValType> output;
std::function<ExecutionResult(Instance&, const Value*, int depth)> function;
std::function<ExecutionResult(Instance&, const Value*, int64_t gas, int depth)> function;
};

// Create vector of ExternalFunctions ready to be passed to instantiate.
Expand Down
Loading

0 comments on commit 340309f

Please sign in to comment.