3

Delegate call bug in ink!

 1 year ago
source link: https://blog.coinfabrik.com/delegate-call-error-decoding-langerror/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Introduction

ink! is a programming language for smart contracts. It can be used in parachains built on Substrate. There was a bug in the CallBuilder::delegate() method and ink_env::invoke_contract_delegate() function which returns unexpected values.

When using the function build_call::<>().delegate(target) we observe that:

  • The storage cell key is calculated correctly.
  • The storage value is being decoded incorrectly.

This can be traced to the `LangError` implementation that changed in #1450 and was introduced in v4.0.0.

We assess the severity of the vulnerability as low.

CVE reference CVE-2023-34449

Affected versions

The issue affects versions v4.0.0, v4.0.1, v4.1.0. v4.2.0.

Minimal Error Example

// First contract, this will be performing a delegate call to the `Callee`.
#[ink(storage)]
pub struct Caller {
value: u128,
}

#[ink(message)]
pub fn get_value(&self, callee_code_hash: Hash) -> u128 {
let result = build_call::<DefaultEnvironment>()
.delegate(callee_code_hash)
.exec_input(ExecutionInput::new(Selector::new(ink::selector_bytes!(
"get_value"
))))

.returns::<u128>()
.invoke();
result
}

// Different contract, using this code hash for the delegate call.
#[ink(storage)]
pub struct Callee {
value: u128,
}

#[ink(message)]
pub fn get_value(&self) -> u128 {
self.value
}

The delegated contract in this example only returns the value.

Expected: 1

Received: 256

Every time a number is returned, it is equal to the expected value times 256 (1 byte).

This is a minimal representation of the error in the ink! documentation:

Cross-Contract Calling | ink! documentation

Testing on Different Types

We tested the use of delegate calls on different ink! types obtaining the following results.

We observe that the storage value returned is shifted by one byte (eight bit shifts left wise). The expected value can be obtained by dividing by 256.

String

An empty value is returned.

The key is not being resolved consistently (it is dependent on the contract names, addresses and variables). If the value is fixed through a manual key, the returned value maintains the same errors as observed in ‘uint’.

Survey of Repos Using Delegate

We found several repositories on GitHub that could be potentially affected by this malfunctionality.

The vendor, Parity, reported it did not find deployed contracts on Shiden, Astar or Aleph Zero affected by this vulnerability

We informed Parity about the bug, and it was solved in #1808. If you are using one of the affected versions (v4.0.0, v4.0.1, v4.1.0. v4.2.0), please update to v4.2.1.

References

Credits

This issue was found by Facundo Lerena with the assistance of Agustín Losiggio, Arturo Beccar-Varela and Agustín Aon, all members of CoinFabrik.

Disclosure timeline

This timeline is not exhaustive and only lists events that we deemed relevant to the disclosure process.

  • 2023-06-13 CoinFabrik sent the vulnerability report to Parity.
  • 2023-06-13 Parity acknowledges the report and sends a fix solving the issue.
  • 2023-06-14 CoinFabrik validates the fix.
  • 2023-06-14 Parity pushes the fixed version and releases an advisory.
  • 2023-06-14 CoinFabrik releases this advisory.

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK