-
Notifications
You must be signed in to change notification settings - Fork 292
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
How to limit memory usage? #720
Comments
This is true only for
So far this was never requested. Also I am not aware of any other Wasm runtime that provides this feature. I might be not up to date though. With the above information about imported |
Thanks @Robbepop. If the memory instance can be host controlled that would be great and would address my issue. If I understood it correctly, the host would create a I've been having a bit of hard time figuring out how to make it work, if you have any code examples that shows how memory importing works that would helpful greatly. |
I think I figured this part out, I first create a new Also if I'm not mistaken, it appears the external and internal memory will exist together, so it's still possible for the VM to grow its internal memory without the host control / limit? |
Happy you figured it out!
Yes, the Wasm side can still grow the imported |
Apologies if my question wasn't clear. When we link in an external memory, I understood the guest can only grow the external memory within the host-defined limit. However, the internal memory will still exist when the module is created from parsing the WASM bytecode, and it can be still grown by WASM without the host's control/lilmit? If that's the case, we don't have a mechanism to limit the total memory consumption (internal + external) from the VM by the host? Just to give a bit of background on what we were trying to do and why memory limiting/metering is important. |
I've been playing around with this too. I can see how to define a |
I partially read the metering description and was wondering as to what exactly are you metering. Are you metering the number of accesses, the number of read or written bytes, the number of accesses Wasm pages, the maximum Wasm pages accessed, etc. for the metering? In our
I warmly recommend looking into the Wasmtime API docs since they are pretty decent and extremely similar to what you'd do in To answer your question: You want to create an If you import a |
Hopefully, I’m not hijacking the original question, but how about if there is no control over the supply of the wasm file, and therefore the ability to import env::mem? |
You need some kind of control. |
Thanks for that. Here's the control I ended up with. I hope that it is useful for someone else: #[derive(Debug)]
enum Error {
MaximumMemoryExceeded(u32),
MaximumMemoryUnspecified,
}
impl Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Error::MaximumMemoryExceeded(pages) => write!(
f,
"WASM file has a maximum memory of {} pages, but must not exceed {} pages",
pages, MAX_MEMORY_PAGES
),
Error::MaximumMemoryUnspecified => write!(
f,
"WASM file must be compiled with a maximum memory of {} pages or less",
MAX_MEMORY_PAGES
),
}
}
}
impl std::error::Error for Error {}
fn validate_module(module: &Module) -> Result<(), Error> {
let Some(max_pages) = module.exports().find_map(|e| if e.name() == "memory" {
e.ty().memory().and_then(|m| m.maximum_pages())
} else {
None
}) else {
return Err(Error::MaximumMemoryUnspecified)
};
if max_pages > MAX_MEMORY_PAGES.into() {
return Err(Error::MaximumMemoryExceeded(max_pages.into()));
}
Ok(())
} Would there be interest in a PR that provides |
@huntc Thank you for kindly sharing your solution with others! :)
I seriously was not aware of this API so far and thus have not dug into it. We probably want this API in |
@jayz22 If your questions are resolved I'd be happy if you close the issue or respond with a follow up. :) |
@Robbepop Thank you for the detailed explanation. I have a couple of followup questions:
I'm not aware of this API but this looks very promising. If I understood it correctly, this ResourceLimiter will be added to the |
Hey @jayz22, I am not aware of any Rust compiler or Cargo flags that can be applied to alter the generated Wasm blob. There might be options and settings, although I would not consider using them if they were not marked as stable or production ready. At Parity we are compiling our ink! smart contracts (written in Rust, compiled to Wasm) via our own custom tool named This tool alters the compilation process for us, applies even more optimizations, e.g. via Binaryen's Therefore we have everything available to us via our own tooling which is super flexible. Users simply always use |
Thanks @Robbepop, I think this has answered my questions regarding memory limiting/metering. Here are some of my main takeaways which maybe useful for someone else:
(feel free correct me or add to it if necessary) I'm definitely very interesting in having the |
@Robbepop I just wanted to follow up a bit on this: I went looking for the code that does this so we could at least try to do some similar analysis / filtering and it looks to me like you're talking about this code -- is that what you meant? If so, it's a little awkward for us to do something similar using Would you object to a few additional APIs added to |
@graydon Hi
Actually I was referring to code in our ink! build tool: https://github.com/paritytech/cargo-contract/blob/master/crates/build/src/lib.rs#L580 As you can see it is also using In my opinion the ecosystem needs or will need such a tool again that is capable to stay up-to-date with modern Wasm and I do not even think that it would be too hard to build such a tool based on
The separate parsing is not a problem in our own case since it is done at different stages of the smart contract life cycle.
Therefore
Generally this kind of post-parse analysis is already possible for imported Wasm objects via I am very open to add Wasmtime APIs that do not yet exist in If smart contracts on your platform are using internal memory instead of imported memory: what is the reason? Is it historical or purposeful? If it is just historical I guess a decent idea is to performance a phase transition where new smart contracts should always be built using imported memory and old contracts silently get converted to those via a tool such as |
Well, ok, but that has to be paired with a component that's in the contract runtime, right? Like someone can upload a contract that wasn't built with that build tool, and you have to detect and reject whatever badness the user chose to upload. That's the component I'm concerned with (am currently writing). We can theoretically modify the build side to do something different too -- we do have a build wrapper command though we would prefer our contract builds to stay as close to
At the moment I'm just hoping to avoid paying the CPU cost of parsing the code twice using two different libraries just to perform very basic inspection on modules that I can already do in wasmi if wasmi just reveals a little more information about the module it's storing in memory. Wasmi has already parsed it during module construction, the data is right there, it seems a good time to look at the module and reject it if it's bad.
Ok. I .. generally undersand these phases, it's just not really clear to me that I need a lot of information from
I was hoping this isn't such a maintenance burden that it'd cause headaches (the
Hmm, wait, I thought that only lets me get exports? Here you say it lets me get imports. What I'm concerned about is if the user maliciously includes a definition of a very large, non-exported memory (just to DoS the server) we can't find it that way.
I mean, I don't know what to say here -- obviously I can't tell you which goals to prioritize or how to spend maintenance time on the project, so if the answer is "no" I understand. I would define the methods myself as an extension trait and happily maintain them, but Rust is picky about that and won't let me -- they'd need to access fields of
Sure, and longer-term I think that's probably the right way for all this to go, and we'll be happy to spend some time helping with that too (if doing so doesn't step on your feet). I'm concerned with shipping a mitigation today that I can accomplish with a single line of code added.
Oh, probably nothing. I mean, assuming it gets called during initial memory instantiation -- it's not clear to me if the
Of course. I've been in your shoes many times and am 100% not trying to demand your labor on something you're uncomfortable with.
Three reasons:
|
From what I understood the |
#643 introduces native fuel metering that allows fuel (proxy to cpu instructions) consumption to be metered and limited, and the limit is configurable.
Is there plan to enhance the metering system to cover memory usage? It would be useful to have native support for configurable memory usage limiting, similar to the fuel metering.
The
Memory
module contains amaximum_pages
and is enforced during the memory grow operation. However, AFAIK the maximum page is specified by the WASM bytecode and is not configurable by Wasmi. Is there a way for Wasmi to natively limit the memory usage and allow that limit to be configurable?The text was updated successfully, but these errors were encountered: