[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
Fri Jun 14 16:53:12 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