Skip to content

[Support] Add SipHash-based 16-bit ptrauth stable hash. #93902

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions llvm/include/llvm/Support/SipHash.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
// An implementation of SipHash, a hash function optimized for speed on
// short inputs. Based on the SipHash reference implementation.
//
// Also provides one specific wrapper on top of SipHash-2-4-64 to compute
// compute ABI-stable ptrauth discriminators.
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_SUPPORT_SIPHASH_H
Expand All @@ -19,6 +22,7 @@
namespace llvm {

template <typename T> class ArrayRef;
class StringRef;

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

/// 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
30 changes: 30 additions & 0 deletions llvm/lib/Support/SipHash.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,26 @@
// 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/ArrayRef.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/Endian.h"
#include <cstdint>

using namespace llvm;
using namespace support;

#define DEBUG_TYPE "llvm-siphash"

// Lightly adapted from the SipHash reference C implementation:
// https://github.com/veorq/SipHash
// by Jean-Philippe Aumasson and Daniel J. Bernstein
Expand Down Expand Up @@ -153,3 +163,23 @@ void llvm::getSipHash_2_4_128(ArrayRef<uint8_t> In, const uint8_t (&K)[16],
uint8_t (&Out)[16]) {
siphash<2, 4>(In.data(), In.size(), K, Out);
}

/// 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};

uint8_t RawHashBytes[8];
getSipHash_2_4_64(arrayRefFromStringRef(Str), K, RawHashBytes);
uint64_t RawHash = endian::read64le(RawHashBytes);

// 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;
}
20 changes: 19 additions & 1 deletion llvm/unittests/Support/SipHashTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,25 @@ TEST(SipHashTest, SipHash_2_4_128) {
}
}

// Below are the unmodified expected outputs from vectors.h
// Tests for the ptrauth-specific SipHash wrapper.
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"));
}

// Below are the unmodified expected outputs from the reference vectors.h.

const uint8_t ExpectedSipHash64[64][8] = {
{
Expand Down
Loading