Skip to content
This repository has been archived by the owner on Aug 2, 2022. It is now read-only.

Make wasm constraints configurable #8360

Merged
merged 39 commits into from
Apr 27, 2020
Merged

Make wasm constraints configurable #8360

merged 39 commits into from
Apr 27, 2020

Conversation

swatanabe-b1
Copy link
Contributor

Change Description

Consensus Changes

  • Consensus Changes

API Changes

  • API Changes

Documentation Additions

  • Documentation Additions

…mit. This also slightly reduces the amount of data that needs to be copied on module initialization.
- OC: Use indirect access when the mutable globals need more memory than
  is allocated statically.
…t during module instantiation, since they are no longer correct. We've already checked them during set code anyway.
@swatanabe-b1
Copy link
Contributor Author

Depends on #8223 and EOSIO/eos-vm#144

@swatanabe-b1 swatanabe-b1 marked this pull request as ready for review December 30, 2019 20:38
max_pages = config.max_pages;
}
mem.reset(max_pages);
EOS_ASSERT(code.starting_memory_pages <= (int)max_pages, wasm_execution_error, "Initial memory out of range");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this line trying to detect when config.max_pages has been shrunk since the wasm was instantiated?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does that check need to happen here instead of a central location?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There isn't a convenient central location to put it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like the check could be wasm_interface::apply() (by way of placing starting memory pages in the wasm_cache_entry). But this would incur an additional lookup every execution which would be unfortunate.

Copy link
Contributor

@spoonincode spoonincode left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a big problem with OC & a configurable call depth.

OC uses the native stack (unrelated to wasm stack). At the moment it ensures that no function uses more than 16KB of native stack. The idea is that 16KB*250 is ~4MB which is considerably less than default of 8MB stack across our supported platforms.

Increasing the call depth limit causes obvious problems here. Some improvements could be made, such as calculating more accurate stack usage based on the call graph. Could also try and use a separate stack for execution though that’s tricky for a few reasons and would still ultimately at the end of the day be a fixed size.

Unfortunately, since the depth limit can change transaction to transaction, it seems like OC would need to reevaluate limits each time the limit is changed.

max_call_depth = config.max_call_depth;
max_pages = config.max_pages;
}
mem.reset(max_pages);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Far too dangerous IMO. Could fail to acquire the resources. Also I think if the limit is large (like 2GB or something) it won't even work as-is.
Probably beyond a certain threshold (could be 33MB, could be more, could be less) OC should switch to using mprotect().

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed it. The threshold for switching to mprotect can be configured at construction.

auto tableElementType = llvm::StructType::get(context,{llvmI8PtrType, llvmI64Type});
defaultTablePointer = emitLiteralPointer((void*)current_prologue,tableElementType->getPointerTo(256));
llvm::Type* tableArrayTy = llvm::ArrayType::get(tableElementType, module.tables.defs[0].type.size.min);
defaultTablePointer = new llvm::GlobalVariable(*llvmModule, tableArrayTy, false, llvm::GlobalValue::ExternalLinkage, llvm::ConstantAggregateZero::get(tableArrayTy), getTableSymbolName());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does this need to be marked ExternallyInitialized?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think so. The external initialization should run after the the module is fully loaded.

@@ -133,7 +133,7 @@ namespace LLVMJIT
if(SectionName == ".stack_sizes") {
return stack_sizes.emplace_back(numBytes).data();
}
WAVM_ASSERT_THROW(isReadOnly);
//WAVM_ASSERT_THROW(isReadOnly);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's going on here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The wasmTable is not in a read-only section.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah okay. This is a safety check that llvm didn't generate a data section that it expects to write to. The table should be placed in a read only section, and the assert should go back in. AFAIK just setting the GlobalVariable const param to true and using private linkage works.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I tried that, but eventually gave up. In order to make it const, the initialization can't be handled externally.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a35f580 has an implementation that works.

@spoonincode
Copy link
Contributor

Can you describe what the behavior is when shrinking a limit after a wasm has been accepted? For example if the initial data max is set to 128KB, my wasm has 96KB worth of data, and then the limit is changed to 64KB.

@swatanabe-b1
Copy link
Contributor Author

The intent is that max_pages and max_call_depth are enforced on apply. The rest are enforced at set_code.

@spoonincode
Copy link
Contributor

It seems like wasm memory initialization & memory_api need to be checktime'd now.

@spoonincode
Copy link
Contributor

actually, I wonder if some other intrinsics are abusable at large memory sizes, 256MB, or such.

@spoonincode
Copy link
Contributor

We need to bump OC's codegen version. I think it's probably possible to keep original code running in this new version by doing things like moving max_linear_memory_pages to the end of the control block but those changes could be done at a later time. At the moment we can just drop all version 0 on startup and start marking new ones version 1.

@spoonincode
Copy link
Contributor

For example, something like this will run way over a 30ms deadline on EOS VM JIT, and it's only double the current memory size.

(module
 (import "env" "eosio_assert" (func $eosio_assert (param i32) (param i32)))
 (import "env" "memset" (func $memset (param i32) (param i32) (param i32) (result i32)))
 (memory 1024)
 (export "apply" (func $apply))
 (func $apply (param $0 i64) (param $1 i64) (param $2 i64)
   (drop (call $memset (i32.const 0) (i32.const 0xff) (i32.const 67108863)))
   (i32.store offset=67108860 (i32.const 0) (i32.const 0xffffffff))
   (call $eosio_assert (i32.const 1) (i32.const 0))
 )
)

Can't help but wonder if allowing a knob to control memory limits opens pandora's box on new deadline overrun attacks. Is anyone even asking for more memory?

@swatanabe-b1
Copy link
Contributor Author

I'm somewhat less concerned about deadline overruns, since this is primarily intended for private and permissioned chains. These parameters will definitely cause problems if they are set too high on a public chain.

@@ -0,0 +1,13 @@
.file "switch_stack_linux.s"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this really linux only?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not strictly linux only, but it won't work on OSX. The code is fine on OSX, but the file format is not.


template<typename F>
void run(F&& f) {
if (stack_top) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why conditionally gate this? Why not have the altstack used all the time?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have a strong opinion either way. It's mostly just to avoid an extra mmap when it isn't actually necessary.

@larryk85 larryk85 changed the base branch from develop to kv-database April 24, 2020 17:00
Copy link
Contributor

@arhag arhag left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The implementation of the new intrinsics seem to have been lost in the translation to the new host function system.

I haven't reviewed any of the changes relating to EOS VM OC (I will leave that to others). Also, I haven't yet reviewed the changes to the unit tests.

libraries/chain/CMakeLists.txt Outdated Show resolved Hide resolved
libraries/chain/protocol_feature_manager.cpp Outdated Show resolved Hide resolved
libraries/chain/webassembly/runtimes/eos-vm.cpp Outdated Show resolved Hide resolved
libraries/chain/webassembly/runtimes/eos-vm.cpp Outdated Show resolved Hide resolved
libraries/chain/webassembly/runtimes/eos-vm.cpp Outdated Show resolved Hide resolved
unittests/wasm_tests.cpp Show resolved Hide resolved
@arhag arhag changed the base branch from kv-database to develop April 24, 2020 19:16
Copy link
Contributor

@arhag arhag left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update the fc submodule.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants