Skip to content

Commit 1f066a7

Browse files
author
Kostya Kortchinsky
committed
[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
1 parent eec3206 commit 1f066a7

File tree

6 files changed

+216
-0
lines changed

6 files changed

+216
-0
lines changed

compiler-rt/lib/scudo/standalone/CMakeLists.txt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,27 @@ if(ANDROID)
3434
endif()
3535

3636
set(SCUDO_SOURCES
37+
checksum.cc
38+
crc32_hw.cc
3739
common.cc
3840
fuchsia.cc
3941
linux.cc)
4042

43+
# Enable the SSE 4.2 instruction set for crc32_hw.cc, if available.
44+
if (COMPILER_RT_HAS_MSSE4_2_FLAG)
45+
set_source_files_properties(crc32_hw.cc PROPERTIES COMPILE_FLAGS -msse4.2)
46+
endif()
47+
48+
# Enable the AArch64 CRC32 feature for crc32_hw.cc, if available.
49+
# Note that it is enabled by default starting with armv8.1-a.
50+
if (COMPILER_RT_HAS_MCRC_FLAG)
51+
set_source_files_properties(crc32_hw.cc PROPERTIES COMPILE_FLAGS -mcrc)
52+
endif()
53+
4154
set(SCUDO_HEADERS
4255
atomic_helpers.h
4356
bytemap.h
57+
checksum.h
4458
internal_defs.h
4559
linux.h
4660
list.h
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
//===-- checksum.cc ---------------------------------------------*- 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+
#include "checksum.h"
10+
#include "atomic_helpers.h"
11+
12+
#if defined(__x86_64__) || defined(__i386__)
13+
#include <cpuid.h>
14+
#elif defined(__arm__) || defined(__aarch64__)
15+
#if SCUDO_FUCHSIA
16+
#include <zircon/features.h>
17+
#include <zircon/syscalls.h>
18+
#else
19+
#include <sys/auxv.h>
20+
#endif
21+
#endif
22+
23+
namespace scudo {
24+
25+
atomic_u8 HashAlgorithm = {BSDChecksum};
26+
27+
#if defined(__x86_64__) || defined(__i386__)
28+
// i386 and x86_64 specific code to detect CRC32 hardware support via CPUID.
29+
// CRC32 requires the SSE 4.2 instruction set.
30+
#ifndef bit_SSE4_2
31+
#define bit_SSE4_2 bit_SSE42 // clang and gcc have different defines.
32+
#endif
33+
34+
bool hasHardwareCRC32() {
35+
u32 Eax, Ebx = 0, Ecx = 0, Edx = 0;
36+
__get_cpuid(0, &Eax, &Ebx, &Ecx, &Edx);
37+
const bool IsIntel = (Ebx == signature_INTEL_ebx) &&
38+
(Edx == signature_INTEL_edx) &&
39+
(Ecx == signature_INTEL_ecx);
40+
const bool IsAMD = (Ebx == signature_AMD_ebx) && (Edx == signature_AMD_edx) &&
41+
(Ecx == signature_AMD_ecx);
42+
if (!IsIntel && !IsAMD)
43+
return false;
44+
__get_cpuid(1, &Eax, &Ebx, &Ecx, &Edx);
45+
return !!(Ecx & bit_SSE4_2);
46+
}
47+
48+
#elif defined(__arm__) || defined(__aarch64__)
49+
#ifndef AT_HWCAP
50+
#define AT_HWCAP 16
51+
#endif
52+
#ifndef HWCAP_CRC32
53+
#define HWCAP_CRC32 (1U << 7) // HWCAP_CRC32 is missing on older platforms.
54+
#endif
55+
56+
bool hasHardwareCRC32() {
57+
#if SCUDO_FUCHSIA
58+
u32 HWCap;
59+
const zx_status_t Status =
60+
zx_system_get_features(ZX_FEATURE_KIND_CPU, &HWCap);
61+
if (Status != ZX_OK)
62+
return false;
63+
return !!(HWCap & ZX_ARM64_FEATURE_ISA_CRC32);
64+
#else
65+
return !!(getauxval(AT_HWCAP) & HWCAP_CRC32);
66+
#endif // SCUDO_FUCHSIA
67+
}
68+
#endif // defined(__x86_64__) || defined(__i386__)
69+
70+
} // namespace scudo
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
//===-- checksum.h ----------------------------------------------*- 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+
#ifndef SCUDO_CHECKSUM_H_
10+
#define SCUDO_CHECKSUM_H_
11+
12+
#include "internal_defs.h"
13+
14+
// Hardware CRC32 is supported at compilation via the following:
15+
// - for i386 & x86_64: -msse4.2
16+
// - for ARM & AArch64: -march=armv8-a+crc or -mcrc
17+
// An additional check must be performed at runtime as well to make sure the
18+
// emitted instructions are valid on the target host.
19+
20+
#ifdef __SSE4_2__
21+
#include <smmintrin.h>
22+
#define CRC32_INTRINSIC FIRST_32_SECOND_64(_mm_crc32_u32, _mm_crc32_u64)
23+
#endif
24+
#ifdef __ARM_FEATURE_CRC32
25+
#include <arm_acle.h>
26+
#define CRC32_INTRINSIC FIRST_32_SECOND_64(__crc32cw, __crc32cd)
27+
#endif
28+
29+
namespace scudo {
30+
31+
enum ChecksumType : u8 {
32+
BSDChecksum = 0,
33+
HardwareCRC32 = 1,
34+
};
35+
36+
// BSD checksum, unlike a software CRC32, doesn't use any array lookup. We save
37+
// significantly on memory accesses, as well as 1K of CRC32 table, on platforms
38+
// that do no support hardware CRC32. The checksum itself is 16-bit, which is at
39+
// odds with CRC32, but enough for our needs.
40+
INLINE u16 computeBSDChecksum(u16 Sum, uptr Data) {
41+
for (u8 I = 0; I < sizeof(Data); I++) {
42+
Sum = static_cast<u16>((Sum >> 1) | ((Sum & 1) << 15));
43+
Sum = static_cast<u16>(Sum + (Data & 0xff));
44+
Data >>= 8;
45+
}
46+
return Sum;
47+
}
48+
49+
bool hasHardwareCRC32();
50+
WEAK u32 computeHardwareCRC32(u32 Crc, uptr Data);
51+
52+
} // namespace scudo
53+
54+
#endif // SCUDO_CHECKSUM_H_
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//===-- crc32_hw.h ----------------------------------------------*- 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+
#include "checksum.h"
10+
11+
namespace scudo {
12+
13+
#if defined(__SSE4_2__) || defined(__ARM_FEATURE_CRC32)
14+
u32 computeHardwareCRC32(u32 Crc, uptr Data) {
15+
return static_cast<u32>(CRC32_INTRINSIC(Crc, Data));
16+
}
17+
#endif // defined(__SSE4_2__) || defined(__ARM_FEATURE_CRC32)
18+
19+
} // namespace scudo

compiler-rt/lib/scudo/standalone/tests/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ endmacro()
5151
set(SCUDO_UNIT_TEST_SOURCES
5252
atomic_test.cc
5353
bytemap_test.cc
54+
checksum_test.cc
5455
list_test.cc
5556
map_test.cc
5657
mutex_test.cc
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
//===-- checksum_test.cc ----------------------------------------*- 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+
#include "checksum.h"
10+
11+
#include "gtest/gtest.h"
12+
13+
#include <string.h>
14+
15+
scudo::u16 computeSoftwareChecksum(scudo::u32 Seed, scudo::uptr *Array,
16+
scudo::uptr ArraySize) {
17+
scudo::u16 Checksum = static_cast<scudo::u16>(Seed & 0xffff);
18+
for (scudo::uptr I = 0; I < ArraySize; I++)
19+
Checksum = scudo::computeBSDChecksum(Checksum, Array[I]);
20+
return Checksum;
21+
}
22+
23+
scudo::u16 computeHardwareChecksum(scudo::u32 Seed, scudo::uptr *Array,
24+
scudo::uptr ArraySize) {
25+
scudo::u32 Crc = Seed;
26+
for (scudo::uptr I = 0; I < ArraySize; I++)
27+
Crc = scudo::computeHardwareCRC32(Crc, Array[I]);
28+
return static_cast<scudo::u16>((Crc & 0xffff) ^ (Crc >> 16));
29+
}
30+
31+
typedef scudo::u16 (*ComputeChecksum)(scudo::u32, scudo::uptr *, scudo::uptr);
32+
33+
// This verifies that flipping bits in the data being checksummed produces a
34+
// different checksum. We do not use random data to avoid flakyness.
35+
template <ComputeChecksum F> void verifyChecksumFunctionBitFlip() {
36+
scudo::uptr Array[sizeof(scudo::u64) / sizeof(scudo::uptr)];
37+
const scudo::uptr ArraySize = ARRAY_SIZE(Array);
38+
memset(Array, 0xaa, sizeof(Array));
39+
const scudo::u32 Seed = 0x41424343U;
40+
const scudo::u16 Reference = F(Seed, Array, ArraySize);
41+
scudo::u8 IdenticalChecksums = 0;
42+
for (scudo::uptr I = 0; I < ArraySize; I++) {
43+
for (scudo::uptr J = 0; J < SCUDO_WORDSIZE; J++) {
44+
Array[I] ^= 1U << J;
45+
if (F(Seed, Array, ArraySize) == Reference)
46+
IdenticalChecksums++;
47+
Array[I] ^= 1U << J;
48+
}
49+
}
50+
// Allow for a couple of identical checksums over the whole set of flips.
51+
EXPECT_LE(IdenticalChecksums, 2);
52+
}
53+
54+
TEST(ScudoChecksumTest, ChecksumFunctions) {
55+
verifyChecksumFunctionBitFlip<computeSoftwareChecksum>();
56+
if (&scudo::computeHardwareCRC32 && scudo::hasHardwareCRC32())
57+
verifyChecksumFunctionBitFlip<computeHardwareChecksum>();
58+
}

0 commit comments

Comments
 (0)