提交 4e8feec4 编写于 作者: M Matt Witherspoon

Protect against crash on WASMs w/o linear memory

Linear memory is optional in WASM. Protect against crashes when a WASM doesn't use it.
上级 9d49bbfa
......@@ -220,7 +220,7 @@ namespace eosio { namespace chain {
// time to compile a brand new (maybe first) copy of this code
Module* module = new Module();
ModuleInstance* instance = nullptr;
size_t mem_end;
size_t mem_end = 0;
vector<char> mem_image;
try {
......@@ -233,18 +233,19 @@ namespace eosio { namespace chain {
instance = instantiateModule(*module, std::move(link_result.resolvedImports));
FC_ASSERT(instance != nullptr);
auto current_memory = Runtime::getDefaultMemory(instance);
MemoryInstance* current_memory = Runtime::getDefaultMemory(instance);
char *mem_ptr = &memoryRef<char>(current_memory, 0);
const auto allocated_memory = Runtime::getDefaultMemorySize(instance);
for (uint64_t i = 0; i < allocated_memory; ++i) {
if (mem_ptr[i]) {
mem_end = i + 1;
if(current_memory) {
char *mem_ptr = &memoryRef<char>(current_memory, 0);
const auto allocated_memory = Runtime::getDefaultMemorySize(instance);
for (uint64_t i = 0; i < allocated_memory; ++i) {
if (mem_ptr[i])
mem_end = i + 1;
}
mem_image.resize(mem_end);
memcpy(mem_image.data(), mem_ptr, mem_end);
}
mem_image.resize(mem_end);
memcpy(mem_image.data(), mem_ptr, mem_end);
} catch (...) {
pending_error = std::current_exception();
}
......@@ -302,9 +303,11 @@ namespace eosio { namespace chain {
_ios.post([&,code_id,this](){
// sanitize by reseting the memory that may now be dirty
auto& info = (*fetch_info(code_id)).get();
char* memstart = &memoryRef<char>( getDefaultMemory(entry.instance), 0 );
memset( memstart + info.mem_end, 0, ((1<<16) - info.mem_end) );
memcpy( memstart, info.mem_image.data(), info.mem_end);
if(getDefaultMemory(entry.instance)) {
char* memstart = &memoryRef<char>( getDefaultMemory(entry.instance), 0 );
memset( memstart + info.mem_end, 0, ((1<<16) - info.mem_end) );
memcpy( memstart, info.mem_image.data(), info.mem_end);
}
// under a lock, put this entry back in the available instances side of the instances vector
with_lock([&,this](){
......@@ -352,8 +355,9 @@ namespace eosio { namespace chain {
void wasm_cache::checkin(const digest_type& code_id, entry& code ) {
auto default_mem = Runtime::getDefaultMemory(code.instance);
Runtime::shrinkMemory(default_mem, Runtime::getMemoryNumPages(default_mem) - 1);
MemoryInstance* default_mem = Runtime::getDefaultMemory(code.instance);
if(default_mem)
Runtime::shrinkMemory(default_mem, Runtime::getMemoryNumPages(default_mem) - 1);
_my->return_entry(code_id, code);
}
......@@ -832,10 +836,13 @@ class memory_api : public context_aware_api {
constexpr uint32_t NBPPL2 = IR::numBytesPerPageLog2;
constexpr uint32_t MAX_MEM = 1024 * 1024;
const auto default_mem = Runtime::getDefaultMemory(code.instance);
const uint32_t num_pages = Runtime::getMemoryNumPages(default_mem);
const uint32_t min_bytes = (num_pages << NBPPL2) > UINT32_MAX ? UINT32_MAX : num_pages << NBPPL2;
const uint32_t prev_num_bytes = sbrk_bytes; //_num_bytes;
MemoryInstance* default_mem = Runtime::getDefaultMemory(code.instance);
if(!default_mem)
throw eosio::chain::page_memory_error();
const uint32_t num_pages = Runtime::getMemoryNumPages(default_mem);
const uint32_t min_bytes = (num_pages << NBPPL2) > UINT32_MAX ? UINT32_MAX : num_pages << NBPPL2;
const uint32_t prev_num_bytes = sbrk_bytes; //_num_bytes;
// round the absolute value of num_bytes to an alignment boundary
num_bytes = (num_bytes + 7) & ~7;
......
......@@ -129,10 +129,11 @@ namespace Runtime
U8* getValidatedMemoryOffsetRange(MemoryInstance* memory,Uptr offset,Uptr numBytes)
{
if(!memory)
causeException(Exception::Cause::accessViolation);
// Validate that the range [offset..offset+numBytes) is contained by the memory's reserved pages.
U8* address = memory->baseAddress + offset;
if( !memory
|| address < memory->reservedBaseAddress
if( address < memory->reservedBaseAddress
|| address + numBytes < address
|| address + numBytes >= memory->reservedBaseAddress + (memory->reservedNumPlatformPages << Platform::getPageSizeLog2()))
{
......
......@@ -33,4 +33,24 @@ static const char entry_wast[] = R"=====(
)
(start $entry)
)
)=====";
static const char simple_no_memory_wast[] = R"=====(
(module
(import "env" "memcpy" (func $memcpy (param i32 i32 i32) (result i32)))
(table 0 anyfunc)
(export "init" (func $init))
(export "apply" (func $apply))
(func $init
)
(func $apply (param $0 i64) (param $1 i64)
(drop
(call $memcpy
(i32.const 0)
(i32.const 1024)
(i32.const 1024)
)
)
)
)
)=====";
\ No newline at end of file
......@@ -13,6 +13,7 @@
#include <proxy/proxy.wast.hpp>
#include <proxy/proxy.abi.hpp>
#include <Runtime/Runtime.h>
#include <fc/variant_object.hpp>
......@@ -549,7 +550,32 @@ BOOST_FIXTURE_TEST_CASE( check_entry_behavior, tester ) try {
BOOST_REQUIRE_EQUAL(true, chain_has_transaction(trx.id()));
const auto& receipt = get_transaction_receipt(trx.id());
BOOST_CHECK_EQUAL(transaction_receipt::executed, receipt.status);
} FC_LOG_AND_RETHROW() /// prove_mem_reset
} FC_LOG_AND_RETHROW()
/**
* Ensure we can load a wasm w/o memory
*/
BOOST_FIXTURE_TEST_CASE( simple_no_memory_check, tester ) try {
produce_blocks(2);
create_accounts( {N(nomem)}, asset::from_string("1000.0000 EOS") );
transfer( N(inita), N(nomem), "10.0000 EOS", "memo" );
produce_block();
set_code(N(nomem), simple_no_memory_wast);
produce_blocks(1);
//the apply func of simple_no_memory_wast tries to call a native func with linear memory pointer
signed_transaction trx;
action act;
act.scope = N(nomem);
act.name = N();
act.authorization = vector<permission_level>{{N(nomem),config::active_name}};
trx.actions.push_back(act);
trx.sign(get_private_key( N(nomem), "active" ), chain_id_type());
BOOST_CHECK_THROW(control->push_transaction( trx ), wasm_execution_error);
} FC_LOG_AND_RETHROW()
BOOST_AUTO_TEST_SUITE_END()
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册