[GBL] Add buffer overlapping check

Add itetools crate. It is used in testing.
Add 'is_overlap()' function to check provided buffers validity.

Bug: 312608785
Test: ./tools/bazel test @gbl//tests --extra_toolchains=@gbl//toolchain:all --verbose_failures
Change-Id: Ia09b5256c0be5fac9719c26fa97956c15dd4cbbe
diff --git a/gbl/integration/aosp_u-boot-mainline/workspace.bzl b/gbl/integration/aosp_u-boot-mainline/workspace.bzl
index 307d84d..5d5b653 100644
--- a/gbl/integration/aosp_u-boot-mainline/workspace.bzl
+++ b/gbl/integration/aosp_u-boot-mainline/workspace.bzl
@@ -21,7 +21,7 @@
 load("@gbl//toolchain:gbl_workspace_util.bzl", "android_rust_prebuilts", "gbl_llvm_prebuilts")
 load("@kernel_toolchain_info//:dict.bzl", "CLANG_VERSION")
 
-def rust_crate_build_file(name, deps = [], features = [], rustc_flags = []):
+def rust_crate_build_file(name, crate_name = "", deps = [], features = [], rustc_flags = []):
     """Generate BUILD file content for a rust crate
 
     This helper is suitable for crates that have straightforward build rules. Specifically, the
@@ -30,6 +30,7 @@
 
     Args:
         name (String): name of the rust_library target.
+        crate_name (String): name of the rust_library crate, same as name by default.
         deps (List of strings): The `deps` field.
         features (List of strings): The `features` field.
         rustc_flags (List of strings): The `rustc_flags` field.
@@ -37,6 +38,7 @@
     Returns:
         A string for the BUILD file content.
     """
+    crate_name = name if len(crate_name) == 0 else crate_name
     deps = "[{}]".format(",".join(["\"{}\"".format(ele) for ele in deps]))
     features = "[{}]".format(",".join(["\"{}\"".format(ele) for ele in features]))
     rustc_flags = "[{}]".format(",".join(["\"{}\"".format(ele) for ele in rustc_flags]))
@@ -45,6 +47,7 @@
 
 rust_library(
     name = \"{}\",
+    crate_name = \"{}\",
     srcs = glob(["**/*.rs"]),
     crate_features = {},
     edition = "2021",
@@ -52,7 +55,7 @@
     visibility = ["//visibility:public"],
     deps = {},
 )
-""".format(name, features, rustc_flags, deps)
+""".format(name, crate_name, features, rustc_flags, deps)
 
 def define_gbl_workspace(name = None):
     """Set up worksapce dependencies for GBL
@@ -198,6 +201,48 @@
     )
 
     native.new_local_repository(
+        name = "itertools",
+        path = "external/rust/crates/itertools",
+        build_file_content = rust_crate_build_file(
+            "itertools",
+            deps = ["@either"],
+            features = ["default", "use_std", "use_alloc"],
+            rustc_flags = ["-A", "dead_code"],
+        ),
+    )
+
+    native.new_local_repository(
+        name = "itertools_noalloc",
+        path = "external/rust/crates/itertools",
+        build_file_content = rust_crate_build_file(
+            "itertools_noalloc",
+            crate_name = "itertools",
+            features = [],
+            deps = ["@either_noalloc"],
+            rustc_flags = ["-A", "dead_code"],
+        ),
+    )
+
+    native.new_local_repository(
+        name = "either",
+        path = "external/rust/crates/either",
+        build_file_content = rust_crate_build_file(
+            "either",
+            features = ["default", "use_std"],
+        ),
+    )
+
+    native.new_local_repository(
+        name = "either_noalloc",
+        path = "external/rust/crates/either",
+        build_file_content = rust_crate_build_file(
+            "either_noalloc",
+            crate_name = "either",
+            features = [],
+        ),
+    )
+
+    native.new_local_repository(
         name = "smoltcp",
         path = "external/rust/crates/smoltcp",
         build_file = "@gbl//smoltcp:BUILD.smoltcp.bazel",
diff --git a/gbl/libgbl/BUILD b/gbl/libgbl/BUILD
index df19be0..0555d47 100644
--- a/gbl/libgbl/BUILD
+++ b/gbl/libgbl/BUILD
@@ -20,6 +20,7 @@
         ["**/*.rs"],
         exclude = ["tests/**/*.rs"],
     ),
+    aliases = {"@itertools_noalloc": "itertools_noalloc"},
     edition = "2021",
     visibility = ["//visibility:public"],
     deps = [
@@ -31,6 +32,7 @@
         "@gbl//libsafemath",
         "@gbl//libstorage",
         "@gbl//third_party/libzbi",
+        "@itertools_noalloc",
         "@spin",
         "@static_assertions",
         "@zerocopy",
@@ -39,6 +41,7 @@
 
 rust_test(
     name = "libgbl_test",
+    aliases = {"@itertools_noalloc": "itertools_noalloc"},
     compile_data = ["@gbl//libstorage/test:test_data"],
     crate = ":libgbl",
     crate_features = ["uuid"],
@@ -56,6 +59,8 @@
         "@avb//:avb_test",
         "@gbl//libavb:sysdeps",
         "@gbl//libstorage:libstorage_testlib",
+        "@itertools",
+        "@itertools_noalloc",
         "@static_assertions",
         "@uuid",
     ],
diff --git a/gbl/libgbl/src/error.rs b/gbl/libgbl/src/error.rs
index fcc4a1a..acfe8be 100644
--- a/gbl/libgbl/src/error.rs
+++ b/gbl/libgbl/src/error.rs
@@ -42,6 +42,8 @@
     /// AvbOps were already borrowed. This would happen on second `load_and_verify_image()` call
     /// unless `reuse()` is called before.
     AvbOpsBusy,
+    /// Buffers overlap and can cause undefined behavior and data corruption.
+    BufferOverlap,
 }
 
 impl Display for Error {
@@ -55,6 +57,7 @@
             Error::OperationProhibited => write!(f, "Operation is prohibited"),
             Error::Internal => write!(f, "Internal error"),
             Error::AvbOpsBusy => write!(f, "AvbOps were already borrowed"),
+            Error::BufferOverlap => write!(f, "Buffers overlap"),
         }
     }
 }
diff --git a/gbl/libgbl/src/lib.rs b/gbl/libgbl/src/lib.rs
index dc8912e..c170e4e 100644
--- a/gbl/libgbl/src/lib.rs
+++ b/gbl/libgbl/src/lib.rs
@@ -47,6 +47,7 @@
 pub mod error;
 pub mod fastboot;
 pub mod ops;
+mod overlap;
 
 /// The 'slots' module, containing types and traits for
 /// querying and modifying slotted boot behavior.
@@ -63,6 +64,7 @@
 };
 
 use ops::GblUtils;
+use overlap::is_overlap;
 
 // TODO: b/312607649 - Replace placeholders with actual structures: https://r.android.com/2721974, etc
 /// TODO: b/312607649 - placeholder type
@@ -504,6 +506,16 @@
         let vendor_boot_image = vendor_boot_image.ok_or(Error::MissingImage)?;
         let init_boot_image = init_boot_image.ok_or(Error::MissingImage)?;
 
+        if is_overlap(&[
+            boot_image.0,
+            vendor_boot_image.0,
+            init_boot_image.0,
+            &ramdisk.0,
+            kernel_load_buffer,
+        ]) {
+            return Err(IntegrationError::GblNativeError(Error::BufferOverlap));
+        }
+
         let info_struct = self.unpack_boot_image(&boot_image, Some(boot_target))?;
 
         let kernel_image = self.kernel_load(&info_struct, boot_image, kernel_load_buffer)?;
diff --git a/gbl/libgbl/src/ops.rs b/gbl/libgbl/src/ops.rs
index 6063c4c..20e04c6 100644
--- a/gbl/libgbl/src/ops.rs
+++ b/gbl/libgbl/src/ops.rs
@@ -16,8 +16,6 @@
 //!
 #[cfg(feature = "alloc")]
 extern crate alloc;
-#[cfg(test)]
-extern crate static_assertions;
 
 use crate::error::{Error, Result as GblResult};
 #[cfg(feature = "alloc")]
diff --git a/gbl/libgbl/src/overlap.rs b/gbl/libgbl/src/overlap.rs
new file mode 100644
index 0000000..20d129d
--- /dev/null
+++ b/gbl/libgbl/src/overlap.rs
@@ -0,0 +1,121 @@
+// Copyright 2024, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! Helper functions to verify image buffers
+use core::cmp::{max, min};
+extern crate itertools_noalloc;
+use itertools_noalloc::Itertools;
+
+/// Check if provided buffers overlap in any way.
+///
+/// Note that zero length buffer is considered to contain no elements.
+/// And would not overlap with any other buffer.
+///
+/// # Args
+///
+/// * `buffers`: slice of buffers to verify
+///
+/// # Returns
+///
+/// * true: if any of the buffers have common elements
+/// * false: if there are no common elements in buffers
+pub fn is_overlap(buffers: &[&[u8]]) -> bool {
+    // Compare each with each since we can't use alloc and sort buffers.
+    // Since the number of buffers we expect is not big, O(n^2) complexity will do.
+    //
+    // Note: this is nice way to find out if 2 ranges overlap:
+    // max(a_start, b_start) > min(a_end, b_end)) -> no overlap
+    buffers
+        .iter()
+        .filter(|buffer| !buffer.is_empty())
+        .map(|slice: &&[u8]| (slice.as_ptr(), slice.last_chunk::<1>().unwrap().as_ptr()))
+        .tuple_combinations()
+        .any(|((a_start, a_end), (b_start, b_end))| !(max(a_start, b_start) > min(a_end, b_end)))
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use itertools::Itertools;
+
+    // Creates slice of specified range: [first; last)
+    // Max range value is SIZE = 64;
+    fn get_range(first: usize, last: usize) -> &'static [u8] {
+        const SIZE: usize = 64;
+        assert!(first < SIZE);
+        assert!(last <= SIZE);
+        static BUFFER: &'static [u8; SIZE] = &[0; SIZE];
+        &BUFFER[first..last]
+    }
+
+    // Check if ranges overlap, testing all permutations
+    fn check_overlap(ranges_set: &[(usize, usize)]) -> bool {
+        ranges_set.iter().permutations(ranges_set.len()).all(|ranges| {
+            let ranges_slices: Vec<&[u8]> =
+                ranges.iter().map(|&(start, end)| get_range(*start, *end)).collect();
+            is_overlap(&ranges_slices)
+        })
+    }
+
+    // Check if ranges don't overlap, testing all permutations
+    fn check_not_overlap(ranges_set: &[(usize, usize)]) -> bool {
+        ranges_set.iter().permutations(ranges_set.len()).all(|ranges| {
+            let ranges_slices: Vec<&[u8]> =
+                ranges.iter().map(|&(start, end)| get_range(*start, *end)).collect();
+            !is_overlap(&ranges_slices)
+        })
+    }
+
+    #[test]
+    fn test_is_overlap_false() {
+        assert!(check_not_overlap(&[(10, 15), (20, 25), (30, 35)]));
+    }
+
+    #[test]
+    fn test_is_overlap_true() {
+        assert!(check_overlap(&[(10, 19), (15, 25)]));
+    }
+
+    #[test]
+    fn test_is_overlap_included() {
+        assert!(check_overlap(&[(10, 11), (10, 11)]));
+        assert!(check_overlap(&[(10, 12), (10, 12)]));
+        assert!(check_overlap(&[(10, 13), (11, 12)]));
+        assert!(check_overlap(&[(10, 14), (11, 13)]));
+        assert!(check_overlap(&[(10, 20), (15, 18)]));
+        assert!(check_overlap(&[(10, 20), (11, 19), (12, 18), (13, 17)]));
+    }
+
+    #[test]
+    fn test_is_overlap_touching() {
+        assert!(check_not_overlap(&[(10, 20), (20, 30), (30, 31)]));
+    }
+
+    #[test]
+    fn test_is_overlap_last_element() {
+        assert!(check_overlap(&[(10, 20), (19, 21)]));
+    }
+
+    #[test]
+    fn test_is_overlap_short() {
+        assert!(check_not_overlap(&[(10, 11), (11, 12), (12, 13)]));
+    }
+
+    #[test]
+    fn test_is_overlap_empty_slice() {
+        assert!(check_not_overlap(&[]));
+        assert!(check_not_overlap(&[(10, 10)]));
+        assert!(check_not_overlap(&[(10, 20), (10, 10), (20, 30), (11, 11), (23, 23)]));
+    }
+}