[llvm-branch-commits] [llvm] [Support] Add SipHash-based 16-bit ptrauth stable hash. (PR #93902)
Ahmed Bougacha via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Tue Jun 11 16:18:38 PDT 2024
https://github.com/ahmedbougacha updated https://github.com/llvm/llvm-project/pull/93902
>From bf413d68cff5ad963c43bb584590908bf03bc3ce Mon Sep 17 00:00:00 2001
From: Ahmed Bougacha <ahmed at bougacha.org>
Date: Tue, 4 Jun 2024 12:36:33 -0700
Subject: [PATCH] [Support] Add SipHash-based 16-bit ptrauth stable hash.
This finally wraps the now-lightly-modified SipHash C reference
implementation, for the main interface we need (16-bit ptrauth
discriminators).
This intentionally doesn't expose a raw interface beyond that to
encourage others to carefully consider their use.
The exact algorithm is the little-endian interpretation of the
non-doubled (i.e. 64-bit) result of applying a SipHash-2-4 using the
constant seed `b5d4c9eb79104a796fec8b1b428781d4` (big-endian), with the
result reduced by modulo to the range of non-zero discriminators (i.e.
`(rawHash % 65535) + 1`).
By "stable" we mean that the result of this hash algorithm will the same
across different compiler versions and target platforms.
The 16-bit hashes are used extensively for the AArch64 ptrauth ABI,
because AArch64 can efficiently load a 16-bit immediate into the high
bits of a register without disturbing the remainder of the value, which
serves as a nice blend operation.
16 bits is also sufficiently compact to not inflate a loader relocation.
We disallow zero to guarantee a different discriminator from the places
in the ABI that use a constant zero.
Co-Authored-By: John McCall <rjmccall at apple.com>
---
llvm/include/llvm/Support/SipHash.h | 39 ++++++++++++++++++++++++++
llvm/lib/Support/SipHash.cpp | 35 +++++++++++++++++++++++
llvm/unittests/Support/CMakeLists.txt | 1 +
llvm/unittests/Support/SipHashTest.cpp | 33 ++++++++++++++++++++++
4 files changed, 108 insertions(+)
create mode 100644 llvm/include/llvm/Support/SipHash.h
create mode 100644 llvm/unittests/Support/SipHashTest.cpp
diff --git a/llvm/include/llvm/Support/SipHash.h b/llvm/include/llvm/Support/SipHash.h
new file mode 100644
index 0000000000000..91447b2344eeb
--- /dev/null
+++ b/llvm/include/llvm/Support/SipHash.h
@@ -0,0 +1,39 @@
+//===--- SipHash.h - An ABI-stable string SipHash ---------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// A family of ABI-stable string hash algorithms based on SipHash, currently
+// used to compute ptrauth discriminators.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_SUPPORT_SIPHASH_H
+#define LLVM_SUPPORT_SIPHASH_H
+
+#include <cstdint>
+
+namespace llvm {
+class StringRef;
+
+/// Compute a stable non-zero 16-bit hash of the given string.
+///
+/// The exact algorithm is the little-endian interpretation of the
+/// non-doubled (i.e. 64-bit) result of applying a SipHash-2-4 using
+/// a specific seed value which can be found in the source.
+/// This 64-bit result is truncated to a non-zero 16-bit value.
+///
+/// We use a 16-bit discriminator because ARM64 can efficiently load
+/// a 16-bit immediate into the high bits of a register without disturbing
+/// the remainder of the value, which serves as a nice blend operation.
+/// 16 bits is also sufficiently compact to not inflate a loader relocation.
+/// We disallow zero to guarantee a different discriminator from the places
+/// in the ABI that use a constant zero.
+uint16_t getPointerAuthStableSipHash(StringRef S);
+
+} // end namespace llvm
+
+#endif
diff --git a/llvm/lib/Support/SipHash.cpp b/llvm/lib/Support/SipHash.cpp
index ef882ae4d8745..b1b4bede7637d 100644
--- a/llvm/lib/Support/SipHash.cpp
+++ b/llvm/lib/Support/SipHash.cpp
@@ -5,10 +5,23 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
+//
+// This file implements an ABI-stable string hash based on SipHash, used to
+// compute ptrauth discriminators.
+//
+//===----------------------------------------------------------------------===//
+#include "llvm/Support/SipHash.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Compiler.h"
+#include "llvm/Support/Debug.h"
#include <cstdint>
+using namespace llvm;
+
+#define DEBUG_TYPE "llvm-siphash"
+
// Lightly adapted from the SipHash reference C implementation by
// Jean-Philippe Aumasson and Daniel J. Bernstein.
@@ -133,3 +146,25 @@ static inline ResultTy siphash(const unsigned char *in, uint64_t inlen,
return firstHalf | (ResultTy(secondHalf) << (sizeof(ResultTy) == 8 ? 0 : 64));
}
+
+//===--- LLVM-specific wrapper around siphash.
+
+/// Compute an ABI-stable 16-bit hash of the given string.
+uint16_t llvm::getPointerAuthStableSipHash(StringRef Str) {
+ static const uint8_t K[16] = {0xb5, 0xd4, 0xc9, 0xeb, 0x79, 0x10, 0x4a, 0x79,
+ 0x6f, 0xec, 0x8b, 0x1b, 0x42, 0x87, 0x81, 0xd4};
+
+ // The aliasing is fine here because of omnipotent char.
+ const auto *Data = reinterpret_cast<const uint8_t *>(Str.data());
+ uint64_t RawHash = siphash<2, 4, uint64_t>(Data, Str.size(), K);
+
+ // Produce a non-zero 16-bit discriminator.
+ uint16_t Discriminator = (RawHash % 0xFFFF) + 1;
+ LLVM_DEBUG(
+ dbgs() << "ptrauth stable hash discriminator: " << utostr(Discriminator)
+ << " (0x"
+ << utohexstr(Discriminator, /*Lowercase=*/false, /*Width=*/4)
+ << ")"
+ << " of: " << Str << "\n");
+ return Discriminator;
+}
diff --git a/llvm/unittests/Support/CMakeLists.txt b/llvm/unittests/Support/CMakeLists.txt
index 2718be8450f80..631f2e6bf00df 100644
--- a/llvm/unittests/Support/CMakeLists.txt
+++ b/llvm/unittests/Support/CMakeLists.txt
@@ -75,6 +75,7 @@ add_llvm_unittest(SupportTests
ScopedPrinterTest.cpp
SHA256.cpp
SignalsTest.cpp
+ SipHashTest.cpp
SourceMgrTest.cpp
SpecialCaseListTest.cpp
SuffixTreeTest.cpp
diff --git a/llvm/unittests/Support/SipHashTest.cpp b/llvm/unittests/Support/SipHashTest.cpp
new file mode 100644
index 0000000000000..49e4e003f637e
--- /dev/null
+++ b/llvm/unittests/Support/SipHashTest.cpp
@@ -0,0 +1,33 @@
+//===- llvm/unittest/Support/SipHashTest.cpp ------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Support/SipHash.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+
+namespace {
+
+TEST(SipHashTest, PointerAuthSipHash) {
+ // Test some basic cases.
+ EXPECT_EQ(0xE793, getPointerAuthStableSipHash(""));
+ EXPECT_EQ(0xF468, getPointerAuthStableSipHash("strlen"));
+ EXPECT_EQ(0x2D15, getPointerAuthStableSipHash("_ZN1 ind; f"));
+
+ // Test some known strings that are already enshrined in the ABI.
+ EXPECT_EQ(0x6AE1, getPointerAuthStableSipHash("isa"));
+ EXPECT_EQ(0xB5AB, getPointerAuthStableSipHash("objc_class:superclass"));
+ EXPECT_EQ(0xC0BB, getPointerAuthStableSipHash("block_descriptor"));
+ EXPECT_EQ(0xC310, getPointerAuthStableSipHash("method_list_t"));
+
+ // Test limit cases where we differ from naive truncations from 64-bit hashes.
+ EXPECT_EQ(1, getPointerAuthStableSipHash("_Zptrkvttf"));
+ EXPECT_EQ(0xFFFF, getPointerAuthStableSipHash("_Zaflhllod"));
+}
+
+} // end anonymous namespace
More information about the llvm-branch-commits
mailing list