Coverage Report

Created: 2025-11-15 06:44

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/upx/src/util/membuffer.h
Line
Count
Source
1
/* membuffer.h --
2
3
   This file is part of the UPX executable compressor.
4
5
   Copyright (C) 1996-2025 Markus Franz Xaver Johannes Oberhumer
6
   Copyright (C) 1996-2025 Laszlo Molnar
7
   All Rights Reserved.
8
9
   UPX and the UCL library are free software; you can redistribute them
10
   and/or modify them under the terms of the GNU General Public License as
11
   published by the Free Software Foundation; either version 2 of
12
   the License, or (at your option) any later version.
13
14
   This program is distributed in the hope that it will be useful,
15
   but WITHOUT ANY WARRANTY; without even the implied warranty of
16
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
   GNU General Public License for more details.
18
19
   You should have received a copy of the GNU General Public License
20
   along with this program; see the file COPYING.
21
   If not, write to the Free Software Foundation, Inc.,
22
   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23
24
   Markus F.X.J. Oberhumer              Laszlo Molnar
25
   <markus@oberhumer.com>               <ezerotven+github@gmail.com>
26
 */
27
28
#pragma once
29
30
// A MemBuffer allocates memory on the heap, and automatically
31
// gets destructed when leaving scope or on exceptions.
32
33
/*************************************************************************
34
// MemBufferBase
35
// provides some base functionality for treating a MemBuffer as a pointer
36
**************************************************************************/
37
38
template <class T>
39
class MemBufferBase {
40
public:
41
    typedef T element_type;
42
    typedef typename std::add_lvalue_reference<T>::type reference;
43
    typedef typename std::add_pointer<T>::type pointer;
44
    typedef unsigned size_type; // limited by UPX_RSIZE_MAX
45
    typedef pointer iterator;
46
    typedef typename std::add_pointer<const T>::type const_iterator;
47
protected:
48
    static const size_t element_size = sizeof(element_type);
49
50
protected:
51
    pointer ptr;
52
    size_type size_in_bytes;
53
54
public:
55
5.03M
    explicit inline MemBufferBase() noexcept : ptr(nullptr), size_in_bytes(0) {}
56
5.03M
    forceinline ~MemBufferBase() noexcept {}
57
58
    // IMPORTANT NOTE: automatic conversion to underlying pointer
59
    // HINT: for fully bound-checked pointer use XSPAN_S from xspan.h
60
80.2k
    operator pointer() const noexcept { return ptr; }
61
62
    // array access
63
4.75M
    reference operator[](ptrdiff_t i) const may_throw {
64
        // NOTE: &array[SIZE] is *not* legal for containers like std::vector and MemBuffer !
65
4.75M
        if very_unlikely (i < 0 || mem_size(element_size, i) + element_size > size_in_bytes)
66
24.6k
            throwCantPack("MemBuffer invalid array index %td (%u bytes)", i, size_in_bytes);
67
4.72M
        return ptr[i];
68
4.75M
    }
69
    // dereference
70
    reference operator*() const DELETED_FUNCTION;
71
    // arrow operator
72
    pointer operator->() const DELETED_FUNCTION;
73
74
98.4k
    iterator begin() const may_throw {
75
98.4k
        if very_unlikely (ptr == nullptr)
76
24.6k
            throwCantPack("MemBuffer begin() unexpected NULL ptr");
77
73.8k
        return ptr;
78
98.4k
    }
79
73.8k
    const_iterator cbegin() const may_throw {
80
73.8k
        if very_unlikely (ptr == nullptr)
81
24.6k
            throwCantPack("MemBuffer cbegin() unexpected NULL ptr");
82
49.2k
        return ptr;
83
73.8k
    }
84
910k
    iterator end() const may_throw {
85
910k
        if very_unlikely (ptr == nullptr)
86
24.6k
            throwCantPack("MemBuffer end() unexpected NULL ptr");
87
885k
        return ptr + size_in_bytes / element_size;
88
910k
    }
89
73.8k
    const_iterator cend() const may_throw {
90
73.8k
        if very_unlikely (ptr == nullptr)
91
24.6k
            throwCantPack("MemBuffer cend() unexpected NULL ptr");
92
49.2k
        return ptr + size_in_bytes / element_size;
93
73.8k
    }
94
95
    // membuffer + n -> pointer
96
    template <class U>
97
    typename std::enable_if<std::is_integral<U>::value, pointer>::type operator+(U n) const
98
1.12M
        may_throw {
99
1.12M
        size_t bytes = mem_size(element_size, n); // check mem_size
100
1.12M
        return raw_bytes(bytes) + n;              // and check bytes
101
1.12M
    }
_ZNK13MemBufferBaseIhEplIjEENSt3__19enable_ifIXsr3std11is_integralIT_EE5valueEPhE4typeES4_
Line
Count
Source
98
99.9k
        may_throw {
99
99.9k
        size_t bytes = mem_size(element_size, n); // check mem_size
100
99.9k
        return raw_bytes(bytes) + n;              // and check bytes
101
99.9k
    }
_ZNK13MemBufferBaseIhEplIiEENSt3__19enable_ifIXsr3std11is_integralIT_EE5valueEPhE4typeES4_
Line
Count
Source
98
114k
        may_throw {
99
114k
        size_t bytes = mem_size(element_size, n); // check mem_size
100
114k
        return raw_bytes(bytes) + n;              // and check bytes
101
114k
    }
_ZNK13MemBufferBaseIhEplIyEENSt3__19enable_ifIXsr3std11is_integralIT_EE5valueEPhE4typeES4_
Line
Count
Source
98
17.2k
        may_throw {
99
17.2k
        size_t bytes = mem_size(element_size, n); // check mem_size
100
17.2k
        return raw_bytes(bytes) + n;              // and check bytes
101
17.2k
    }
_ZNK13MemBufferBaseIhEplIxEENSt3__19enable_ifIXsr3std11is_integralIT_EE5valueEPhE4typeES4_
Line
Count
Source
98
4.57k
        may_throw {
99
4.57k
        size_t bytes = mem_size(element_size, n); // check mem_size
100
4.57k
        return raw_bytes(bytes) + n;              // and check bytes
101
4.57k
    }
_ZNK13MemBufferBaseIhEplImEENSt3__19enable_ifIXsr3std11is_integralIT_EE5valueEPhE4typeES4_
Line
Count
Source
98
885k
        may_throw {
99
885k
        size_t bytes = mem_size(element_size, n); // check mem_size
100
885k
        return raw_bytes(bytes) + n;              // and check bytes
101
885k
    }
102
private:
103
    // membuffer - n -> pointer; not allowed - use raw_bytes() if needed
104
    template <class U>
105
    typename std::enable_if<std::is_integral<U>::value, pointer>::type operator-(U n) const
106
        DELETED_FUNCTION;
107
108
public: // raw access
109
24.6k
    pointer raw_ptr() const noexcept { return ptr; }
110
24.6k
    size_type raw_size_in_bytes() const noexcept { return size_in_bytes; }
111
112
1.86M
    pointer raw_bytes(size_t bytes) const may_throw {
113
1.86M
        if (bytes > 0) {
114
1.76M
            if very_unlikely (ptr == nullptr)
115
24.6k
                throwCantPack("MemBuffer raw_bytes unexpected NULL ptr");
116
1.74M
            if very_unlikely (bytes > size_in_bytes)
117
418k
                throwCantPack("MemBuffer raw_bytes invalid size");
118
1.74M
        }
119
1.41M
        return ptr;
120
1.86M
    }
121
122
private:
123
    // disable taking the address => force passing by reference
124
    // [I'm not too sure about this design decision, but we can always allow it if needed]
125
    UPX_CXX_DISABLE_ADDRESS(MemBufferBase)
126
};
127
128
/*************************************************************************
129
// MemBufferBase global overloads
130
**************************************************************************/
131
132
// global operators
133
#if ALLOW_INT_PLUS_MEMBUFFER
134
// rewrite "n + membuffer" to "membuffer + n" (member function) so that this will get checked above
135
template <class T, class U>
136
inline typename std::enable_if<std::is_integral<U>::value, typename MemBufferBase<T>::pointer>::type
137
52.5k
operator+(U n, const MemBufferBase<T> &mbb) {
138
52.5k
    return mbb + n;
139
52.5k
}
_ZplIhjENSt3__19enable_ifIXsr3std11is_integralIT0_EE5valueEN13MemBufferBaseIT_E7pointerEE4typeES2_RKS5_
Line
Count
Source
137
30.7k
operator+(U n, const MemBufferBase<T> &mbb) {
138
30.7k
    return mbb + n;
139
30.7k
}
_ZplIhyENSt3__19enable_ifIXsr3std11is_integralIT0_EE5valueEN13MemBufferBaseIT_E7pointerEE4typeES2_RKS5_
Line
Count
Source
137
17.2k
operator+(U n, const MemBufferBase<T> &mbb) {
138
17.2k
    return mbb + n;
139
17.2k
}
_ZplIhxENSt3__19enable_ifIXsr3std11is_integralIT0_EE5valueEN13MemBufferBaseIT_E7pointerEE4typeES2_RKS5_
Line
Count
Source
137
4.57k
operator+(U n, const MemBufferBase<T> &mbb) {
138
4.57k
    return mbb + n;
139
4.57k
}
Unexecuted instantiation: _ZplIhmENSt3__19enable_ifIXsr3std11is_integralIT0_EE5valueEN13MemBufferBaseIT_E7pointerEE4typeES2_RKS5_
140
#else
141
// not allowed
142
template <class T, class U>
143
inline typename std::enable_if<std::is_integral<U>::value, typename MemBufferBase<T>::pointer>::type
144
operator+(U n, const MemBufferBase<T> &mbb) DELETED_FUNCTION;
145
#endif
146
147
// raw_bytes overload
148
template <class T>
149
inline typename MemBufferBase<T>::pointer raw_bytes(const MemBufferBase<T> &mbb,
150
147k
                                                    size_t size_in_bytes) {
151
147k
    return mbb.raw_bytes(size_in_bytes);
152
147k
}
153
template <class T>
154
inline typename MemBufferBase<T>::pointer raw_index_bytes(const MemBufferBase<T> &mbb, size_t index,
155
0
                                                          size_t size_in_bytes) {
156
0
    typedef typename MemBufferBase<T>::element_type element_type;
157
0
    return mbb.raw_bytes(mem_size(sizeof(element_type), index, size_in_bytes)) + index;
158
0
}
159
160
// some more global overloads using a checked raw_bytes() call
161
#if __cplusplus >= 201703L
162
#define XSPAN_REQUIRES_CONVERTIBLE_ANY_DIRECTION(A, B, RType)                                      \
163
    std::enable_if_t<std::is_convertible_v<A *, B *> || std::is_convertible_v<B *, A *>, RType>
164
#elif __cplusplus >= 201103L
165
#define XSPAN_REQUIRES_CONVERTIBLE_ANY_DIRECTION(A, B, RType)                                      \
166
    typename std::enable_if<                                                                       \
167
        std::is_convertible<A *, B *>::value || std::is_convertible<B *, A *>::value, RType>::type
168
#else
169
#define XSPAN_REQUIRES_CONVERTIBLE_ANY_DIRECTION(A, B, RType)                                      \
170
    typename std::enable_if<std::is_same<A, B>::value, RType>::type
171
#endif
172
#define C                        MemBufferBase
173
#define XSPAN_FWD_C_IS_MEMBUFFER 1
174
#if WITH_XSPAN >= 1
175
#define D XSPAN_NS(Ptr)
176
#endif
177
#include "xspan_fwd.h"
178
#undef XSPAN_FWD_C_IS_MEMBUFFER
179
#undef C
180
#undef D
181
#undef XSPAN_REQUIRES_CONVERTIBLE_ANY_DIRECTION
182
183
/*************************************************************************
184
// MemBuffer
185
**************************************************************************/
186
187
class MemBuffer final : public MemBufferBase<byte> {
188
public:
189
4.89M
    explicit inline MemBuffer() noexcept : MemBufferBase<byte>() {}
190
    explicit MemBuffer(upx_uint64_t bytes) may_throw;
191
    ~MemBuffer() noexcept;
192
193
    static unsigned getSizeForCompression(unsigned uncompressed_size, unsigned extra = 0) may_throw;
194
    static unsigned getSizeForDecompression(unsigned uncompressed_size, unsigned extra = 0)
195
        may_throw;
196
197
    void alloc(upx_uint64_t bytes) may_throw;
198
    void allocForCompression(unsigned uncompressed_size, unsigned extra = 0) may_throw;
199
    void allocForDecompression(unsigned uncompressed_size, unsigned extra = 0) may_throw;
200
201
    void dealloc() noexcept;
202
    void checkState() const may_throw;
203
204
    // explicit conversion
205
250k
    void *getVoidPtr() noexcept { return (void *) ptr; }
206
14.2k
    const void *getVoidPtr() const noexcept { return (const void *) ptr; }
207
5.53k
    unsigned getSizeInBytes() const noexcept { return size_in_bytes; }
208
251k
    unsigned getSize() const noexcept { return size_in_bytes; } // note: element_size == 1
209
210
    // util
211
    noinline void fill(unsigned off, unsigned len, int value) may_throw;
212
4
    forceinline void clear(unsigned off, unsigned len) may_throw { fill(off, len, 0); }
213
83.3k
    forceinline void clear() may_throw { fill(0, size_in_bytes, 0); }
214
215
    // If the entire range [skip, skip+take) is inside the buffer,
216
    // then return &ptr[skip]; else throwCantPack(sprintf(errfmt, skip, take)).
217
    // This is similar to BoundedPtr, except only checks once.
218
    // skip == offset, take == size_in_bytes
219
98.5k
    forceinline pointer subref(const char *errfmt, size_t skip, size_t take) may_throw {
220
98.5k
        return (pointer) subref_impl(errfmt, skip, take);
221
98.5k
    }
222
223
private:
224
    void *subref_impl(const char *errfmt, size_t skip, size_t take) may_throw;
225
226
    // static debug stats
227
    struct Stats {
228
        upx_std_atomic(upx_uint32_t) global_alloc_counter;
229
        upx_std_atomic(upx_uint32_t) global_dealloc_counter;
230
#if WITH_THREADS
231
        // avoid link errors on some 32-bit platforms: undefined reference to __atomic_fetch_add_8
232
        upx_std_atomic(size_t) global_total_bytes; // stats may overflow on 32-bit systems
233
        upx_std_atomic(size_t) global_total_active_bytes;
234
#else
235
        upx_std_atomic(upx_uint64_t) global_total_bytes;
236
        upx_std_atomic(upx_uint64_t) global_total_active_bytes;
237
#endif
238
    };
239
    static Stats stats;
240
241
#if DEBUG
242
    // debugging aid
243
    struct Debug {
244
        void *last_return_address_alloc;
245
        void *last_return_address_dealloc;
246
        void *last_return_address_fill;
247
        void *last_return_address_subref;
248
        Debug() noexcept { mem_clear(this); }
249
    };
250
    Debug debug;
251
#endif
252
253
    UPX_CXX_DISABLE_COPY_MOVE(MemBuffer)             // disable copy and move
254
    UPX_CXX_DISABLE_NEW_DELETE_NO_VIRTUAL(MemBuffer) // disable dynamic allocation
255
};
256
257
/* vim:set ts=4 sw=4 et: */