Skip to content

Commit b0a19c3

Browse files
[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). 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 <[email protected]>
1 parent 577c3f1 commit b0a19c3

File tree

3 files changed

+68
-1
lines changed

3 files changed

+68
-1
lines changed

llvm/include/llvm/Support/SipHash.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
// An implementation of SipHash, a hash function optimized for speed on
1010
// short inputs. Based on the SipHash reference implementation.
1111
//
12+
// Also provides one specific wrapper on top of SipHash-2-4-64 to compute
13+
// compute ABI-stable ptrauth discriminators.
14+
//
1215
//===----------------------------------------------------------------------===//
1316

1417
#ifndef LLVM_SUPPORT_SIPHASH_H
@@ -19,6 +22,7 @@
1922
namespace llvm {
2023

2124
template <typename T> class ArrayRef;
25+
class StringRef;
2226

2327
/// Computes a SipHash-2-4 64-bit result.
2428
void getSipHash_2_4_64(ArrayRef<uint8_t> In, const uint8_t (&K)[16],
@@ -28,6 +32,21 @@ void getSipHash_2_4_64(ArrayRef<uint8_t> In, const uint8_t (&K)[16],
2832
void getSipHash_2_4_128(ArrayRef<uint8_t> In, const uint8_t (&K)[16],
2933
uint8_t (&Out)[16]);
3034

35+
/// Compute a stable non-zero 16-bit hash of the given string.
36+
///
37+
/// The exact algorithm is the little-endian interpretation of the
38+
/// non-doubled (i.e. 64-bit) result of applying a SipHash-2-4 using
39+
/// a specific seed value which can be found in the source.
40+
/// This 64-bit result is truncated to a non-zero 16-bit value.
41+
///
42+
/// We use a 16-bit discriminator because ARM64 can efficiently load
43+
/// a 16-bit immediate into the high bits of a register without disturbing
44+
/// the remainder of the value, which serves as a nice blend operation.
45+
/// 16 bits is also sufficiently compact to not inflate a loader relocation.
46+
/// We disallow zero to guarantee a different discriminator from the places
47+
/// in the ABI that use a constant zero.
48+
uint16_t getPointerAuthStableSipHash(StringRef S);
49+
3150
} // end namespace llvm
3251

3352
#endif

llvm/lib/Support/SipHash.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,26 @@
55
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
66
//
77
//===----------------------------------------------------------------------===//
8+
//
9+
// This file implements an ABI-stable string hash based on SipHash, used to
10+
// compute ptrauth discriminators.
11+
//
12+
//===----------------------------------------------------------------------===//
813

914
#include "llvm/Support/SipHash.h"
1015
#include "llvm/ADT/ArrayRef.h"
16+
#include "llvm/ADT/StringExtras.h"
17+
#include "llvm/ADT/StringRef.h"
1118
#include "llvm/Support/Compiler.h"
19+
#include "llvm/Support/Debug.h"
1220
#include "llvm/Support/Endian.h"
1321
#include <cstdint>
1422

1523
using namespace llvm;
1624
using namespace support;
1725

26+
#define DEBUG_TYPE "llvm-siphash"
27+
1828
// Lightly adapted from the SipHash reference C implementation:
1929
// https://github.com/veorq/SipHash
2030
// by Jean-Philippe Aumasson and Daniel J. Bernstein
@@ -153,3 +163,23 @@ void llvm::getSipHash_2_4_128(ArrayRef<uint8_t> In, const uint8_t (&K)[16],
153163
uint8_t (&Out)[16]) {
154164
siphash<2, 4>(In.data(), In.size(), K, Out);
155165
}
166+
167+
/// Compute an ABI-stable 16-bit hash of the given string.
168+
uint16_t llvm::getPointerAuthStableSipHash(StringRef Str) {
169+
static const uint8_t K[16] = {0xb5, 0xd4, 0xc9, 0xeb, 0x79, 0x10, 0x4a, 0x79,
170+
0x6f, 0xec, 0x8b, 0x1b, 0x42, 0x87, 0x81, 0xd4};
171+
172+
uint8_t RawHashBytes[8];
173+
getSipHash_2_4_64(arrayRefFromStringRef(Str), K, RawHashBytes);
174+
uint64_t RawHash = endian::read64le(RawHashBytes);
175+
176+
// Produce a non-zero 16-bit discriminator.
177+
uint16_t Discriminator = (RawHash % 0xFFFF) + 1;
178+
LLVM_DEBUG(
179+
dbgs() << "ptrauth stable hash discriminator: " << utostr(Discriminator)
180+
<< " (0x"
181+
<< utohexstr(Discriminator, /*Lowercase=*/false, /*Width=*/4)
182+
<< ")"
183+
<< " of: " << Str << "\n");
184+
return Discriminator;
185+
}

llvm/unittests/Support/SipHashTest.cpp

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,25 @@ TEST(SipHashTest, SipHash_2_4_128) {
5050
}
5151
}
5252

53-
// Below are the unmodified expected outputs from vectors.h
53+
// Tests for the ptrauth-specific SipHash wrapper.
54+
TEST(SipHashTest, PointerAuthSipHash) {
55+
// Test some basic cases.
56+
EXPECT_EQ(0xE793, getPointerAuthStableSipHash(""));
57+
EXPECT_EQ(0xF468, getPointerAuthStableSipHash("strlen"));
58+
EXPECT_EQ(0x2D15, getPointerAuthStableSipHash("_ZN1 ind; f"));
59+
60+
// Test some known strings that are already enshrined in the ABI.
61+
EXPECT_EQ(0x6AE1, getPointerAuthStableSipHash("isa"));
62+
EXPECT_EQ(0xB5AB, getPointerAuthStableSipHash("objc_class:superclass"));
63+
EXPECT_EQ(0xC0BB, getPointerAuthStableSipHash("block_descriptor"));
64+
EXPECT_EQ(0xC310, getPointerAuthStableSipHash("method_list_t"));
65+
66+
// Test limit cases where we differ from naive truncations from 64-bit hashes.
67+
EXPECT_EQ(1, getPointerAuthStableSipHash("_Zptrkvttf"));
68+
EXPECT_EQ(0xFFFF, getPointerAuthStableSipHash("_Zaflhllod"));
69+
}
70+
71+
// Below are the unmodified expected outputs from the reference vectors.h.
5472

5573
const uint8_t ExpectedSipHash64[64][8] = {
5674
{

0 commit comments

Comments
 (0)