-
Notifications
You must be signed in to change notification settings - Fork 5.8k
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
External calls should not generate RETURNDATACOPY if the return data is not used #12306
Comments
Right, this does sound like a sensible optimization. I suppose it was done this way because we generally try to make the code generation straightforward and rely on the optimizer to remove unused stuff instead. In this case it looks like this is not being optimized out even though memory goes unused. This is something that the upcoming solidity/libsolidity/codegen/ExpressionCompiler.cpp Lines 2725 to 2761 in defc74c
At least as long as we're talking about the case where the return data goes completely unused (which would cover the low-level calls). Handling the situation where it's only partially used (i.e. you ignore some but not all return values) in a high-level call would be more complex and might require changes in the ABI decoder code. |
Related to @hrkrshnn 's focus task of tracking memory references. I'm not sure we should really even further complicate the call code. |
Understandable that complicating the code generation here is a major concern. I would argue that this is a subtle security issue, and is at least worth adding to the docs (happy to do that if that's the best way forward). Being able to force a contract to copy unlimited data into memory after any external call is potentially a serious problem for many contracts. |
@chriseth Is there a link where we could track the work on tracking memory references? I was just looking to propose a related optimization. |
I don't think we have one overarching issue for that. There's a bunch of issues about tracking memory stores and removing redundant ones (#10690, #10755, #6727, #12211), there are also a few ongoing PRs (#12272, #11352, #11192). You can post in one that looks relevant but if it's a completely new thing I think it would be fine to just open a new issue specifically for that optimization. |
This issue has been marked as stale due to inactivity for the last 90 days. |
This should be fixed. |
The really nice, though quite complex, way to fix this would be to treat I'll label the issue, s.t. it remains open in any case. |
Description
Solidity automatically copies all return data into memory after any external call, including low-level calls for which the return data is never captured or used. To my knowledge, the only way to prevent this is to make the call from an inline assembly block.
This is somewhat counterintuitive and makes it difficult to reason about the resource consumption of an untrusted external call. Here is an example of a subtle vulnerability resulting from this behavior (acknowledging that this violates the suggested withdrawal method for sending funds to untrusted addresses).
It is common to assume that the external call made in
KingOfTheHill.bid()
will always have some gas remaining after the call to complete execution. In theory, theunseatedKing
address receiving this call will be able to consume a maximum of63/64 * G
gas, where G is the gas remaining at the callsite. This may lead to the assumption that there is always some amount of gas that a bidder can provide to thebid()
call such that the remaining1/64 * G
gas will be enough to finish execution without exhausting the gas.But, the call recipient can force the caller to consume gas after the call by returning a large amount of data.
As seen above, if the
unseatedKing
is a contract that returns as much 0 data as possible without throwing anOUT_OF_GAS
exception, there is no amount of gas that a bidder can provide tobid()
such that execution does not run out of gas after the call to theunseatedKing
. All of the data that theunseatedKing
contract returns will be copied into memory after the call, andRETURNDATACOPY
is more expensive per byte of data thanRETURN
. TheunseatedKing
gets 63/64 of remaining gasG
to return as much data as possible, and thebid()
function can never copy that much data into memory without exhausting the remaining1/64 * G
gas.Expected behavior
Do not
RETURNDATACOPY
after a low-level external call unless the return data is captured or used, e.g.:The text was updated successfully, but these errors were encountered: