Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[scudo][standalone] Implement checksumming functions
Summary: This CL implements the checksumming functions. This departs from the current Scudo code in one aspect: the software version is no longer CRC32 but a BSD checksum. This is because the software CRC32 was too impactful in terms of performances, the BSD checksum has no array lookup which is better (and saves 1KB of data). As with the current version, we only flip the CRC compiler flag for a single compilation unit by default, to allow for a library compiled with HW CRC32 to work on a system without HW CRC32. There is some platform & hardware specific code in those files, but since departs from a mere platform split, it felt right to me to have it that way. Reviewers: morehouse, eugenis, vitalybuka, hctim, mcgrathr Reviewed By: morehouse Subscribers: mgorny, delcypher, jfb, jdoerfert, #sanitizers, llvm-commits Tags: #llvm, #sanitizers Differential Revision: https://reviews.llvm.org/D59116 llvm-svn: 355923
- Loading branch information
Kostya Kortchinsky
committed
Mar 12, 2019
1 parent
eec3206
commit 1f066a7
Showing
6 changed files
with
216 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
//===-- checksum.cc ---------------------------------------------*- 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 | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
#include "checksum.h" | ||
#include "atomic_helpers.h" | ||
|
||
#if defined(__x86_64__) || defined(__i386__) | ||
#include <cpuid.h> | ||
#elif defined(__arm__) || defined(__aarch64__) | ||
#if SCUDO_FUCHSIA | ||
#include <zircon/features.h> | ||
#include <zircon/syscalls.h> | ||
#else | ||
#include <sys/auxv.h> | ||
#endif | ||
#endif | ||
|
||
namespace scudo { | ||
|
||
atomic_u8 HashAlgorithm = {BSDChecksum}; | ||
|
||
#if defined(__x86_64__) || defined(__i386__) | ||
// i386 and x86_64 specific code to detect CRC32 hardware support via CPUID. | ||
// CRC32 requires the SSE 4.2 instruction set. | ||
#ifndef bit_SSE4_2 | ||
#define bit_SSE4_2 bit_SSE42 // clang and gcc have different defines. | ||
#endif | ||
|
||
bool hasHardwareCRC32() { | ||
u32 Eax, Ebx = 0, Ecx = 0, Edx = 0; | ||
__get_cpuid(0, &Eax, &Ebx, &Ecx, &Edx); | ||
const bool IsIntel = (Ebx == signature_INTEL_ebx) && | ||
(Edx == signature_INTEL_edx) && | ||
(Ecx == signature_INTEL_ecx); | ||
const bool IsAMD = (Ebx == signature_AMD_ebx) && (Edx == signature_AMD_edx) && | ||
(Ecx == signature_AMD_ecx); | ||
if (!IsIntel && !IsAMD) | ||
return false; | ||
__get_cpuid(1, &Eax, &Ebx, &Ecx, &Edx); | ||
return !!(Ecx & bit_SSE4_2); | ||
} | ||
|
||
#elif defined(__arm__) || defined(__aarch64__) | ||
#ifndef AT_HWCAP | ||
#define AT_HWCAP 16 | ||
#endif | ||
#ifndef HWCAP_CRC32 | ||
#define HWCAP_CRC32 (1U << 7) // HWCAP_CRC32 is missing on older platforms. | ||
#endif | ||
|
||
bool hasHardwareCRC32() { | ||
#if SCUDO_FUCHSIA | ||
u32 HWCap; | ||
const zx_status_t Status = | ||
zx_system_get_features(ZX_FEATURE_KIND_CPU, &HWCap); | ||
if (Status != ZX_OK) | ||
return false; | ||
return !!(HWCap & ZX_ARM64_FEATURE_ISA_CRC32); | ||
#else | ||
return !!(getauxval(AT_HWCAP) & HWCAP_CRC32); | ||
#endif // SCUDO_FUCHSIA | ||
} | ||
#endif // defined(__x86_64__) || defined(__i386__) | ||
|
||
} // namespace scudo |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
//===-- checksum.h ----------------------------------------------*- 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 | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
#ifndef SCUDO_CHECKSUM_H_ | ||
#define SCUDO_CHECKSUM_H_ | ||
|
||
#include "internal_defs.h" | ||
|
||
// Hardware CRC32 is supported at compilation via the following: | ||
// - for i386 & x86_64: -msse4.2 | ||
// - for ARM & AArch64: -march=armv8-a+crc or -mcrc | ||
// An additional check must be performed at runtime as well to make sure the | ||
// emitted instructions are valid on the target host. | ||
|
||
#ifdef __SSE4_2__ | ||
#include <smmintrin.h> | ||
#define CRC32_INTRINSIC FIRST_32_SECOND_64(_mm_crc32_u32, _mm_crc32_u64) | ||
#endif | ||
#ifdef __ARM_FEATURE_CRC32 | ||
#include <arm_acle.h> | ||
#define CRC32_INTRINSIC FIRST_32_SECOND_64(__crc32cw, __crc32cd) | ||
#endif | ||
|
||
namespace scudo { | ||
|
||
enum ChecksumType : u8 { | ||
BSDChecksum = 0, | ||
HardwareCRC32 = 1, | ||
}; | ||
|
||
// BSD checksum, unlike a software CRC32, doesn't use any array lookup. We save | ||
// significantly on memory accesses, as well as 1K of CRC32 table, on platforms | ||
// that do no support hardware CRC32. The checksum itself is 16-bit, which is at | ||
// odds with CRC32, but enough for our needs. | ||
INLINE u16 computeBSDChecksum(u16 Sum, uptr Data) { | ||
for (u8 I = 0; I < sizeof(Data); I++) { | ||
Sum = static_cast<u16>((Sum >> 1) | ((Sum & 1) << 15)); | ||
Sum = static_cast<u16>(Sum + (Data & 0xff)); | ||
Data >>= 8; | ||
} | ||
return Sum; | ||
} | ||
|
||
bool hasHardwareCRC32(); | ||
WEAK u32 computeHardwareCRC32(u32 Crc, uptr Data); | ||
|
||
} // namespace scudo | ||
|
||
#endif // SCUDO_CHECKSUM_H_ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
//===-- crc32_hw.h ----------------------------------------------*- 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 | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
#include "checksum.h" | ||
|
||
namespace scudo { | ||
|
||
#if defined(__SSE4_2__) || defined(__ARM_FEATURE_CRC32) | ||
u32 computeHardwareCRC32(u32 Crc, uptr Data) { | ||
return static_cast<u32>(CRC32_INTRINSIC(Crc, Data)); | ||
} | ||
#endif // defined(__SSE4_2__) || defined(__ARM_FEATURE_CRC32) | ||
|
||
} // namespace scudo |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
//===-- checksum_test.cc ----------------------------------------*- 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 | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
#include "checksum.h" | ||
|
||
#include "gtest/gtest.h" | ||
|
||
#include <string.h> | ||
|
||
scudo::u16 computeSoftwareChecksum(scudo::u32 Seed, scudo::uptr *Array, | ||
scudo::uptr ArraySize) { | ||
scudo::u16 Checksum = static_cast<scudo::u16>(Seed & 0xffff); | ||
for (scudo::uptr I = 0; I < ArraySize; I++) | ||
Checksum = scudo::computeBSDChecksum(Checksum, Array[I]); | ||
return Checksum; | ||
} | ||
|
||
scudo::u16 computeHardwareChecksum(scudo::u32 Seed, scudo::uptr *Array, | ||
scudo::uptr ArraySize) { | ||
scudo::u32 Crc = Seed; | ||
for (scudo::uptr I = 0; I < ArraySize; I++) | ||
Crc = scudo::computeHardwareCRC32(Crc, Array[I]); | ||
return static_cast<scudo::u16>((Crc & 0xffff) ^ (Crc >> 16)); | ||
} | ||
|
||
typedef scudo::u16 (*ComputeChecksum)(scudo::u32, scudo::uptr *, scudo::uptr); | ||
|
||
// This verifies that flipping bits in the data being checksummed produces a | ||
// different checksum. We do not use random data to avoid flakyness. | ||
template <ComputeChecksum F> void verifyChecksumFunctionBitFlip() { | ||
scudo::uptr Array[sizeof(scudo::u64) / sizeof(scudo::uptr)]; | ||
const scudo::uptr ArraySize = ARRAY_SIZE(Array); | ||
memset(Array, 0xaa, sizeof(Array)); | ||
const scudo::u32 Seed = 0x41424343U; | ||
const scudo::u16 Reference = F(Seed, Array, ArraySize); | ||
scudo::u8 IdenticalChecksums = 0; | ||
for (scudo::uptr I = 0; I < ArraySize; I++) { | ||
for (scudo::uptr J = 0; J < SCUDO_WORDSIZE; J++) { | ||
Array[I] ^= 1U << J; | ||
if (F(Seed, Array, ArraySize) == Reference) | ||
IdenticalChecksums++; | ||
Array[I] ^= 1U << J; | ||
} | ||
} | ||
// Allow for a couple of identical checksums over the whole set of flips. | ||
EXPECT_LE(IdenticalChecksums, 2); | ||
} | ||
|
||
TEST(ScudoChecksumTest, ChecksumFunctions) { | ||
verifyChecksumFunctionBitFlip<computeSoftwareChecksum>(); | ||
if (&scudo::computeHardwareCRC32 && scudo::hasHardwareCRC32()) | ||
verifyChecksumFunctionBitFlip<computeHardwareChecksum>(); | ||
} |