CVE-2024-35229 (published 2024-05-27T17:15Z)

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.\n


Weakness information:
cwe_id: NVD-CWE-noinfo
cwe_name: Insufficient Information
description: There is insufficient information about the issue to classify it; details are unkown or unspecified.
extended_description: Insufficient Information

Fix information:
repo_url: https://github.com/matter-labs/era-compiler-solidity
commit_details: repo_url: https://github.com/matter-labs/era-compiler-solidity
author: Alex Z
author_date: 2023-06-01 13:01:40+02:00
msg: Release v1.3.10
repository: repo_url: https://github.com/matter-labs/era-compiler-solidity
repo_name: matter-labs/era-compiler-solidity
description: Solidity compiler for ZKsync
repo_language: Rust
owner: matter-labs
file_changes: filename: config.toml
change_type: ModificationType.MODIFY
diff: @@ -20,7 +20,7 @@ rustflags = [
 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 = [

programming_language: Lua
filename: CHANGELOG.md
change_type: ModificationType.MODIFY
diff: @@ -1,5 +1,11 @@
 # The `zksolc` changelog
 
+## [1.3.10] - 2023-04-23
+
+### Fixed
+
+- The evaluation order of Yul function arguments
+
 ## [1.3.9] - 2023-04-18
 
 ### Added

programming_language: Markdown
filename: Cargo.lock
change_type: ModificationType.MODIFY
diff: @@ -148,7 +148,7 @@ dependencies = [
 
 [[package]]
 name = "compiler-solidity"
-version = "1.3.9"
+version = "1.3.10"
 dependencies = [
  "anyhow",
  "colored",

programming_language: PowerShell
filename: Cargo.toml
change_type: ModificationType.MODIFY
diff: @@ -1,6 +1,6 @@
 [package]
 name = "compiler-solidity"
-version = "1.3.9"
+version = "1.3.10"
 authors = [
     "Alex Zarudnyy <a.zarudnyy@matterlabs.dev>",
 ]

programming_language: Lua
filename: README.md
change_type: ModificationType.MODIFY
diff: @@ -13,10 +13,10 @@ This repository contains the compiler from Solidity to zkEVM bytecode.
 
 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.
 
@@ -34,7 +34,7 @@ We recommend at least 4 GB of RAM available for the build process.
    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.
 
@@ -145,6 +145,11 @@ 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.
 

programming_language: Markdown
filename: mod.rs
change_type: ModificationType.MODIFY
diff: @@ -112,10 +112,11 @@ impl FunctionCall {
                     && 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)
                 })?;
@@ -171,10 +172,11 @@ impl FunctionCall {
             }
             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)
                 })?;
@@ -675,7 +677,7 @@ impl FunctionCall {
             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(),
@@ -685,7 +687,7 @@ impl FunctionCall {
                 .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(),
@@ -698,7 +700,7 @@ impl FunctionCall {
                 .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(),
@@ -711,7 +713,7 @@ impl FunctionCall {
                 .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(),
@@ -724,7 +726,7 @@ impl FunctionCall {
                 .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(),
@@ -1398,9 +1400,10 @@ impl FunctionCall {
         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"))
     }
@@ -1416,31 +1419,10 @@ impl FunctionCall {
         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"))
     }

programming_language: Rust