1 // Copyright 2020 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #ifndef PARTITION_ALLOC_PARTITION_ALLOC_INL_H_
6 #define PARTITION_ALLOC_PARTITION_ALLOC_INL_H_
7
8 #include <algorithm>
9 #include <cstring>
10
11 #include "build/build_config.h"
12 #include "partition_alloc/in_slot_metadata.h"
13 #include "partition_alloc/partition_alloc_base/compiler_specific.h"
14 #include "partition_alloc/partition_alloc_base/debug/debugging_buildflags.h"
15 #include "partition_alloc/partition_alloc_config.h"
16 #include "partition_alloc/random.h"
17 #include "partition_alloc/tagging.h"
18 #include "partition_alloc/thread_isolation/thread_isolation.h"
19
20 // Prefetch *x into memory.
21 #if defined(__clang__) || defined(COMPILER_GCC)
22 #define PA_PREFETCH(x) __builtin_prefetch(x)
23 #else
24 #define PA_PREFETCH(x)
25 #endif
26
27 namespace partition_alloc::internal {
28
29 // This is a `memset` that resists being optimized away. Adapted from
30 // boringssl/src/crypto/mem.c. (Copying and pasting is bad, but //base can't
31 // depend on //third_party, and this is small enough.)
32 #if PA_CONFIG(IS_NONCLANG_MSVC)
33 // MSVC only supports inline assembly on x86. This preprocessor directive
34 // is intended to be a replacement for the same.
35 //
36 // TODO(crbug.com/1351310): Make sure inlining doesn't degrade this into
37 // a no-op or similar. The documentation doesn't say.
38 #pragma optimize("", off)
39 #endif
SecureMemset(void * ptr,uint8_t value,size_t size)40 PA_ALWAYS_INLINE void SecureMemset(void* ptr, uint8_t value, size_t size) {
41 memset(ptr, value, size);
42
43 #if !PA_CONFIG(IS_NONCLANG_MSVC)
44 // As best as we can tell, this is sufficient to break any optimisations that
45 // might try to eliminate "superfluous" memsets. If there's an easy way to
46 // detect memset_s, it would be better to use that.
47 __asm__ __volatile__("" : : "r"(ptr) : "memory");
48 #endif // !PA_CONFIG(IS_NONCLANG_MSVC)
49 }
50 #if PA_CONFIG(IS_NONCLANG_MSVC)
51 #pragma optimize("", on)
52 #endif
53
54 #if BUILDFLAG(PA_EXPENSIVE_DCHECKS_ARE_ON)
55 // Used to memset() memory for debugging purposes only.
DebugMemset(void * ptr,int value,size_t size)56 PA_ALWAYS_INLINE void DebugMemset(void* ptr, int value, size_t size) {
57 // Only set the first 512kiB of the allocation. This is enough to detect uses
58 // of uininitialized / freed memory, and makes tests run significantly
59 // faster. Note that for direct-mapped allocations, memory is decomitted at
60 // free() time, so freed memory usage cannot happen.
61
62 #if BUILDFLAG(ENABLE_THREAD_ISOLATION)
63 LiftThreadIsolationScope lift_thread_isolation_restrictions;
64 #endif
65 size_t size_to_memset = std::min(size, size_t{1} << 19);
66 memset(ptr, value, size_to_memset);
67 }
68 #endif // BUILDFLAG(PA_EXPENSIVE_DCHECKS_ARE_ON)
69
70 // Returns true if we've hit the end of a random-length period. We don't want to
71 // invoke `RandomValue` too often, because we call this function in a hot spot
72 // (`Free`), and `RandomValue` incurs the cost of atomics.
73 #if !BUILDFLAG(PA_DCHECK_IS_ON)
RandomPeriod()74 PA_ALWAYS_INLINE bool RandomPeriod() {
75 static thread_local uint8_t counter = 0;
76 if (PA_UNLIKELY(counter == 0)) {
77 // It's OK to truncate this value.
78 counter = static_cast<uint8_t>(RandomValue());
79 }
80 // If `counter` is 0, this will wrap. That is intentional and OK.
81 counter--;
82 return counter == 0;
83 }
84 #endif // !BUILDFLAG(PA_DCHECK_IS_ON)
85
ObjectInnerPtr2Addr(const void * ptr)86 PA_ALWAYS_INLINE uintptr_t ObjectInnerPtr2Addr(const void* ptr) {
87 return UntagPtr(ptr);
88 }
ObjectPtr2Addr(const void * object)89 PA_ALWAYS_INLINE uintptr_t ObjectPtr2Addr(const void* object) {
90 // TODO(bartekn): Check that |object| is indeed an object start.
91 return ObjectInnerPtr2Addr(object);
92 }
SlotStartAddr2Ptr(uintptr_t slot_start)93 PA_ALWAYS_INLINE void* SlotStartAddr2Ptr(uintptr_t slot_start) {
94 // TODO(bartekn): Check that |slot_start| is indeed a slot start.
95 return TagAddr(slot_start);
96 }
SlotStartPtr2Addr(const void * slot_start)97 PA_ALWAYS_INLINE uintptr_t SlotStartPtr2Addr(const void* slot_start) {
98 // TODO(bartekn): Check that |slot_start| is indeed a slot start.
99 return UntagPtr(slot_start);
100 }
101
102 } // namespace partition_alloc::internal
103
104 #endif // PARTITION_ALLOC_PARTITION_ALLOC_INL_H_
105