Skip to content

Commit bf413d6

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). 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 <[email protected]>
1 parent 6679d54 commit bf413d6

File tree

4 files changed

+108
-0
lines changed

4 files changed

+108
-0
lines changed

llvm/include/llvm/Support/SipHash.h

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
//===--- SipHash.h - An ABI-stable string SipHash ---------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// A family of ABI-stable string hash algorithms based on SipHash, currently
10+
// used to compute ptrauth discriminators.
11+
//
12+
//===----------------------------------------------------------------------===//
13+
14+
#ifndef LLVM_SUPPORT_SIPHASH_H
15+
#define LLVM_SUPPORT_SIPHASH_H
16+
17+
#include <cstdint>
18+
19+
namespace llvm {
20+
class StringRef;
21+
22+
/// Compute a stable non-zero 16-bit hash of the given string.
23+
///
24+
/// The exact algorithm is the little-endian interpretation of the
25+
/// non-doubled (i.e. 64-bit) result of applying a SipHash-2-4 using
26+
/// a specific seed value which can be found in the source.
27+
/// This 64-bit result is truncated to a non-zero 16-bit value.
28+
///
29+
/// We use a 16-bit discriminator because ARM64 can efficiently load
30+
/// a 16-bit immediate into the high bits of a register without disturbing
31+
/// the remainder of the value, which serves as a nice blend operation.
32+
/// 16 bits is also sufficiently compact to not inflate a loader relocation.
33+
/// We disallow zero to guarantee a different discriminator from the places
34+
/// in the ABI that use a constant zero.
35+
uint16_t getPointerAuthStableSipHash(StringRef S);
36+
37+
} // end namespace llvm
38+
39+
#endif

llvm/lib/Support/SipHash.cpp

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,23 @@
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

14+
#include "llvm/Support/SipHash.h"
15+
#include "llvm/ADT/StringExtras.h"
16+
#include "llvm/ADT/StringRef.h"
917
#include "llvm/Support/Compiler.h"
18+
#include "llvm/Support/Debug.h"
1019
#include <cstdint>
1120

21+
using namespace llvm;
22+
23+
#define DEBUG_TYPE "llvm-siphash"
24+
1225
// Lightly adapted from the SipHash reference C implementation by
1326
// Jean-Philippe Aumasson and Daniel J. Bernstein.
1427

@@ -133,3 +146,25 @@ static inline ResultTy siphash(const unsigned char *in, uint64_t inlen,
133146

134147
return firstHalf | (ResultTy(secondHalf) << (sizeof(ResultTy) == 8 ? 0 : 64));
135148
}
149+
150+
//===--- LLVM-specific wrapper around siphash.
151+
152+
/// Compute an ABI-stable 16-bit hash of the given string.
153+
uint16_t llvm::getPointerAuthStableSipHash(StringRef Str) {
154+
static const uint8_t K[16] = {0xb5, 0xd4, 0xc9, 0xeb, 0x79, 0x10, 0x4a, 0x79,
155+
0x6f, 0xec, 0x8b, 0x1b, 0x42, 0x87, 0x81, 0xd4};
156+
157+
// The aliasing is fine here because of omnipotent char.
158+
const auto *Data = reinterpret_cast<const uint8_t *>(Str.data());
159+
uint64_t RawHash = siphash<2, 4, uint64_t>(Data, Str.size(), K);
160+
161+
// Produce a non-zero 16-bit discriminator.
162+
uint16_t Discriminator = (RawHash % 0xFFFF) + 1;
163+
LLVM_DEBUG(
164+
dbgs() << "ptrauth stable hash discriminator: " << utostr(Discriminator)
165+
<< " (0x"
166+
<< utohexstr(Discriminator, /*Lowercase=*/false, /*Width=*/4)
167+
<< ")"
168+
<< " of: " << Str << "\n");
169+
return Discriminator;
170+
}

llvm/unittests/Support/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ add_llvm_unittest(SupportTests
7575
ScopedPrinterTest.cpp
7676
SHA256.cpp
7777
SignalsTest.cpp
78+
SipHashTest.cpp
7879
SourceMgrTest.cpp
7980
SpecialCaseListTest.cpp
8081
SuffixTreeTest.cpp
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
//===- llvm/unittest/Support/SipHashTest.cpp ------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "llvm/Support/SipHash.h"
10+
#include "gtest/gtest.h"
11+
12+
using namespace llvm;
13+
14+
namespace {
15+
16+
TEST(SipHashTest, PointerAuthSipHash) {
17+
// Test some basic cases.
18+
EXPECT_EQ(0xE793, getPointerAuthStableSipHash(""));
19+
EXPECT_EQ(0xF468, getPointerAuthStableSipHash("strlen"));
20+
EXPECT_EQ(0x2D15, getPointerAuthStableSipHash("_ZN1 ind; f"));
21+
22+
// Test some known strings that are already enshrined in the ABI.
23+
EXPECT_EQ(0x6AE1, getPointerAuthStableSipHash("isa"));
24+
EXPECT_EQ(0xB5AB, getPointerAuthStableSipHash("objc_class:superclass"));
25+
EXPECT_EQ(0xC0BB, getPointerAuthStableSipHash("block_descriptor"));
26+
EXPECT_EQ(0xC310, getPointerAuthStableSipHash("method_list_t"));
27+
28+
// Test limit cases where we differ from naive truncations from 64-bit hashes.
29+
EXPECT_EQ(1, getPointerAuthStableSipHash("_Zptrkvttf"));
30+
EXPECT_EQ(0xFFFF, getPointerAuthStableSipHash("_Zaflhllod"));
31+
}
32+
33+
} // end anonymous namespace

0 commit comments

Comments
 (0)