[llvm] [Support] Use a 64-bit byte count in SHA1 and SHA256 (PR #230385)

Buyun Xu via llvm-commits llvm-commits at lists.llvm.org
Sat Oct 10 21:52:54 PDT 2026


https://github.com/felixtensor updated https://github.com/llvm/llvm-project/pull/230385

>From d9fec9377b7c36f6d93adae43c1cabf708b98f63 Mon Sep 17 00:00:00 2001
From: Buyun Xu <xubuyun at outlook.com>
Date: Fri, 9 Oct 2026 14:48:21 +0800
Subject: [PATCH] [Support] Use a 64-bit byte count in SHA1 and SHA256

The byte count was a uint32_t, so SHA1 gave wrong digests for inputs of
4 GiB or more. SHA256 also converted it to a bit length using 32-bit
arithmetic, which overflows at 512 MiB.
---
 llvm/include/llvm/Support/SHA1.h                 |  2 +-
 llvm/include/llvm/Support/SHA256.h               |  2 +-
 llvm/lib/Support/SHA1.cpp                        |  6 +++---
 llvm/unittests/Support/SHA256.cpp                | 14 ++++++++++++++
 llvm/unittests/Support/raw_sha1_ostream_test.cpp | 12 ++++++++++++
 5 files changed, 31 insertions(+), 5 deletions(-)

diff --git a/llvm/include/llvm/Support/SHA1.h b/llvm/include/llvm/Support/SHA1.h
index 93d14a3b76636..4428be417c0ec 100644
--- a/llvm/include/llvm/Support/SHA1.h
+++ b/llvm/include/llvm/Support/SHA1.h
@@ -65,7 +65,7 @@ class SHA1 {
       uint32_t L[BLOCK_LENGTH / 4];
     } Buffer;
     uint32_t State[HASH_LENGTH / 4];
-    uint32_t ByteCount;
+    uint64_t ByteCount;
     uint8_t BufferOffset;
   } InternalState;
 
diff --git a/llvm/include/llvm/Support/SHA256.h b/llvm/include/llvm/Support/SHA256.h
index 2704db1b03d78..aab1ddd5713db 100644
--- a/llvm/include/llvm/Support/SHA256.h
+++ b/llvm/include/llvm/Support/SHA256.h
@@ -72,7 +72,7 @@ class SHA256 {
       uint32_t L[BLOCK_LENGTH / 4];
     } Buffer;
     uint32_t State[HASH_LENGTH / 4];
-    uint32_t ByteCount;
+    uint64_t ByteCount;
     uint8_t BufferOffset;
   } InternalState;
 
diff --git a/llvm/lib/Support/SHA1.cpp b/llvm/lib/Support/SHA1.cpp
index 412f894492bfe..5bcdef7c4958f 100644
--- a/llvm/lib/Support/SHA1.cpp
+++ b/llvm/lib/Support/SHA1.cpp
@@ -247,9 +247,9 @@ void SHA1::pad() {
     addUncounted(0x00);
 
   // Append length in the last 8 bytes
-  addUncounted(0); // We're only using 32 bit lengths
-  addUncounted(0); // But SHA-1 supports 64 bit lengths
-  addUncounted(0); // So zero pad the top bits
+  addUncounted(InternalState.ByteCount >> 53);
+  addUncounted(InternalState.ByteCount >> 45);
+  addUncounted(InternalState.ByteCount >> 37);
   addUncounted(InternalState.ByteCount >> 29); // Shifting to multiply by 8
   addUncounted(InternalState.ByteCount >>
                21); // as SHA-1 supports bitstreams as well as
diff --git a/llvm/unittests/Support/SHA256.cpp b/llvm/unittests/Support/SHA256.cpp
index f33d2d5fcfec5..8652bc79cd2d7 100644
--- a/llvm/unittests/Support/SHA256.cpp
+++ b/llvm/unittests/Support/SHA256.cpp
@@ -76,4 +76,18 @@ TEST(SHA256Test, SHA256) {
             "cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0");
 }
 
+#if defined(EXPENSIVE_CHECKS)
+TEST(SHA256Test, LargeInput) {
+  std::string rep(1 << 20, '\0');
+  SHA256 Hash;
+  for (int i = 0; i < 512; ++i) {
+    Hash.update({reinterpret_cast<const uint8_t *>(rep.data()), rep.size()});
+  }
+  auto hash = Hash.final();
+  auto hashStr = toHex(hash);
+  EXPECT_EQ(hashStr,
+            "9acca8e8c22201155389f65abbf6bc9723edc7384ead80503839f49dcc56d767");
+}
+#endif
+
 } // namespace
diff --git a/llvm/unittests/Support/raw_sha1_ostream_test.cpp b/llvm/unittests/Support/raw_sha1_ostream_test.cpp
index be8333237d81d..8ac4aaaa90eb4 100644
--- a/llvm/unittests/Support/raw_sha1_ostream_test.cpp
+++ b/llvm/unittests/Support/raw_sha1_ostream_test.cpp
@@ -46,6 +46,18 @@ TEST(sha1_hash_test, Update) {
   ASSERT_EQ("3E4A614101AD84985AB0FE54DC12A6D71551E5AE", Hash);
 }
 
+#if defined(EXPENSIVE_CHECKS)
+TEST(sha1_hash_test, LargeInput) {
+  SHA1 sha1;
+  std::string Input(1 << 20, '\0');
+  for (int I = 0; I < 4096; ++I)
+    sha1.update(Input);
+
+  std::string Hash = toHex(sha1.final());
+  ASSERT_EQ("1BF99EE9F374E58E201E4DDA4F474E570EB77229", Hash);
+}
+#endif
+
 // Check that getting the intermediate hash in the middle of the stream does
 // not invalidate the final result.
 TEST(raw_sha1_ostreamTest, Intermediate) {



More information about the llvm-commits mailing list