Coverage Report

Created: 2026-06-30 07:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/upx/src/p_unix.cpp
Line
Count
Source
1
/* p_unix.cpp --
2
3
   This file is part of the UPX executable compressor.
4
5
   Copyright (C) Markus Franz Xaver Johannes Oberhumer
6
   Copyright (C) Laszlo Molnar
7
   Copyright (C) John F. Reiser
8
   All Rights Reserved.
9
10
   UPX and the UCL library are free software; you can redistribute them
11
   and/or modify them under the terms of the GNU General Public License as
12
   published by the Free Software Foundation; either version 2 of
13
   the License, or (at your option) any later version.
14
15
   This program is distributed in the hope that it will be useful,
16
   but WITHOUT ANY WARRANTY; without even the implied warranty of
17
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18
   GNU General Public License for more details.
19
20
   You should have received a copy of the GNU General Public License
21
   along with this program; see the file COPYING.
22
   If not, write to the Free Software Foundation, Inc.,
23
   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24
25
   Markus F.X.J. Oberhumer              Laszlo Molnar
26
   <markus@oberhumer.com>               <ezerotven+github@gmail.com>
27
28
   John F. Reiser
29
   <jreiser@users.sourceforge.net>
30
 */
31
32
33
#include "conf.h"
34
35
#include "file.h"
36
#include "filter.h"
37
#include "packer.h"
38
#include "p_unix.h"
39
#include "p_elf.h"
40
41
// do not change
42
0
#define BLOCKSIZE       (512*1024)
43
44
45
/*************************************************************************
46
//
47
**************************************************************************/
48
49
PackUnix::PackUnix(InputFile *f) :
50
533k
    super(f), exetype(0), blocksize(0), overlay_offset(0), lsize(0),
51
533k
    methods_used(0), szb_info(sizeof(b_info))
52
533k
{
53
533k
    COMPILE_TIME_ASSERT(sizeof(Elf32_Ehdr) == 52)
54
533k
    COMPILE_TIME_ASSERT(sizeof(Elf32_Phdr) == 32)
55
533k
    COMPILE_TIME_ASSERT(sizeof(b_info) == 12)
56
533k
    COMPILE_TIME_ASSERT(sizeof(l_info) == 12)
57
533k
    COMPILE_TIME_ASSERT(sizeof(p_info) == 12)
58
59
    // opt->o_unix.android_shlib is global, but must be hint
60
    // that applies only when an actual ET_DYN on EM_ARM (only!).
61
    // User might say "--android-shlib" but give mulitple files
62
    // where some are ET_EXEC.
63
533k
    saved_opt_android_shlib = opt->o_unix.android_shlib;
64
533k
    opt->o_unix.android_shlib = 0;  // Must apply selectively
65
    // Besides, cannot figure out why asl_slide_Shdrs does not work.
66
533k
}
67
68
PackUnix::~PackUnix() noexcept
69
533k
{
70
533k
    opt->o_unix.android_shlib = saved_opt_android_shlib;
71
533k
}
72
73
// common part of canPack(), enhanced by subclasses
74
tribool PackUnix::canPack()
75
0
{
76
0
    if (exetype == 0)
77
0
        return false;
78
79
0
#if defined(__unix__)
80
    // must be executable by owner
81
0
    if ((fi->st.st_mode & S_IXUSR) == 0)
82
0
        throwCantPack("file not executable; try 'chmod +x'");
83
0
#endif
84
0
    if (file_size < 4096)
85
0
        throwCantPack("file is too small");
86
87
    // info: currently the header is 36 (32+4) bytes before EOF
88
0
    unsigned char buf[256];
89
0
    fi->seek(-(off_t)sizeof(buf), SEEK_END);
90
0
    fi->readx(buf, sizeof(buf));
91
0
    checkAlreadyPacked(buf, sizeof(buf));
92
93
0
    return true;
94
0
}
95
96
97
void PackUnix::writePackHeader(OutputFile *fo)
98
0
{
99
0
    unsigned char buf[32];
100
0
    memset(buf, 0, sizeof(buf));
101
102
0
    const int hsize = ph.getPackHeaderSize();
103
0
    assert((unsigned)hsize <= sizeof(buf));
104
105
    // note: magic constants are always le32
106
0
    set_le32(buf+0, UPX_MAGIC_LE32);
107
0
    set_le32(buf+4, UPX_MAGIC2_LE32);
108
109
0
    checkPatch(nullptr, 0, 0, 0);  // reset
110
0
    patchPackHeader(buf, hsize);
111
0
    checkPatch(nullptr, 0, 0, 0);  // reset
112
113
0
    fo->write(buf, hsize);
114
0
}
115
116
117
/*************************************************************************
118
// Generic Unix pack(). Subclasses must provide patchLoader().
119
//
120
// A typical compressed Unix executable looks like this:
121
//   - loader stub
122
//   - 12 bytes header info
123
//   - the compressed blocks, each with a 8 byte header for block sizes
124
//   - 4 bytes block end marker (uncompressed size 0)
125
//   - 32 bytes UPX packheader
126
//   - 4 bytes overlay offset (needed for decompression)
127
**************************************************************************/
128
129
// see note below and Packer::compress()
130
bool PackUnix::checkCompressionRatio(unsigned, unsigned) const
131
0
{
132
0
    return true;
133
0
}
134
135
void PackUnix::pack1(OutputFile * /*fo*/, Filter & /*ft*/)
136
0
{
137
    // derived class usually provides this
138
0
}
139
140
int PackUnix::getStrategy(Filter &/*ft*/)
141
0
{
142
    // Called just before reading and compressing each block.
143
    // Might want to adjust blocksize, etc.
144
145
    // If user specified the filter, then use it (-2==filter_strategy).
146
    // Else try the first two filters, and pick the better (2==filter_strategy).
147
0
    return (opt->no_filter ? -3 : ((opt->filter > 0) ? -2 : 2));
148
0
}
149
150
int PackUnix::pack2(OutputFile *fo, Filter &ft)
151
0
{
152
    // compress blocks
153
0
    total_in = 0;
154
0
    total_out = 0;
155
156
// FIXME: ui_total_passes is not correct with multiple blocks...
157
//    ui_total_passes = (file_size + blocksize - 1) / blocksize;
158
//    if (ui_total_passes == 1)
159
//        ui_total_passes = 0;
160
161
0
    unsigned remaining = file_size;
162
0
    unsigned n_block = 0;
163
0
    while (remaining > 0)
164
0
    {
165
        // FIXME: disable filters if we have more than one block.
166
        // FIXME: There is only 1 un-filter in the stub [as of 2002-11-10].
167
        // So the next block really has no choice!
168
        // This merely prevents an assert() in compressWithFilters(),
169
        // which assumes it has free choice on each call [block].
170
        // And if the choices aren't the same on each block,
171
        // then un-filtering will give incorrect results.
172
0
        int filter_strategy = getStrategy(ft);
173
0
        if (file_size > (off_t)blocksize)
174
0
            filter_strategy = -3;      // no filters
175
176
0
        int l = fi->readx(ibuf, UPX_MIN(blocksize, remaining));
177
0
        remaining -= l;
178
179
        // Note: compression for a block can fail if the
180
        //       file is e.g. blocksize + 1 bytes long
181
182
        // compress
183
0
        ph.overlap_overhead = 0;
184
0
        ph.c_len = ph.u_len = l;
185
0
        ft.buf_len = l;
186
187
        // compressWithFilters() updates u_adler _inside_ compress();
188
        // that is, AFTER filtering.  We want BEFORE filtering,
189
        // so that decompression checks the end-to-end checksum.
190
0
        unsigned const end_u_adler = upx_adler32(ibuf, ph.u_len, ph.u_adler);
191
0
        compressWithFilters(&ft, OVERHEAD, NULL_cconf, filter_strategy,
192
0
            !!n_block++);  // check compression ratio only on first block
193
194
0
        if (ph.c_len < ph.u_len) {
195
0
            const upx_bytep tbuf = nullptr;
196
0
            if (ft.id == 0) tbuf = ibuf;
197
0
            ph.overlap_overhead = OVERHEAD;
198
0
            if (!testOverlappingDecompression(obuf, tbuf, ph.overlap_overhead)) {
199
                // not in-place compressible
200
0
                ph.c_len = ph.u_len;
201
0
            }
202
0
        }
203
0
        if (ph.c_len >= ph.u_len) {
204
            // block is not compressible
205
0
            ph.c_len = ph.u_len;
206
            // must manually update checksum of compressed data
207
0
            ph.c_adler = upx_adler32(ibuf, ph.u_len, ph.saved_c_adler);
208
0
        }
209
210
        // write block header
211
0
        b_info blk_info;
212
0
        memset(&blk_info, 0, sizeof(blk_info));
213
0
        set_te32(&blk_info.sz_unc, ph.u_len);
214
0
        set_te32(&blk_info.sz_cpr, ph.c_len);
215
0
        if (ph.c_len < ph.u_len) {
216
0
            blk_info.b_method = (unsigned char) ph.method;
217
0
            blk_info.b_ftid = (unsigned char) ph.filter;
218
0
            blk_info.b_cto8 = (unsigned char) ph.filter_cto;
219
0
        }
220
0
        fo->write(&blk_info, sizeof(blk_info));
221
0
        b_len += sizeof(b_info);
222
223
        // write compressed data
224
0
        if (ph.c_len < ph.u_len) {
225
0
            fo->write(obuf, ph.c_len);
226
0
            verifyOverlappingDecompression();  // uses ph.u_adler
227
0
        }
228
0
        else {
229
0
            fo->write(ibuf, ph.u_len);
230
0
        }
231
0
        ph.u_adler = end_u_adler;
232
233
0
        total_in += ph.u_len;
234
0
        total_out += ph.c_len;
235
0
    }
236
237
    // update header with totals
238
0
    ph.u_len = total_in;
239
0
    ph.c_len = total_out;
240
241
0
    if ((off_t)total_in != file_size) {
242
0
        throwEOFException();
243
0
    }
244
245
0
    return 1;  // default: write end-of-compression bhdr next
246
0
}
247
248
void
249
PackUnix::patchLoaderChecksum()
250
0
{
251
0
    unsigned char *const ptr = getLoader();
252
0
    l_info *const lp = &linfo;
253
    // checksum for loader; also some PackHeader info
254
0
    lp->l_magic = UPX_MAGIC_LE32;  // LE32 always
255
0
    set_te16(&lp->l_lsize, (upx_uint16_t) lsize);
256
0
    lp->l_version = (unsigned char) ph.version;
257
0
    lp->l_format  = (unsigned char) ph.format;
258
    // INFO: lp->l_checksum is currently unused
259
0
    set_te32(&lp->l_checksum, upx_adler32(ptr, lsize));
260
0
}
261
262
off_t PackUnix::pack3(OutputFile *fo, Filter &ft)
263
0
{
264
0
    if (nullptr==linker) {
265
        // If no filter, then linker is not constructed by side effect
266
        // of packExtent calling compressWithFilters.
267
        // This is typical after "/usr/bin/patchelf --set-rpath".
268
0
        buildLoader(&ft);
269
0
    }
270
0
    upx_byte *p = getLoader();
271
0
    lsize = getLoaderSize();
272
0
    updateLoader(fo);
273
0
    patchLoaderChecksum();
274
0
    fo->write(p, lsize);
275
0
    return fo->getBytesWritten();
276
0
}
277
278
void PackUnix::pack4(OutputFile *fo, Filter &)
279
0
{
280
0
    writePackHeader(fo);
281
282
0
    unsigned tmp;
283
0
    set_te32(&tmp, overlay_offset);
284
0
    fo->write(&tmp, sizeof(tmp));
285
0
}
286
287
void PackUnix::pack(OutputFile *fo)
288
0
{
289
0
    Filter ft(ph.level);
290
0
    ft.addvalue = 0;
291
0
    b_len = 0;
292
0
    progid = 0;
293
294
    // set options
295
0
    blocksize = opt->o_unix.blocksize;
296
0
    if (blocksize <= 0)
297
0
        blocksize = BLOCKSIZE;
298
0
    if ((off_t)blocksize > file_size)
299
0
        blocksize = file_size;
300
301
    // init compression buffers
302
0
    ibuf.alloc(blocksize);
303
0
    obuf.allocForCompression(blocksize);
304
305
0
    fi->seek(0, SEEK_SET);
306
0
    pack1(fo, ft);  // generate Elf header, etc.
307
308
    // Shlib probably did not generate Elf header yet.
309
0
    if (fo->st_size()) { // Only append if pack1 actually wrote something.
310
0
        p_info hbuf;
311
0
        set_te32(&hbuf.p_progid, progid);
312
0
        set_te32(&hbuf.p_filesize, file_size);
313
0
        set_te32(&hbuf.p_blocksize, blocksize);
314
0
        fo->write(&hbuf, sizeof(hbuf));
315
0
    }
316
317
    // append the compressed body
318
0
    if (pack2(fo, ft)) {
319
        // write block end marker (uncompressed size 0)
320
0
        b_info hdr; memset(&hdr, 0, sizeof(hdr));
321
0
        set_le32(&hdr.sz_cpr, UPX_MAGIC_LE32);
322
0
        fo->write(&hdr, sizeof(hdr));
323
0
    }
324
325
0
    pack3(fo, ft);  // append loader
326
327
0
    pack4(fo, ft);  // append PackHeader and overlay_offset; update Elf header
328
329
    // finally check the compression ratio
330
0
    if (!checkFinalCompressionRatio(fo))
331
0
        throwNotCompressible();
332
0
}
333
334
335
void PackUnix::packExtent(
336
    const Extent &x,
337
    Filter *ft,
338
    OutputFile *fo,
339
    unsigned hdr_u_len,
340
    unsigned b_extra,
341
    bool inhibit_compression_check
342
)
343
0
{
344
0
    MemBuffer hdr_ibuf;
345
0
    if (hdr_u_len) {
346
0
        hdr_ibuf.alloc(hdr_u_len);
347
0
        fi->seek(0, SEEK_SET);
348
0
        int l = fi->readx(hdr_ibuf, hdr_u_len);
349
0
        (void)l;
350
0
    }
351
0
    fi->seek(x.offset, SEEK_SET);
352
0
    for (off_t rest = x.size; 0 != rest; ) {
353
0
        unsigned const init_u_adler = ph.u_adler;
354
0
        unsigned const init_c_adler = ph.c_adler;
355
0
        int const filter_strategy = ft ? getStrategy(*ft) : 0;
356
0
        int l = fi->readx(ibuf, UPX_MIN(rest, (off_t)blocksize));
357
0
        if (l == 0) {
358
0
            break;
359
0
        }
360
0
        rest -= l;
361
362
        // Note: compression for a block can fail if the
363
        //       file is e.g. blocksize + 1 bytes long
364
365
        // compress
366
0
        ph.c_len = ph.u_len = l;
367
0
        ph.overlap_overhead = 0;
368
0
        unsigned end_u_adler = 0;
369
0
        if (ft) {
370
            // compressWithFilters() updates u_adler _inside_ compress();
371
            // that is, AFTER filtering.  We want BEFORE filtering,
372
            // so that decompression checks the end-to-end checksum.
373
0
            end_u_adler = upx_adler32(ibuf, ph.u_len, ph.u_adler);
374
0
            ft->buf_len = l;
375
376
                // compressWithFilters() requirements?
377
0
            ph.filter = 0;
378
0
            ph.filter_cto = 0;
379
0
            ft->id = 0;
380
0
            ft->cto = 0;
381
382
0
            compressWithFilters(ft, OVERHEAD, NULL_cconf, filter_strategy,
383
0
                                0, 0, 0, hdr_ibuf, hdr_u_len, inhibit_compression_check);
384
0
        }
385
0
        else {
386
0
            (void) compress(ibuf, ph.u_len, obuf);    // ignore return value
387
0
        }
388
389
0
        if (ph.c_len < ph.u_len) {
390
0
            const upx_bytep tbuf = nullptr;
391
0
            if (ft == nullptr || ft->id == 0) tbuf = ibuf;
392
0
            ph.overlap_overhead = OVERHEAD;
393
0
            if (!inhibit_compression_check
394
0
            &&  !testOverlappingDecompression(obuf, tbuf, ph.overlap_overhead)) {
395
                // not in-place de-compressible, so force a not-compressed block
396
0
                ph.c_len = ph.u_len;
397
0
            }
398
0
        }
399
0
        if (ph.c_len >= ph.u_len) {
400
            // block is not compressible, or not in-place de-compressible
401
0
            ph.c_len = ph.u_len;
402
0
            memcpy(obuf, ibuf, ph.c_len);
403
            // must update checksum of "compressed" data
404
            // Input to adler32 is value before the failure.
405
0
            ph.c_adler = upx_adler32(ibuf, ph.u_len, init_c_adler);
406
0
        }
407
408
        // write block sizes
409
0
        b_info tmp;
410
0
        if (hdr_u_len) {
411
0
            unsigned hdr_c_len = 0;
412
0
            MemBuffer hdr_obuf;
413
0
            hdr_obuf.allocForCompression(hdr_u_len);
414
0
            int r = upx_compress(hdr_ibuf, hdr_u_len, hdr_obuf, &hdr_c_len,
415
0
                /* &progress callback */ nullptr,
416
0
                ph_forced_method(ph.method), 10,
417
0
                /* &config_t */ nullptr, /* &result_t */ nullptr);
418
0
            if (r != UPX_E_OK)
419
0
                throwInternalError("header compression failed");
420
0
            if (hdr_c_len >= hdr_u_len)
421
0
                throwInternalError("header compression size increase");
422
0
            ph.saved_u_adler = upx_adler32(hdr_ibuf, hdr_u_len, init_u_adler);
423
0
            ph.saved_c_adler = upx_adler32(hdr_obuf, hdr_c_len, init_c_adler);
424
0
            ph.u_adler = upx_adler32(ibuf, ph.u_len, ph.saved_u_adler);
425
0
            ph.c_adler = upx_adler32(obuf, ph.c_len, ph.saved_c_adler);
426
0
            end_u_adler = ph.u_adler;
427
0
            memset(&tmp, 0, sizeof(tmp));
428
0
            set_te32(&tmp.sz_unc, hdr_u_len);
429
0
            set_te32(&tmp.sz_cpr, hdr_c_len);
430
0
            tmp.b_method = (unsigned char) ph_forced_method(ph.method);
431
0
            tmp.b_extra = b_extra;
432
0
            fo->write(&tmp, sizeof(tmp));
433
0
            total_out += sizeof(tmp);
434
0
            b_len += sizeof(b_info);
435
0
            fo->write(hdr_obuf, hdr_c_len);
436
0
            total_out += hdr_c_len;
437
0
            total_in  += hdr_u_len;
438
0
            hdr_u_len = 0;  // compress hdr one time only
439
0
        }
440
0
        memset(&tmp, 0, sizeof(tmp));
441
0
        set_te32(&tmp.sz_unc, ph.u_len);
442
0
        set_te32(&tmp.sz_cpr, ph.c_len);
443
0
        if (ph.c_len < ph.u_len) {
444
0
            tmp.b_method = (unsigned char) ph.method;
445
0
            if (ft) {
446
0
                tmp.b_ftid = (unsigned char) ft->id;
447
0
                tmp.b_cto8 = ft->cto;
448
0
            }
449
0
        }
450
0
        tmp.b_extra = b_extra;
451
0
        fo->write(&tmp, sizeof(tmp));
452
0
        total_out += sizeof(tmp);
453
0
        b_len += sizeof(b_info);
454
455
0
        if (ft) {
456
0
            ph.u_adler = end_u_adler;
457
0
        }
458
        // write compressed data
459
0
        if (ph.c_len < ph.u_len) {
460
0
            fo->write(obuf, ph.c_len);
461
0
            total_out += ph.c_len;
462
            // Checks ph.u_adler after decompression, after unfiltering
463
0
            if (!inhibit_compression_check)
464
0
                verifyOverlappingDecompression(ft);
465
0
        }
466
0
        else {
467
0
            fo->write(ibuf, ph.u_len);
468
0
            total_out += ph.u_len;
469
0
        }
470
471
0
        total_in += ph.u_len;
472
0
    }
473
0
}
474
475
// Consumes b_info header block and sz_cpr data block from input file 'fi'.
476
// De-compresses; appends to output file 'fo' unless rewrite or peeking.
477
// For "peeking" without writing: set (fo = nullptr), (is_rewrite = -1)
478
// Return actual length when peeking; else 0.
479
unsigned PackUnix::unpackExtent(unsigned wanted, OutputFile *fo,
480
    unsigned &c_adler, unsigned &u_adler,
481
    bool first_PF_X,
482
    int is_rewrite // 0(false): write; 1(true): rewrite; -1: no write
483
)
484
2.82k
{
485
2.82k
    b_info hdr; memset(&hdr, 0, sizeof(hdr));
486
2.82k
    unsigned inlen = 0; // output index (if-and-only-if peeking)
487
33.1k
    while (wanted) {
488
31.5k
        fi->readx(&hdr, szb_info);
489
31.5k
        int const sz_unc = ph.u_len = get_te32(&hdr.sz_unc);
490
31.5k
        int const sz_cpr = ph.c_len = get_te32(&hdr.sz_cpr);
491
31.5k
        ph.filter_cto = hdr.b_cto8;
492
493
31.5k
        if (sz_unc == 0 || M_LZMA < hdr.b_method) {
494
314
            throwCantUnpack("corrupt b_info");
495
0
            break;
496
314
        }
497
31.1k
        if (sz_unc <= 0 || sz_cpr <= 0)
498
168
            throwCantUnpack("corrupt b_info");
499
31.0k
        if (sz_cpr > sz_unc || sz_unc > (int)blocksize)
500
194
            throwCantUnpack("corrupt b_info");
501
502
        // place the input for overlapping de-compression
503
30.8k
        int j = inlen + sz_unc + OVERHEAD + (sz_unc >> ELF_NRV_FUDGE) - sz_cpr;
504
30.8k
        if (ibuf.getSize() < (unsigned)(j + sz_cpr)) {
505
0
            throwCantUnpack("corrupt b_info");
506
0
        }
507
30.8k
        fi->readx(ibuf+j, sz_cpr);
508
30.8k
        total_in += sz_cpr;
509
        // update checksum of compressed data
510
30.8k
        c_adler = upx_adler32(ibuf + j, sz_cpr, c_adler);
511
512
30.8k
        if (sz_cpr < sz_unc) { // block was compressed
513
27.6k
            ph.set_method(hdr.b_method);
514
27.6k
            decompress(ibuf+j, ibuf+inlen, false);
515
27.6k
            if (12==szb_info) { // modern per-block filter
516
26.9k
                if (hdr.b_ftid) {
517
25.7k
                    Filter ft(ph.level);  // FIXME: ph.level for b_info?
518
25.7k
                    ft.init(hdr.b_ftid, 0);
519
25.7k
                    ft.cto = hdr.b_cto8;
520
25.7k
                    ft.unfilter(ibuf+inlen, sz_unc);
521
25.7k
                }
522
26.9k
            }
523
670
            else { // ancient per-file filter
524
670
                if (first_PF_X) { // Elf32_Ehdr is never filtered
525
0
                    first_PF_X = false;  // but everything else might be
526
0
                }
527
670
                else if (ph.filter) {
528
0
                    Filter ft(ph.level);
529
0
                    ft.init(ph.filter, 0);
530
0
                    ft.cto = (unsigned char) ph.filter_cto;
531
0
                    ft.unfilter(ibuf+inlen, sz_unc);
532
0
                }
533
670
            }
534
27.6k
        }
535
3.21k
        else if (sz_cpr == sz_unc) { // slide literal (non-compressible) block
536
3.11k
            memmove(&ibuf[inlen], &ibuf[j], sz_unc);
537
3.11k
        }
538
        // update checksum of uncompressed data
539
30.8k
        u_adler = upx_adler32(ibuf + inlen, sz_unc, u_adler);
540
        // write block
541
30.8k
        if (fo) {
542
18.3k
            if (is_rewrite) {
543
143
                fo->rewrite(ibuf, sz_unc);
544
143
            }
545
18.2k
            else {
546
18.2k
                fo->write(ibuf, sz_unc);
547
18.2k
                total_out += sz_unc;
548
18.2k
            }
549
18.3k
        }
550
12.4k
        else if (is_rewrite < 0) { // append to &ibuf[inlen]
551
460
            inlen += sz_unc;  // accounting; data is already there
552
460
            if (wanted <= (unsigned)sz_unc)  // done
553
460
                break;
554
460
        }
555
12.0k
        else if (wanted < (unsigned)sz_unc) // mismatched end-of-block
556
1
            throwCantUnpack("corrupt b_info");
557
12.0k
        else { // "upx -t": (!fo && !(is_rewrite < 0))
558
            // No output.
559
12.0k
        }
560
30.3k
        wanted -= sz_unc;
561
30.3k
    }
562
2.14k
    return inlen;
563
2.82k
}
564
565
/*************************************************************************
566
// Generic Unix canUnpack().
567
**************************************************************************/
568
569
// The prize is the value of overlay_offset: the offset of compressed data
570
tribool PackUnix::canUnpack()
571
77.0k
{
572
77.0k
    int const small = 32 + sizeof(overlay_offset);
573
    // Allow zero-filled last page, for Mac OS X code signing.
574
77.0k
    int bufsize = 2*4096 + 2*small +1;
575
77.0k
    if (bufsize > fi->st_size())
576
70.2k
        bufsize = fi->st_size();
577
77.0k
    MemBuffer buf(bufsize);
578
579
77.0k
    fi->seek(-(off_t)bufsize, SEEK_END);
580
77.0k
    fi->readx(buf, bufsize);
581
77.0k
    return find_overlay_offset(buf);
582
77.0k
}
583
584
int PackUnix::find_overlay_offset(MemBuffer const &buf)
585
77.0k
{
586
77.0k
    int const small = 32 + sizeof(overlay_offset);
587
77.0k
    int const bufsize = buf.getSize();
588
77.0k
    int i = bufsize;
589
1.64M
    while (i > small && 0 == buf[--i]) { }
590
77.0k
    i -= small;
591
    // allow incompressible extents
592
77.0k
    if (i < 0 || !getPackHeader(buf + i, bufsize - i, true))
593
66.8k
        return false;
594
595
10.1k
    int l = ph.buf_offset + ph.getPackHeaderSize();
596
10.1k
    if (l < 0 || i + l + 4 > bufsize)
597
8
        throwCantUnpack("file corrupted");
598
10.1k
    overlay_offset = get_te32(buf + i + l);
599
10.1k
    if ((off_t)overlay_offset >= file_size)
600
73
        throwCantUnpack("file corrupted");
601
602
10.0k
    return true;
603
10.1k
}
604
605
/*************************************************************************
606
// Generic Unix unpack().
607
//
608
// This code looks much like the one in stub/l_linux.c
609
// See notes there.
610
**************************************************************************/
611
612
void PackUnix::unpack(OutputFile *fo)
613
2.77k
{
614
2.77k
    b_info bhdr;
615
2.77k
    unsigned c_adler = upx_adler32(nullptr, 0);
616
2.77k
    unsigned u_adler = upx_adler32(nullptr, 0);
617
618
2.77k
    if (ph.version <= 11) {
619
13
        szb_info = sizeof(bhdr.sz_unc) + sizeof(bhdr.sz_cpr);  // old style
620
13
    }
621
    // defaults for ph.version == 8
622
2.77k
    unsigned orig_file_size = 0;
623
2.77k
    blocksize = 512 * 1024;
624
625
2.77k
    fi->seek(overlay_offset, SEEK_SET);
626
2.77k
    if (ph.version > 8)
627
2.77k
    {
628
2.77k
        p_info hbuf;
629
2.77k
        fi->readx(&hbuf, sizeof(hbuf));
630
2.77k
        orig_file_size = get_te32(&hbuf.p_filesize);
631
2.77k
        blocksize = get_te32(&hbuf.p_blocksize);
632
2.77k
        off_t max_inflated = file_size * 273;  // zlib limit (256 + 16 + 1)
633
634
2.77k
        if (max_inflated < orig_file_size
635
2.74k
        ||  max_inflated < blocksize
636
2.72k
        ||  file_size > (off_t)orig_file_size
637
2.68k
        ||  blocksize > orig_file_size) {
638
129
            throwCantUnpack("file header corrupted");
639
129
        }
640
2.77k
    }
641
0
    else
642
0
    {
643
        // skip 4 bytes (program id)
644
0
        fi->seek(4, SEEK_CUR);
645
0
    }
646
647
2.64k
    if ((int)(blocksize + OVERHEAD) < 0)
648
0
        throwCantUnpack("blocksize corrupted");
649
2.64k
    ibuf.alloc(blocksize + OVERHEAD + (blocksize >> ELF_NRV_FUDGE));
650
651
    // decompress blocks
652
2.64k
    total_in = 0;
653
2.64k
    total_out = 0;
654
2.64k
    memset(&bhdr, 0, sizeof(bhdr));
655
2.64k
    for (;;)
656
9.25k
    {
657
79.6k
#define buf ibuf
658
9.25k
        int i;
659
9.25k
        unsigned sz_unc, sz_cpr;
660
661
9.25k
        fi->readx(&bhdr, szb_info);
662
9.25k
        ph.u_len = sz_unc = get_te32(&bhdr.sz_unc);
663
9.25k
        ph.c_len = sz_cpr = get_te32(&bhdr.sz_cpr);
664
9.25k
        if (szb_info < sizeof(b_info)) { // some upx version 11
665
23
            bhdr.b_method = ph.method;
666
23
            bhdr.b_ftid = ph.filter;
667
23
            bhdr.b_cto8 = ph.filter_cto;
668
23
        }
669
670
9.25k
        if (sz_unc == 0)                   // uncompressed size 0 -> EOF
671
473
        {
672
            // note: must reload sz_cpr as magic is always stored le32
673
473
            sz_cpr = get_le32(&bhdr.sz_cpr);
674
473
            if (sz_cpr != UPX_MAGIC_LE32)  // sz_cpr must be h->magic
675
189
                throwCompressedDataViolation();
676
284
            break;
677
473
        }
678
        // Minimum compressed length: LZMA has 5-byte info header.
679
        // NRV_d8 has 1-byte initial 8 flag bits, plus end-of-block marker
680
        // (32 bit look-back offset of all 1s: encoded as 24 pairs of bits
681
        // {not last, 1} then low 8-bits of 0xff; total: 8 + 2*24 + 8 bits
682
        // ==> 8 bytes)
683
8.77k
        if (sz_unc <= 0 || sz_cpr <= 5u
684
8.48k
        ||  sz_cpr > sz_unc || sz_unc > blocksize)
685
852
            throwCantUnpack("corrupt b_info %#x %#x", sz_unc, sz_cpr);
686
687
        // Compressed output has control bytes such as the 32-bit
688
        // first flag bits of NRV_d32, the 5-byte info of LZMA, etc.
689
        // Fuzzers may try sz_cpr shorter than possible.
690
        // Use some OVERHEAD for safety.
691
7.92k
        i = blocksize + OVERHEAD - upx::umax(12u, sz_cpr);
692
7.92k
        if (i < 0)
693
0
            throwCantUnpack("corrupt b_info %#x %#x", sz_cpr, blocksize);
694
7.92k
        fi->readx(buf+i, sz_cpr);
695
        // update checksum of compressed data
696
7.92k
        c_adler = upx_adler32(buf + i, sz_cpr, c_adler);
697
        // decompress
698
7.92k
        if (sz_cpr < sz_unc) {
699
6.36k
            decompress(buf+i, buf, false);
700
6.36k
            if (0!=bhdr.b_ftid) {
701
5.08k
                Filter ft(ph.level);
702
5.08k
                ft.init(bhdr.b_ftid);
703
5.08k
                ft.cto = bhdr.b_cto8;
704
5.08k
                ft.unfilter(buf, sz_unc);
705
5.08k
            }
706
6.36k
            i = 0;
707
6.36k
        }
708
        // update checksum of uncompressed data
709
7.92k
        u_adler = upx_adler32(buf + i, sz_unc, u_adler);
710
7.92k
        total_in  += sz_cpr;
711
7.92k
        total_out += sz_unc;
712
        // write block
713
7.92k
        if (fo)
714
3.94k
            fo->write(buf + i, sz_unc);
715
7.92k
#undef buf
716
7.92k
    }
717
718
    // update header with totals
719
1.60k
    ph.c_len = total_in;
720
1.60k
    ph.u_len = total_out;
721
722
    // all bytes must be written
723
1.60k
    if (ph.version > 8 && total_out != orig_file_size)
724
29
        throwEOFException();
725
726
    // finally test the checksums
727
1.57k
    if (ph.c_adler != c_adler || ph.u_adler != u_adler)
728
238
        throwChecksumError();
729
1.57k
}
730
731
/* vim:set ts=4 sw=4 et: */