Coverage Report

Created: 2026-06-13 07:11

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/upx/src/compress/compress_zlib.cpp
Line
Count
Source
1
/* compress_zlib.cpp --
2
3
   This file is part of the UPX executable compressor.
4
5
   Copyright (C) Markus Franz Xaver Johannes Oberhumer
6
   All Rights Reserved.
7
8
   UPX and the UCL library are free software; you can redistribute them
9
   and/or modify them under the terms of the GNU General Public License as
10
   published by the Free Software Foundation; either version 2 of
11
   the License, or (at your option) any later version.
12
13
   This program is distributed in the hope that it will be useful,
14
   but WITHOUT ANY WARRANTY; without even the implied warranty of
15
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
   GNU General Public License for more details.
17
18
   You should have received a copy of the GNU General Public License
19
   along with this program; see the file COPYING.
20
   If not, write to the Free Software Foundation, Inc.,
21
   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22
23
   Markus F.X.J. Oberhumer
24
   <markus@oberhumer.com>
25
 */
26
27
#include "../conf.h"
28
29
446k
void zlib_compress_config_t::reset() noexcept {
30
446k
    mem_clear(this);
31
446k
    mem_level.reset();
32
446k
    window_bits.reset();
33
446k
    strategy.reset();
34
446k
}
35
36
#if WITH_ZLIB
37
#include "compress.h"
38
#include "../util/membuffer.h"
39
// NOLINTBEGIN(clang-analyzer-optin.performance.Padding)
40
#define ZLIB_CONST 1
41
#include <zlib/zlib.h>
42
#include <zlib/deflate.h>
43
// NOLINTEND(clang-analyzer-optin.performance.Padding)
44
45
81.9k
static int convert_errno_from_zlib(int zr) {
46
81.9k
    switch (zr) {
47
0
    case Z_OK:
48
0
        return UPX_E_OK;
49
    // positive values
50
0
    case Z_STREAM_END:
51
0
        return UPX_E_ERROR;
52
0
    case Z_NEED_DICT:
53
0
        return UPX_E_ERROR;
54
    // negative values
55
0
    case Z_ERRNO:
56
0
        return UPX_E_ERROR;
57
0
    case Z_STREAM_ERROR:
58
0
        return UPX_E_ERROR;
59
501
    case Z_DATA_ERROR:
60
501
        return UPX_E_ERROR;
61
0
    case Z_MEM_ERROR:
62
0
        return UPX_E_OUT_OF_MEMORY;
63
40.6k
    case Z_BUF_ERROR:
64
40.6k
        return UPX_E_OUTPUT_OVERRUN;
65
0
    case Z_VERSION_ERROR:
66
0
        return UPX_E_ERROR;
67
40.7k
    case -7: // UPX extra
68
40.7k
        return UPX_E_INPUT_OVERRUN;
69
0
    default:
70
0
        break;
71
81.9k
    }
72
0
    return UPX_E_ERROR;
73
81.9k
}
74
75
/*************************************************************************
76
//
77
**************************************************************************/
78
79
int upx_zlib_compress(const upx_bytep src, unsigned src_len, upx_bytep dst, unsigned *dst_len,
80
                      upx_callback_t *cb_parm, int method, int level,
81
0
                      const upx_compress_config_t *cconf_parm, upx_compress_result_t *cresult) {
82
0
    assert(method == M_DEFLATE);
83
0
    assert(level > 0);
84
0
    assert(cresult != nullptr);
85
0
    UNUSED(cb_parm);
86
0
    int r = UPX_E_ERROR;
87
0
    int zr;
88
0
    const zlib_compress_config_t *const lcconf = cconf_parm ? &cconf_parm->conf_zlib : nullptr;
89
0
    zlib_compress_result_t *const res = &cresult->result_zlib;
90
0
    res->reset();
91
92
0
    if (level == 10)
93
0
        level = 9;
94
95
0
    zlib_compress_config_t::mem_level_t mem_level;
96
0
    zlib_compress_config_t::window_bits_t window_bits;
97
0
    zlib_compress_config_t::strategy_t strategy;
98
    // cconf overrides
99
0
    if (lcconf) {
100
0
        upx::oassign(mem_level, lcconf->mem_level);
101
0
        upx::oassign(window_bits, lcconf->window_bits);
102
0
        upx::oassign(strategy, lcconf->strategy);
103
0
    }
104
105
0
    z_stream s;
106
0
    s.zalloc = (alloc_func) nullptr;
107
0
    s.zfree = (free_func) nullptr;
108
0
    s.next_in = src;
109
0
    s.avail_in = src_len;
110
0
    s.next_out = dst;
111
0
    s.avail_out = *dst_len;
112
0
    s.total_in = s.total_out = 0;
113
114
0
    zr = (int) deflateInit2(&s, level, Z_DEFLATED, 0 - (int) window_bits, mem_level, strategy);
115
0
    if (zr != Z_OK)
116
0
        goto error;
117
0
    assert(s.state->level == level);
118
0
    zr = deflate(&s, Z_FINISH);
119
0
    if (zr != Z_STREAM_END)
120
0
        goto error;
121
0
    zr = deflateEnd(&s);
122
0
    if (zr != Z_OK)
123
0
        goto error;
124
0
    r = UPX_E_OK;
125
0
    goto done;
126
0
error:
127
0
    (void) deflateEnd(&s);
128
0
    r = convert_errno_from_zlib(zr);
129
0
    if (r == UPX_E_OK)
130
0
        r = UPX_E_ERROR;
131
0
done:
132
0
    if (r == UPX_E_OK) {
133
0
        if (s.avail_in != 0 || s.total_in != src_len)
134
0
            r = UPX_E_ERROR;
135
0
    }
136
0
    assert(s.total_in <= src_len);
137
0
    assert(s.total_out <= *dst_len);
138
0
    *dst_len = s.total_out;
139
0
    return r;
140
0
}
141
142
/*************************************************************************
143
//
144
**************************************************************************/
145
146
int upx_zlib_decompress(const upx_bytep src, unsigned src_len, upx_bytep dst, unsigned *dst_len,
147
122k
                        int method, const upx_compress_result_t *cresult) {
148
122k
    assert(method == M_DEFLATE);
149
122k
    UNUSED(method);
150
122k
    UNUSED(cresult);
151
122k
    int r = UPX_E_ERROR;
152
122k
    int zr;
153
154
122k
    z_stream s;
155
122k
    s.zalloc = (alloc_func) nullptr;
156
122k
    s.zfree = (free_func) nullptr;
157
122k
    s.next_in = src;
158
122k
    s.avail_in = src_len;
159
122k
    s.next_out = dst;
160
122k
    s.avail_out = *dst_len;
161
122k
    s.total_in = s.total_out = 0;
162
163
122k
    zr = inflateInit2(&s, -15);
164
122k
    if (zr != Z_OK)
165
0
        goto error;
166
122k
    zr = inflate(&s, Z_FINISH);
167
122k
    if (zr != Z_STREAM_END) {
168
81.9k
        if (zr == Z_BUF_ERROR && s.avail_in == 0)
169
40.7k
            zr = -7; // UPX extra
170
81.9k
        goto error;
171
81.9k
    }
172
40.6k
    zr = inflateEnd(&s);
173
40.6k
    if (zr != Z_OK)
174
0
        goto error;
175
40.6k
    r = UPX_E_OK;
176
40.6k
    goto done;
177
81.9k
error:
178
81.9k
    (void) inflateEnd(&s);
179
81.9k
    r = convert_errno_from_zlib(zr);
180
81.9k
    if (r == UPX_E_OK)
181
0
        r = UPX_E_ERROR;
182
122k
done:
183
122k
    if (r == UPX_E_OK) {
184
40.6k
        if (s.avail_in != 0 || s.total_in != src_len)
185
21
            r = UPX_E_INPUT_NOT_CONSUMED;
186
40.6k
    }
187
122k
    assert(s.total_in <= src_len);
188
122k
    assert(s.total_out <= *dst_len);
189
0
    *dst_len = s.total_out;
190
122k
    return r;
191
81.9k
}
192
193
/*************************************************************************
194
// test_overlap - see <ucl/ucl.h> for semantics
195
**************************************************************************/
196
197
int upx_zlib_test_overlap(const upx_bytep buf, const upx_bytep tbuf, unsigned src_off,
198
                          unsigned src_len, unsigned *dst_len, int method,
199
0
                          const upx_compress_result_t *cresult) {
200
0
    assert(method == M_DEFLATE);
201
202
0
    MemBuffer b(src_off + src_len);
203
0
    memcpy(b + src_off, buf + src_off, src_len);
204
0
    unsigned saved_dst_len = *dst_len;
205
0
    int r = upx_zlib_decompress(raw_index_bytes(b, src_off, src_len), src_len,
206
0
                                raw_bytes(b, *dst_len), dst_len, method, cresult);
207
0
    if (r != UPX_E_OK)
208
0
        return r;
209
0
    if (*dst_len != saved_dst_len)
210
0
        return UPX_E_ERROR;
211
    // NOTE: there is a very tiny possibility that decompression has
212
    //   succeeded but the data is not restored correctly because of
213
    //   in-place buffer overlapping, so we use an extra memcmp().
214
0
    if (tbuf != nullptr && memcmp(tbuf, b, *dst_len) != 0)
215
0
        return UPX_E_ERROR;
216
0
    return UPX_E_OK;
217
0
}
218
219
/*************************************************************************
220
// misc
221
**************************************************************************/
222
223
40.6k
int upx_zlib_init(void) {
224
40.6k
    if (strcmp(ZLIB_VERSION, zlibVersion()) != 0)
225
0
        return -2;
226
40.6k
    return 0;
227
40.6k
}
228
229
0
const char *upx_zlib_version_string(void) { return zlibVersion(); }
230
231
#if 0 // UNUSED
232
unsigned upx_zlib_adler32(const void *buf, unsigned len, unsigned adler) {
233
    return adler32(adler, (const Bytef *) buf, len);
234
}
235
#endif
236
237
#if 0 // UNUSED
238
unsigned upx_zlib_crc32(const void *buf, unsigned len, unsigned crc) {
239
    return crc32(crc, (const Bytef *) buf, len);
240
}
241
#endif
242
243
/*************************************************************************
244
// doctest checks
245
**************************************************************************/
246
247
#if DEBUG && !defined(DOCTEST_CONFIG_DISABLE) && 1
248
249
static bool check_zlib(const int method, const int level, const unsigned expected_c_len) {
250
    const unsigned u_len = 16384;
251
    const unsigned c_extra = 4096;
252
    MemBuffer u_buf, c_buf, d_buf;
253
    unsigned c_len, d_len;
254
    upx_compress_result_t cresult;
255
    int r;
256
257
    u_buf.alloc(u_len);
258
    memset(u_buf, 0, u_len);
259
    c_buf.allocForCompression(u_len, c_extra);
260
    d_buf.allocForDecompression(u_len);
261
262
    c_len = c_buf.getSize() - c_extra;
263
    r = upx_zlib_compress(raw_bytes(u_buf, u_len), u_len, raw_index_bytes(c_buf, c_extra, c_len),
264
                          &c_len, nullptr, method, level, NULL_cconf, &cresult);
265
    if (r != 0 || c_len != expected_c_len)
266
        return false;
267
268
    d_len = d_buf.getSize();
269
    r = upx_zlib_decompress(raw_index_bytes(c_buf, c_extra, c_len), c_len, raw_bytes(d_buf, d_len),
270
                            &d_len, method, nullptr);
271
    if (r != 0 || d_len != u_len || memcmp(u_buf, d_buf, u_len) != 0)
272
        return false;
273
274
    d_len = u_len - 1;
275
    r = upx_zlib_decompress(raw_index_bytes(c_buf, c_extra, c_len), c_len, raw_bytes(d_buf, d_len),
276
                            &d_len, method, nullptr);
277
    if (r == 0)
278
        return false;
279
280
    // TODO: rewrite Packer::findOverlapOverhead() so that we can test it here
281
    // unsigned x_len = d_len;
282
    // r = upx_zlib_test_overlap(c_buf, u_buf, c_extra, c_len, &x_len, method, nullptr);
283
    return true;
284
}
285
286
TEST_CASE("compress_zlib") {
287
    CHECK(check_zlib(M_DEFLATE, 1, 89));
288
    CHECK(check_zlib(M_DEFLATE, 3, 89));
289
    CHECK(check_zlib(M_DEFLATE, 5, 33));
290
}
291
292
#endif // DEBUG
293
294
40.6k
TEST_CASE("upx_zlib_decompress") {
295
40.6k
    const byte *c_data;
296
40.6k
    byte d_buf[16];
297
40.6k
    unsigned d_len;
298
40.6k
    int r;
299
300
40.6k
    c_data = (const byte *) "\xfb\xff\x1f\x15\x00\x00";
301
40.6k
    d_len = 16;
302
40.6k
    r = upx_zlib_decompress(c_data, 6, d_buf, &d_len, M_DEFLATE, nullptr);
303
40.6k
    CHECK((r == 0 && d_len == 16));
304
40.6k
    r = upx_zlib_decompress(c_data, 5, d_buf, &d_len, M_DEFLATE, nullptr);
305
40.6k
    CHECK(r == UPX_E_INPUT_OVERRUN);
306
40.6k
    d_len = 15;
307
40.6k
    r = upx_zlib_decompress(c_data, 6, d_buf, &d_len, M_DEFLATE, nullptr);
308
40.6k
    CHECK(r == UPX_E_OUTPUT_OVERRUN);
309
40.6k
    UNUSED(r);
310
40.6k
}
311
312
#endif // WITH_ZLIB
313
314
/* vim:set ts=4 sw=4 et: */