This is an automated email from the ASF dual-hosted git repository.

chenBright pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/brpc.git


The following commit(s) were added to refs/heads/master by this push:
     new 7505b987 Fix Snappy decompression corruption (#3703)
7505b987 is described below

commit 7505b9879998164bc345f48df04a4ed4f4de0424
Author: Bright Chen <[email protected]>
AuthorDate: Wed Nov 4 10:08:04 2026 +0800

    Fix Snappy decompression corruption (#3703)
---
 .../third_party/snappy/snappy-stubs-internal.h     | 116 +++++----------------
 test/brpc_snappy_compress_unittest.cpp             |  62 +++++++++++
 2 files changed, 89 insertions(+), 89 deletions(-)

diff --git a/src/butil/third_party/snappy/snappy-stubs-internal.h 
b/src/butil/third_party/snappy/snappy-stubs-internal.h
index 764f97bc..b58817cb 100644
--- a/src/butil/third_party/snappy/snappy-stubs-internal.h
+++ b/src/butil/third_party/snappy/snappy-stubs-internal.h
@@ -32,9 +32,9 @@
 #define BUTIL_THIRD_PARTY_SNAPPY_OPENSOURCE_SNAPPY_STUBS_INTERNAL_H_
 
 #include <string>
+#include <cstring>
 #include <assert.h>
 #include <stdlib.h>
-#include <string.h>
 #include "butil/compiler_specific.h"
 #include "butil/basictypes.h"
 #include "butil/sys_byteorder.h"
@@ -69,100 +69,47 @@
 namespace butil {
 namespace snappy {
 
-// x86 and PowerPC can simply do these loads and stores native.
-
-#if defined(__i386__) || defined(__x86_64__) || defined(__powerpc__)
-
-#define UNALIGNED_LOAD16(_p) (*reinterpret_cast<const uint16_t *>(_p))
-#define UNALIGNED_LOAD32(_p) (*reinterpret_cast<const uint32_t *>(_p))
-#define UNALIGNED_LOAD64(_p) (*reinterpret_cast<const uint64_t *>(_p))
-
-#define UNALIGNED_STORE16(_p, _val) (*reinterpret_cast<uint16_t *>(_p) = 
(_val))
-#define UNALIGNED_STORE32(_p, _val) (*reinterpret_cast<uint32_t *>(_p) = 
(_val))
-#define UNALIGNED_STORE64(_p, _val) (*reinterpret_cast<uint64_t *>(_p) = 
(_val))
-
-// ARMv7 and newer support native unaligned accesses, but only of 16-bit
-// and 32-bit values (not 64-bit); older versions either raise a fatal signal,
-// do an unaligned read and rotate the words around a bit, or do the reads very
-// slowly (trip through kernel mode). There's no simple #define that says just
-// “ARMv7 or higher”, so we have to filter away all ARMv5 and ARMv6
-// sub-architectures.
-//
-// This is a mess, but there's not much we can do about it.
-
-#elif defined(__arm__) &&                       \
-    !defined(__ARM_ARCH_4__) &&                 \
-    !defined(__ARM_ARCH_4T__) &&                \
-    !defined(__ARM_ARCH_5__) &&                 \
-    !defined(__ARM_ARCH_5T__) &&                \
-    !defined(__ARM_ARCH_5TE__) &&               \
-    !defined(__ARM_ARCH_5TEJ__) &&              \
-    !defined(__ARM_ARCH_6__) &&                 \
-    !defined(__ARM_ARCH_6J__) &&                \
-    !defined(__ARM_ARCH_6K__) &&                \
-    !defined(__ARM_ARCH_6Z__) &&                \
-    !defined(__ARM_ARCH_6ZK__) &&               \
-    !defined(__ARM_ARCH_6T2__)
-
-#define UNALIGNED_LOAD16(_p) (*reinterpret_cast<const uint16_t *>(_p))
-#define UNALIGNED_LOAD32(_p) (*reinterpret_cast<const uint32_t *>(_p))
-
-#define UNALIGNED_STORE16(_p, _val) (*reinterpret_cast<uint16_t *>(_p) = 
(_val))
-#define UNALIGNED_STORE32(_p, _val) (*reinterpret_cast<uint32_t *>(_p) = 
(_val))
-
-// TODO(user): NEON supports unaligned 64-bit loads and stores.
-// See if that would be more efficient on platforms supporting it,
-// at least for copies.
-
-inline uint64_t UNALIGNED_LOAD64(const void *p) {
-    uint64_t t;
-    memcpy(&t, p, sizeof t);
-    return t;
-}
-
-inline void UNALIGNED_STORE64(void *p, uint64_t v) {
-    memcpy(p, &v, sizeof v);
-}
-
-#else
-
-// These functions are provided for architectures that don't support
-// unaligned loads and stores.
+// Potentially unaligned loads and stores.
+// Backported from google/snappy commit 041c608.
 
 inline uint16_t UNALIGNED_LOAD16(const void *p) {
-    uint16_t t;
-    memcpy(&t, p, sizeof t);
-    return t;
+    // Compiles to a single movzx/ldrh on clang/gcc/msvc.
+    uint16_t v;
+    std::memcpy(&v, p, sizeof(v));
+    return v;
 }
 
 inline uint32_t UNALIGNED_LOAD32(const void *p) {
-    uint32_t t;
-    memcpy(&t, p, sizeof t);
-    return t;
+    // Compiles to a single mov/ldr on clang/gcc/msvc.
+    uint32_t v;
+    std::memcpy(&v, p, sizeof(v));
+    return v;
 }
 
 inline uint64_t UNALIGNED_LOAD64(const void *p) {
-    uint64_t t;
-    memcpy(&t, p, sizeof t);
-    return t;
+    // Compiles to a single mov/ldr on clang/gcc/msvc.
+    uint64_t v;
+    std::memcpy(&v, p, sizeof(v));
+    return v;
 }
 
 inline void UNALIGNED_STORE16(void *p, uint16_t v) {
-    memcpy(p, &v, sizeof v);
+    // Compiles to a single mov/strh on clang/gcc/msvc.
+    std::memcpy(p, &v, sizeof(v));
 }
 
 inline void UNALIGNED_STORE32(void *p, uint32_t v) {
-    memcpy(p, &v, sizeof v);
+    // Compiles to a single mov/str on clang/gcc/msvc.
+    std::memcpy(p, &v, sizeof(v));
 }
 
 inline void UNALIGNED_STORE64(void *p, uint64_t v) {
-    memcpy(p, &v, sizeof v);
+    // Compiles to a single mov/str on clang/gcc/msvc.
+    std::memcpy(p, &v, sizeof(v));
 }
 
-#endif
-
-// This can be more efficient than UNALIGNED_LOAD64 + UNALIGNED_STORE64
-// on some platforms, in particular ARM.
+// Support overlapping source and destination buffers.
+// Backported from google/snappy commit 3c706d2.
 inline void UnalignedCopy64(const void *src, void *dst) {
 #if defined(__riscv) && __riscv_xlen == 64
     // RISC-V optimized: single ld/sd pair for 8-byte copy (aligned only)
@@ -174,21 +121,12 @@ inline void UnalignedCopy64(const void *src, void *dst) {
             : "=&r"(tmp)
             : "m"(*(const uint64_t*)src), "m"(*(uint64_t*)dst)
             : "memory");
-    } else {
-        // Unaligned: fall back to memcpy-based approach
-        memcpy(dst, src, 8);
-    }
-#else
-    if (sizeof(void *) == 8) {
-        UNALIGNED_STORE64(dst, UNALIGNED_LOAD64(src));
-    } else {
-        const char *src_char = reinterpret_cast<const char *>(src);
-        char *dst_char = reinterpret_cast<char *>(dst);
-
-        UNALIGNED_STORE32(dst_char, UNALIGNED_LOAD32(src_char));
-        UNALIGNED_STORE32(dst_char + 4, UNALIGNED_LOAD32(src_char + 4));
+        return;
     }
 #endif
+    char tmp[8];
+    std::memcpy(tmp, src, 8);
+    std::memcpy(dst, tmp, 8);
 }
 
 // Convert to little-endian storage, opposite of network format.
diff --git a/test/brpc_snappy_compress_unittest.cpp 
b/test/brpc_snappy_compress_unittest.cpp
index a71b1766..aa822be8 100644
--- a/test/brpc_snappy_compress_unittest.cpp
+++ b/test/brpc_snappy_compress_unittest.cpp
@@ -105,6 +105,68 @@ TEST_F(test_compress_method, snappy_iobuf) {
     ASSERT_STREQ(check_buf.to_string().c_str(), test);
 }
 
+TEST_F(test_compress_method, snappy_overlapping_copy) {
+    // Short offsets require successive copies to read bytes written by the
+    // preceding copy. Integer-pointer casts used for unaligned accesses cause
+    // Clang 18 -O2 to reuse a load and corrupt the overlapping copies.
+    for (size_t offset = 1; offset < 8; ++offset) {
+        SCOPED_TRACE(offset);
+        const std::string pattern = std::string("abcdefg").substr(0, offset);
+        const std::string suffix = "0123456789abcdef";
+        std::string expected = pattern;
+        for (size_t i = 0; i < 64; ++i) {
+            expected.push_back(pattern[i % offset]);
+        }
+        expected += suffix;
+
+        // Encode a literal, a 64-byte copy with a two-byte offset, and a
+        // trailing literal that leaves room for the fast copy path.
+        std::string compressed;
+        compressed.push_back(static_cast<char>(expected.size()));
+        compressed.push_back(static_cast<char>((offset - 1) << 2));
+        compressed += pattern;
+        compressed.push_back(static_cast<char>(0xfe));
+        compressed.push_back(static_cast<char>(offset));
+        compressed.push_back('\0');
+        compressed.push_back(static_cast<char>((suffix.size() - 1) << 2));
+        compressed += suffix;
+
+        std::string flat;
+        ASSERT_TRUE(butil::snappy::Uncompress(
+            compressed.data(), compressed.size(), &flat));
+        EXPECT_EQ(expected, flat);
+
+        butil::IOBuf input;
+        input.append(compressed);
+        butil::IOBuf first, second;
+        ASSERT_TRUE(brpc::policy::SnappyDecompress(input, &first));
+        ASSERT_TRUE(brpc::policy::SnappyDecompress(input, &second));
+        EXPECT_EQ(expected, first.to_string());
+        EXPECT_EQ(expected, second.to_string());
+    }
+}
+
+TEST_F(test_compress_method, snappy_repeated_pattern_across_blocks) {
+    const size_t block_size = butil::snappy::kBlockSize;
+    const size_t sizes[] = {
+        block_size - 1, block_size, block_size + 1, 2 * block_size + 17
+    };
+    const char pattern[] = {'a', '\0', 'b'};
+    for (size_t size : sizes) {
+        SCOPED_TRACE(size);
+        std::string expected;
+        for (size_t i = 0; i < size; ++i) {
+            expected.push_back(pattern[i % sizeof(pattern)]);
+        }
+        butil::IOBuf input, compressed, output;
+        input.append(expected);
+        ASSERT_TRUE(brpc::policy::SnappyCompress(input, &compressed));
+        ASSERT_TRUE(brpc::policy::SnappyDecompress(compressed, &output));
+        ASSERT_EQ(expected.size(), output.size());
+        ASSERT_TRUE(expected == output.to_string());
+    }
+}
+
 TEST_F(test_compress_method, mass_snappy) {
     snappy_message::SnappyMessageProto old_msg;
     int len = 12435; 


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to