CVE-2024-35229: Insufficient Information (NVD-CWE-noinfo)
value: ZKsync Era is a layer 2 rollup that uses zero-knowledge proofs to scale Ethereum. Prior to version 1.3.10, there is a very specific pattern `f(a(),b()); check_if_a_executed_last()` in Yul that exposes a bug in evaluation order of Yul function arguments. This vulnerability has been fixed in version 1.3.10. As a workaround, update and redeploy affected contracts.

CWE description: There is insufficient information about the issue to classify it; details are unkown or unspecified.
Published: 2024-05-27T17:15Z
Repository: https://github.com/matter-labs/era-compiler-solidity
Language: Other
Files changed: .cargo/config.toml, CHANGELOG.md, Cargo.lock, Cargo.toml, README.md, src/yul/parser/statement/expression/function_call/mod.rs
Commit date: 2023-06-01 13:03:02 +0200
Commit message: Release v1.3.10

Diff: diff --git a/.cargo/config.toml b/.cargo/config.toml
index 4537f2b..eb0f1b6 100644
--- a/.cargo/config.toml
+++ b/.cargo/config.toml
@@ -18,11 +18,11 @@ rustflags = [
 
 [profile.release]
 strip = true
 
 [env]
-LLVM_SYS_150_PREFIX = { value = "./target-llvm/target-final/", relative = true, force = true }
+LLVM_SYS_150_PREFIX = { value = "./target-llvm/target-final/", relative = true, force = false }
 
 [tools.clippy]
 warn = [
     "missing_docs_in_private_items",
 ]
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 693ed77..43298bb 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,7 +1,13 @@
 # The `zksolc` changelog
 
+## [1.3.10] - 2023-04-23
+
+### Fixed
+
+- The evaluation order of Yul function arguments
+
 ## [1.3.9] - 2023-04-18
 
 ### Added
 
 - A warning that Yul is not validated in system mode
diff --git a/Cargo.lock b/Cargo.lock
index 3deaa8f..1cca15e 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -146,11 +146,11 @@ dependencies = [
  "zkevm_opcode_defs",
 ]
 
 [[package]]
 name = "compiler-solidity"
-version = "1.3.9"
+version = "1.3.10"
 dependencies = [
  "anyhow",
  "colored",
  "compiler-common",
  "compiler-llvm-context",
diff --git a/Cargo.toml b/Cargo.toml
index e722130..5715c2c 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,8 +1,8 @@
 [package]
 name = "compiler-solidity"
-version = "1.3.9"
+version = "1.3.10"
 authors = [
     "Alex Zarudnyy <a.zarudnyy@matterlabs.dev>",
 ]
 license = "MIT OR Apache-2.0"
 edition = "2021"
diff --git a/README.md b/README.md
index 2e8a48f..aee7be0 100644
--- a/README.md
+++ b/README.md
@@ -11,14 +11,14 @@ This repository contains the compiler from Solidity to zkEVM bytecode.
 
 ## System Requirements
 
 Supported platforms:
 - **Linux: x86_64**  
-   MUSL-based static builds do not depend on system libraries and run on any recent Linux distribution.
+   MUSL-based static builds do not depend on system libraries and can be run on any recent Linux distribution.
 - **MacOS 11+: x86_64, arm64 (M1, M2)**
 - **Windows: x86_64**  
-   Only Windows 10 has been tested so far, but other versions should be OK as well.
+   Only Windows 10 has been tested so far, but other versions should be OK. 
 
 We recommend at least 4 GB of RAM available for the build process.
 
 ## Building
 
@@ -32,11 +32,11 @@ We recommend at least 4 GB of RAM available for the build process.
 
    Currently we are not pinned to any specific version of Rust, so just install the latest stable build for your platform.
    Also install the `musl` target if you are compiling on Linux in order to distribute the binary:
    `rustup target add x86_64-unknown-linux-musl`
 
-1. Download a version of the [solc](https://docs.soliditylang.org/en/develop/installing-solidity.html) compiler.  If it is not named exactly `solc` in your `$PATH`, see the `--solc` option below
+1. Download a version of the [solc](https://docs.soliditylang.org/en/develop/installing-solidity.html) compiler.  If it is not named exactly `solc` and in your `$PATH`, see the `--solc` option below
 
 1. Check out or clone the appropriate branch of this repository.
 
 1. Go to the project root and run `git checkout <ref>` with the tag, branch, or commit you want to build.
 
@@ -143,10 +143,15 @@ Useful for early versions of `solc` 0.8.x, where Yul was considered highly exper
 #### `--system-mode`
 Enable system contract compilation mode.  
 In this mode, zkEVM extensions are enabled. For example, calls to addresses `0xFFFF` and less are substituted with special
 zkEVM instructions. In the Yul mode, the `verbatim_*` and `throw` instructions become available.
 
+#### `--metadata-hash`
+Set the metadata hash mode.  
+The only supported value is `none` that disables appending the metadata hash.  
+Is enabled by default.
+
 #### `--asm`
 Output zkEVM assembly of the contracts.
 
 #### `--bin`
 Output zkEVM bytecode of the contracts.
diff --git a/src/yul/parser/statement/expression/function_call/mod.rs b/src/yul/parser/statement/expression/function_call/mod.rs
index ab79a0a..b0ea35f 100644
--- a/src/yul/parser/statement/expression/function_call/mod.rs
+++ b/src/yul/parser/statement/expression/function_call/mod.rs
@@ -110,14 +110,15 @@ impl FunctionCall {
                 if name
                     .starts_with(compiler_llvm_context::Function::ZKSYNC_NEAR_CALL_ABI_PREFIX)
                     && context.is_system_mode() =>
             {
                 let mut values = Vec::with_capacity(self.arguments.len());
-                for argument in self.arguments.into_iter() {
+                for argument in self.arguments.into_iter().rev() {
                     let value = argument.into_llvm(context)?.expect("Always exists").value;
                     values.push(value);
                 }
+                values.reverse();
                 let function = context.get_function(name.as_str()).ok_or_else(|| {
                     anyhow::anyhow!("{} Undeclared function `{}`", location, name)
                 })?;
                 let r#return = function.borrow().r#return();
 
@@ -169,14 +170,15 @@ impl FunctionCall {
                     Ok(return_value)
                 }
             }
             Name::UserDefined(name) => {
                 let mut values = Vec::with_capacity(self.arguments.len());
-                for argument in self.arguments.into_iter() {
+                for argument in self.arguments.into_iter().rev() {
                     let value = argument.into_llvm(context)?.expect("Always exists").value;
                     values.push(value);
                 }
+                values.reverse();
                 let function = context.get_function(name.as_str()).ok_or_else(|| {
                     anyhow::anyhow!("{} Undeclared function `{}`", location, name)
                 })?;
 
                 let expected_arguments_count =
@@ -673,21 +675,21 @@ impl FunctionCall {
             }
             Name::Stop => compiler_llvm_context::r#return::stop(context).map(|_| None),
             Name::Invalid => compiler_llvm_context::r#return::invalid(context).map(|_| None),
 
             Name::Log0 => {
-                let arguments = self.pop_arguments_llvm_log::<D, 2>(context)?;
+                let arguments = self.pop_arguments_llvm::<D, 2>(context)?;
                 compiler_llvm_context::event::log(
                     context,
                     arguments[0].into_int_value(),
                     arguments[1].into_int_value(),
                     vec![],
                 )
                 .map(|_| None)
             }
             Name::Log1 => {
-                let arguments = self.pop_arguments_llvm_log::<D, 3>(context)?;
+                let arguments = self.pop_arguments_llvm::<D, 3>(context)?;
                 compiler_llvm_context::event::log(
                     context,
                     arguments[0].into_int_value(),
                     arguments[1].into_int_value(),
                     arguments[2..]
@@ -696,11 +698,11 @@ impl FunctionCall {
                         .collect(),
                 )
                 .map(|_| None)
             }
             Name::Log2 => {
-                let arguments = self.pop_arguments_llvm_log::<D, 4>(context)?;
+                let arguments = self.pop_arguments_llvm::<D, 4>(context)?;
                 compiler_llvm_context::event::log(
                     context,
                     arguments[0].into_int_value(),
                     arguments[1].into_int_value(),
                     arguments[2..]
@@ -709,11 +711,11 @@ impl FunctionCall {
                         .collect(),
                 )
                 .map(|_| None)
             }
             Name::Log3 => {
-                let arguments = self.pop_arguments_llvm_log::<D, 5>(context)?;
+                let arguments = self.pop_arguments_llvm::<D, 5>(context)?;
                 compiler_llvm_context::event::log(
                     context,
                     arguments[0].into_int_value(),
                     arguments[1].into_int_value(),
                     arguments[2..]
@@ -722,11 +724,11 @@ impl FunctionCall {
                         .collect(),
                 )
                 .map(|_| None)
             }
             Name::Log4 => {
-                let arguments = self.pop_arguments_llvm_log::<D, 6>(context)?;
+                let arguments = self.pop_arguments_llvm::<D, 6>(context)?;
                 compiler_llvm_context::event::log(
                     context,
                     arguments[0].into_int_value(),
                     arguments[1].into_int_value(),
                     arguments[2..]
@@ -1396,13 +1398,14 @@ impl FunctionCall {
     ) -> anyhow::Result<[inkwell::values::BasicValueEnum<'ctx>; N]>
     where
         D: compiler_llvm_context::Dependency,
     {
         let mut arguments = Vec::with_capacity(N);
-        for expression in self.arguments.drain(0..N) {
+        for expression in self.arguments.drain(0..N).rev() {
             arguments.push(expression.into_llvm(context)?.expect("Always exists").value);
         }
+        arguments.reverse();
 
         Ok(arguments.try_into().expect("Always successful"))
     }
 
     ///
@@ -1414,34 +1417,13 @@ impl FunctionCall {
     ) -> anyhow::Result<[compiler_llvm_context::Argument<'ctx>; N]>
     where
         D: compiler_llvm_context::Dependency,
     {
         let mut arguments = Vec::with_capacity(N);
-        for expression in self.arguments.drain(0..N) {
+        for expression in self.arguments.drain(0..N).rev() {
             arguments.push(expression.into_llvm(context)?.expect("Always exists"));
         }
-
-        Ok(arguments.try_into().expect("Always successful"))
-    }
-
-    ///
-    /// Pops the specified number of arguments, converted into their LLVM values.
-    ///
-    /// This function inverts the order of event topics, taking into account its behavior in EVM.
-    ///
-    fn pop_arguments_llvm_log<'ctx, D, const N: usize>(
-        &mut self,
-        context: &mut compiler_llvm_context::Context<'ctx, D>,
-    ) -> anyhow::Result<[inkwell::values::BasicValueEnum<'ctx>; N]>
-    where
-        D: compiler_llvm_context::Dependency,
-    {
-        self.arguments[2..].reverse();
-        let mut arguments = Vec::with_capacity(N);
-        for expression in self.arguments.drain(0..N) {
-            arguments.push(expression.into_llvm(context)?.expect("Always exists").value);
-        }
-        arguments[2..].reverse();
+        arguments.reverse();
 
         Ok(arguments.try_into().expect("Always successful"))
     }
 }

Vulnerable code: LLVM_SYS_150_PREFIX = { value = "./target-llvm/target-final/", relative = true, force = true }
version = "1.3.9"
version = "1.3.9"
   MUSL-based static builds do not depend on system libraries and run on any recent Linux distribution.
   Only Windows 10 has been tested so far, but other versions should be OK as well.
1. Download a version of the [solc](https://docs.soliditylang.org/en/develop/installing-solidity.html) compiler.  If it is not named exactly `solc` in your `$PATH`, see the `--solc` option below
                for argument in self.arguments.into_iter() {
                for argument in self.arguments.into_iter() {
                let arguments = self.pop_arguments_llvm_log::<D, 2>(context)?;
                let arguments = self.pop_arguments_llvm_log::<D, 3>(context)?;
                let arguments = self.pop_arguments_llvm_log::<D, 4>(context)?;
                let arguments = self.pop_arguments_llvm_log::<D, 5>(context)?;
                let arguments = self.pop_arguments_llvm_log::<D, 6>(context)?;
        for expression in self.arguments.drain(0..N) {
        for expression in self.arguments.drain(0..N) {

        Ok(arguments.try_into().expect("Always successful"))
    }

    ///
    /// Pops the specified number of arguments, converted into their LLVM values.
    ///
    /// This function inverts the order of event topics, taking into account its behavior in EVM.
    ///
    fn pop_arguments_llvm_log<'ctx, D, const N: usize>(
        &mut self,
        context: &mut compiler_llvm_context::Context<'ctx, D>,
    ) -> anyhow::Result<[inkwell::values::BasicValueEnum<'ctx>; N]>
    where
        D: compiler_llvm_context::Dependency,
    {
        self.arguments[2..].reverse();
        let mut arguments = Vec::with_capacity(N);
        for expression in self.arguments.drain(0..N) {
            arguments.push(expression.into_llvm(context)?.expect("Always exists").value);
        }
        arguments[2..].reverse();
Fixed code: LLVM_SYS_150_PREFIX = { value = "./target-llvm/target-final/", relative = true, force = false }
## [1.3.10] - 2023-04-23

### Fixed

- The evaluation order of Yul function arguments

version = "1.3.10"
version = "1.3.10"
   MUSL-based static builds do not depend on system libraries and can be run on any recent Linux distribution.
   Only Windows 10 has been tested so far, but other versions should be OK. 
1. Download a version of the [solc](https://docs.soliditylang.org/en/develop/installing-solidity.html) compiler.  If it is not named exactly `solc` and in your `$PATH`, see the `--solc` option below
#### `--metadata-hash`
Set the metadata hash mode.  
The only supported value is `none` that disables appending the metadata hash.  
Is enabled by default.

                for argument in self.arguments.into_iter().rev() {
                values.reverse();
                for argument in self.arguments.into_iter().rev() {
                values.reverse();
                let arguments = self.pop_arguments_llvm::<D, 2>(context)?;
                let arguments = self.pop_arguments_llvm::<D, 3>(context)?;
                let arguments = self.pop_arguments_llvm::<D, 4>(context)?;
                let arguments = self.pop_arguments_llvm::<D, 5>(context)?;
                let arguments = self.pop_arguments_llvm::<D, 6>(context)?;
        for expression in self.arguments.drain(0..N).rev() {
        arguments.reverse();
        for expression in self.arguments.drain(0..N).rev() {
        arguments.reverse();