Skip to content
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

Error-code spoofing #1175

Closed
graydon opened this issue Nov 3, 2023 · 2 comments
Closed

Error-code spoofing #1175

graydon opened this issue Nov 3, 2023 · 2 comments
Assignees
Labels
bug Something isn't working

Comments

@graydon
Copy link
Contributor

graydon commented Nov 3, 2023

When a contract returns an error the ABI doesn't differentiate the guest-level values Ok(Error) from Err(Error) from Error. This is fine and expected, but with_frame in the host attempts to "fix" this by funneling Ok(Error) into Err(HostError) back in host-land, and this makes it possible for user code to "spoof" error codes -- return things other than ScErrorType::ContractError.

For most error codes this is at best a minor source of confusion, but we have some paths that care about the difference, eg. try_call decides whether an error is recoverable or unrecoverable based on the error type, and also we just generally want ScErrorCode::InternalError to only represent "there's a problem in the host's own logic" unambiguously.

Naturally the fuzzer discovered this game immediately: it can generate InternalError by writing a contract consisting of the constant value InternalError.

The fix for this is probably to get with_frame out of the business of massaging Ok(Error) returns and have call, try_call and whatever else emulates calling and error-propagation in the native-contract case look at the Ok(Error) cases they specifically want to admit -- Ok(ScErrorType::ContractError) alone -- and map that to Err(...), and the rest into different InvalidAction codes or something.

// FIXME: fuzzer discovered that it can just return "InternalError" from
// a contract to make the host think it had an InternalError. See
// https://github.com/stellar/rs-soroban-env/issues/1175
if error.is_code(ScErrorCode::InternalError) {
Err(arbitrary::Error::IncorrectFormat)
} else {
Ok(error)
}

@graydon graydon added the bug Something isn't working label Nov 3, 2023
@graydon graydon self-assigned this Nov 3, 2023
@leighmcculloch
Copy link
Member

This issue has a knock on affect on the SDK that required this change, but it is unclear if that change is the right change:

And requires at least this follow up:

graydon added a commit that referenced this issue Nov 14, 2023
graydon added a commit that referenced this issue Nov 14, 2023
github-merge-queue bot pushed a commit that referenced this issue Nov 15, 2023
I _think_ this is actually all that we need (or should) do for #1175.
It's a little less than I was expecting to change: in particular I kept
the adjustment in `with_frame`, and just tightened its condition (and
commented why).

There are more call paths that go through `with_frame` than I expected
and adjusting all of them is a bit more repetitive and introduces a bit
more risk of drift between them than I think warrants the risk of
accidentally over-adjusting an error unintentionally here (eg. if
someone uses with_frame to pass an error we _don't_ want adjusted this
way for some reason).

Nothing is ideal in this story, but I think this is a conservative fix
that at least avoids the problem we were concerned with. @dmkozh wdyt? I
will tack on a test that exercises this if you concur.
@graydon
Copy link
Contributor Author

graydon commented Nov 16, 2023

Done in #1209

@graydon graydon closed this as completed Nov 16, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants