aboutsummaryrefslogtreecommitdiffstats
path: root/vespamalloc/src/vespamalloc/malloc/memblockboundscheck.h
blob: 67b98701d8e174d20fa498470904e51c042ca609 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#pragma once

#include <vespamalloc/malloc/common.h>
#include <vespamalloc/util/callstack.h>

namespace vespamalloc {

class MemBlockBoundsCheckBaseTBase : public CommonT<5>
{
public:
    using Stack = StackEntry;
    void * rawPtr()          { return _ptr; }
    void *ptr()              {
        char *p((char*)_ptr);
        return p ? (p+alignment()) : nullptr;
    }
    const void *ptr() const {
        const char *p((const char*)_ptr);
        return p ? (p+alignment()) : nullptr;
    }

    void setThreadId(uint32_t th)         { if (_ptr) { static_cast<uint32_t *>(_ptr)[2] = th; } }
    bool allocated()                const { return (static_cast<uint32_t*>(_ptr)[3] == ALLOC_MAGIC); }
    size_t size()                   const { return static_cast<const uint32_t *>(_ptr)[0]; }
    size_t alignment()              const { return static_cast<const uint32_t *>(_ptr)[1]; }
    uint32_t threadId()             const { return static_cast<uint32_t *>(_ptr)[2]; }
    Stack * callStack()                   { return reinterpret_cast<Stack *>((char *)_ptr + size() + alignment()); }
    const Stack * callStack()       const { return reinterpret_cast<const Stack *>((const char *)_ptr + size() + alignment()); }
    void fillMemory(size_t sz) {
        if (_fillValue != NO_FILL) {
            memset(ptr(), _fillValue, sz);
        }
    }
    static void bigBlockLimit(size_t lim) { _bigBlockLimit = lim; }
    static void dumpFile(FILE * fp)       { _logFile = fp; }
    static void setFill(uint8_t pattern)  { _fillValue = pattern; }
    static bool verifySizeClass(int sc)   { return sc >= 0; }

    template<typename T>
    void readjustAlignment(const T & segment) {
        size_t ptr_class_size = this->classSize(T::adjustedClassSize(segment.sizeClass(_ptr)));
        size_t clamped_class_size = std::min(size_t(0x10000), ptr_class_size);
        size_t bitmask = ~(clamped_class_size - 1);
        size_t tmp = reinterpret_cast<size_t>(_ptr);
        tmp &= bitmask;
        _ptr = reinterpret_cast<void *>(tmp);
    }
    void logBigBlock(size_t exact, size_t adjusted, size_t gross) const __attribute__((noinline));
protected:
    MemBlockBoundsCheckBaseTBase(void * p) : _ptr(p) { }
    void verifyFill() const __attribute__((noinline));

    void setSize(size_t sz) {
        ASSERT_STACKTRACE(sz < 0x100000000ul);
        static_cast<uint32_t *>(_ptr)[0] = sz;
    }
    void setAlignment(size_t alignment) { static_cast<uint32_t *>(_ptr)[1] = alignment; }
    static constexpr size_t preambleOverhead(std::align_val_t alignment) {
        return std::max(preambleOverhead(), size_t(alignment));
    }
    static constexpr size_t preambleOverhead() {
        return 4*sizeof(unsigned);
    }

    enum {
        ALLOC_MAGIC = 0xF1E2D3C4,
        FREE_MAGIC = 0x63242367,
        HEAD_MAGIC3 = 0x5BF29BC7,
        TAIL_MAGIC = 0x1A2B3C4D
    };
    enum { NO_FILL = 0xa8};

    void * _ptr;

    static FILE *_logFile;
    static size_t _bigBlockLimit;
    static uint8_t _fillValue;
};

template <size_t MaxSizeClassMultiAllocC, size_t StackTraceLen>
class MemBlockBoundsCheckBaseT : public MemBlockBoundsCheckBaseTBase
{
public:
    enum {
        MaxSizeClassMultiAlloc = MaxSizeClassMultiAllocC,
        SizeClassSpan = (MaxSizeClassMultiAllocC-5)
    };
    MemBlockBoundsCheckBaseT() : MemBlockBoundsCheckBaseTBase(nullptr) { }
    MemBlockBoundsCheckBaseT(void * p)
        : MemBlockBoundsCheckBaseTBase(p ? static_cast<char *>(p) - preambleOverhead() : nullptr)
    { }
    MemBlockBoundsCheckBaseT(void * p, size_t sz)
        : MemBlockBoundsCheckBaseTBase(p)
    {
        setSize(sz);
        setAlignment(preambleOverhead());
    }
    MemBlockBoundsCheckBaseT(void * p, size_t, bool) : MemBlockBoundsCheckBaseTBase(p) { }
    bool validCommon() const {
        const unsigned *p(reinterpret_cast<const unsigned*>(_ptr));
        return p
            && ((p[3] == ALLOC_MAGIC) || (p[3] == FREE_MAGIC))
            && *(reinterpret_cast<const unsigned *> ((const char*)_ptr + size() + alignment() + StackTraceLen*sizeof(void *))) == TAIL_MAGIC;
    }
    template<typename T>
    static size_t usable_size(void *ptr, const T & segment) {
        MemBlockBoundsCheckBaseT mem(ptr);
        mem.readjustAlignment(segment);
        return mem.size();
    }
    bool validAlloc1() const {
        unsigned *p((unsigned*)_ptr);
        return validCommon() && (p[3] == ALLOC_MAGIC);
    }
    bool validFree1()  const {
        unsigned *p((unsigned*)_ptr);
        if (_fillValue != NO_FILL) {
            verifyFill();
        }
        return validCommon() && (p[3] == FREE_MAGIC);
    }
    void alloc(bool log) {
        unsigned *p((unsigned*)_ptr);
        if (p) {
            p[3] = ALLOC_MAGIC;
            if (StackTraceLen) {
                Stack * cStack = callStack();
                if (log) {
                    Stack::fillStack(cStack, StackTraceLen);
                } else {
                    cStack[0] = Stack();
                }
            }
        }
    }

    void free() __attribute__((noinline)) {
        static_cast<unsigned*>(_ptr)[3] = FREE_MAGIC;
        fillMemory(size());
        setTailMagic();
    }
    void setExact(size_t sz) {
        init(sz, preambleOverhead());
    }
    void setExact(size_t sz, std::align_val_t alignment) {
        init(sz, preambleOverhead(alignment));
    }
    size_t callStackLen()           const {
        const Stack * stack = callStack();
        // Use int to avoid compiler warning about always true.
        for (int i(0); i < (int)StackTraceLen; i++) {
            if (! stack[i].valid()) {
                return i+1;
            }
        }
        return StackTraceLen;
    }
    static constexpr size_t adjustSize(size_t sz)   { return sz + overhead(); }
    static constexpr size_t adjustSize(size_t sz, std::align_val_t alignment)   { return sz + overhead(alignment); }
    static constexpr size_t unAdjustSize(size_t sz) { return sz - overhead(); }
    static void dumpInfo(size_t level) __attribute__((noinline));
    static constexpr size_t getMinSizeForAlignment(size_t align, size_t sz) { return sz + align; }
    void info(FILE * os, unsigned level=0) const __attribute__((noinline));

protected:
    static constexpr size_t postambleOverhead() {
        return sizeof(unsigned) + StackTraceLen*sizeof(void *);
    }
    static constexpr size_t overhead() {
        return preambleOverhead() + postambleOverhead();
    }
    static constexpr size_t overhead(std::align_val_t alignment) {
        return preambleOverhead(alignment) + postambleOverhead();
    }
    void setTailMagic() {
        *(reinterpret_cast<unsigned *> ((char*)_ptr + size() + alignment() + StackTraceLen*sizeof(void *))) = TAIL_MAGIC;
    }
    void init(size_t sz, size_t alignment) {
        if (_ptr) {
            setSize(sz);
            setAlignment(alignment);
            setTailMagic();
        }
    }
};

} // namespace vespamalloc