Coverage Report

Created: 2025-11-15 06:44

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) 1996-2025 Markus Franz Xaver Johannes Oberhumer
6
   Copyright (C) 1996-2025 Laszlo Molnar
7
   Copyright (C) 2000-2025 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
337k
    super(f), exetype(0), blocksize(0), overlay_offset(0), lsize(0),
51
337k
    methods_used(0), szb_info(sizeof(b_info))
52
337k
{
53
337k
    COMPILE_TIME_ASSERT(sizeof(Elf32_Ehdr) == 52)
54
337k
    COMPILE_TIME_ASSERT(sizeof(Elf32_Phdr) == 32)
55
337k
    COMPILE_TIME_ASSERT(sizeof(b_info) == 12)
56
337k
    COMPILE_TIME_ASSERT(sizeof(l_info) == 12)
57
337k
    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
337k
    saved_opt_android_shlib = opt->o_unix.android_shlib;
64
337k
    opt->o_unix.android_shlib = 0;  // Must apply selectively
65
    // Besides, cannot figure out why asl_slide_Shdrs does not work.
66
337k
}
67
68
PackUnix::~PackUnix()
69
337k
{
70
337k
    opt->o_unix.android_shlib = saved_opt_android_shlib;
71
337k
}
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
    unsigned const init_u_adler = ph.u_adler;
345
0
    unsigned const init_c_adler = ph.c_adler;
346
0
    MemBuffer hdr_ibuf;
347
0
    if (hdr_u_len) {
348
0
        hdr_ibuf.alloc(hdr_u_len);
349
0
        fi->seek(0, SEEK_SET);
350
0
        int l = fi->readx(hdr_ibuf, hdr_u_len);
351
0
        (void)l;
352
0
    }
353
0
    fi->seek(x.offset, SEEK_SET);
354
0
    for (off_t rest = x.size; 0 != rest; ) {
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 (!testOverlappingDecompression(obuf, tbuf, ph.overlap_overhead)) {
394
                // not in-place compressible
395
0
                ph.c_len = ph.u_len;
396
0
            }
397
0
        }
398
0
        if (ph.c_len >= ph.u_len) {
399
            // block is not compressible
400
0
            ph.c_len = ph.u_len;
401
0
            memcpy(obuf, ibuf, ph.c_len);
402
            // must update checksum of compressed data
403
0
            ph.c_adler = upx_adler32(ibuf, ph.u_len, ph.c_adler);
404
0
        }
405
406
        // write block sizes
407
0
        b_info tmp;
408
0
        if (hdr_u_len) {
409
0
            unsigned hdr_c_len = 0;
410
0
            MemBuffer hdr_obuf;
411
0
            hdr_obuf.allocForCompression(hdr_u_len);
412
0
            int r = upx_compress(hdr_ibuf, hdr_u_len, hdr_obuf, &hdr_c_len,
413
0
                /* &progress callback */ nullptr,
414
0
                ph_forced_method(ph.method), 10,
415
0
                /* &config_t */ nullptr, /* &result_t */ nullptr);
416
0
            if (r != UPX_E_OK)
417
0
                throwInternalError("header compression failed");
418
0
            if (hdr_c_len >= hdr_u_len)
419
0
                throwInternalError("header compression size increase");
420
0
            ph.saved_u_adler = upx_adler32(hdr_ibuf, hdr_u_len, init_u_adler);
421
0
            ph.saved_c_adler = upx_adler32(hdr_obuf, hdr_c_len, init_c_adler);
422
0
            ph.u_adler = upx_adler32(ibuf, ph.u_len, ph.saved_u_adler);
423
0
            ph.c_adler = upx_adler32(obuf, ph.c_len, ph.saved_c_adler);
424
0
            end_u_adler = ph.u_adler;
425
0
            memset(&tmp, 0, sizeof(tmp));
426
0
            set_te32(&tmp.sz_unc, hdr_u_len);
427
0
            set_te32(&tmp.sz_cpr, hdr_c_len);
428
0
            tmp.b_method = (unsigned char) ph_forced_method(ph.method);
429
0
            tmp.b_extra = b_extra;
430
0
            fo->write(&tmp, sizeof(tmp));
431
0
            total_out += sizeof(tmp);
432
0
            b_len += sizeof(b_info);
433
0
            fo->write(hdr_obuf, hdr_c_len);
434
0
            total_out += hdr_c_len;
435
0
            total_in  += hdr_u_len;
436
0
            hdr_u_len = 0;  // compress hdr one time only
437
0
        }
438
0
        memset(&tmp, 0, sizeof(tmp));
439
0
        set_te32(&tmp.sz_unc, ph.u_len);
440
0
        set_te32(&tmp.sz_cpr, ph.c_len);
441
0
        if (ph.c_len < ph.u_len) {
442
0
            tmp.b_method = (unsigned char) ph.method;
443
0
            if (ft) {
444
0
                tmp.b_ftid = (unsigned char) ft->id;
445
0
                tmp.b_cto8 = ft->cto;
446
0
            }
447
0
        }
448
0
        tmp.b_extra = b_extra;
449
0
        fo->write(&tmp, sizeof(tmp));
450
0
        total_out += sizeof(tmp);
451
0
        b_len += sizeof(b_info);
452
453
0
        if (ft) {
454
0
            ph.u_adler = end_u_adler;
455
0
        }
456
        // write compressed data
457
0
        if (ph.c_len < ph.u_len) {
458
0
            fo->write(obuf, ph.c_len);
459
0
            total_out += ph.c_len;
460
            // Checks ph.u_adler after decompression, after unfiltering
461
0
            verifyOverlappingDecompression(ft);
462
0
        }
463
0
        else {
464
0
            fo->write(ibuf, ph.u_len);
465
0
            total_out += ph.u_len;
466
0
        }
467
468
0
        total_in += ph.u_len;
469
0
    }
470
0
}
471
472
// Consumes b_info header block and sz_cpr data block from input file 'fi'.
473
// De-compresses; appends to output file 'fo' unless rewrite or peeking.
474
// For "peeking" without writing: set (fo = nullptr), (is_rewrite = -1)
475
// Return actual length when peeking; else 0.
476
unsigned PackUnix::unpackExtent(unsigned wanted, OutputFile *fo,
477
    unsigned &c_adler, unsigned &u_adler,
478
    bool first_PF_X,
479
    int is_rewrite // 0(false): write; 1(true): rewrite; -1: no write
480
)
481
1.55k
{
482
1.55k
    b_info hdr; memset(&hdr, 0, sizeof(hdr));
483
1.55k
    unsigned inlen = 0; // output index (if-and-only-if peeking)
484
7.99k
    while (wanted) {
485
6.75k
        fi->readx(&hdr, szb_info);
486
6.75k
        int const sz_unc = ph.u_len = get_te32(&hdr.sz_unc);
487
6.75k
        int const sz_cpr = ph.c_len = get_te32(&hdr.sz_cpr);
488
6.75k
        ph.filter_cto = hdr.b_cto8;
489
490
6.75k
        if (sz_unc == 0 || M_LZMA < hdr.b_method) {
491
32
            throwCantUnpack("corrupt b_info");
492
0
            break;
493
32
        }
494
6.71k
        if (sz_unc <= 0 || sz_cpr <= 0)
495
35
            throwCantUnpack("corrupt b_info");
496
6.68k
        if (sz_cpr > sz_unc || sz_unc > (int)blocksize)
497
32
            throwCantUnpack("corrupt b_info");
498
499
        // place the input for overlapping de-compression
500
6.65k
        int j = inlen + sz_unc + OVERHEAD - sz_cpr;
501
6.65k
        if (ibuf.getSize() < (unsigned)(j + sz_cpr)) {
502
0
            throwCantUnpack("corrupt b_info");
503
0
        }
504
6.65k
        fi->readx(ibuf+j, sz_cpr);
505
6.65k
        total_in += sz_cpr;
506
        // update checksum of compressed data
507
6.65k
        c_adler = upx_adler32(ibuf + j, sz_cpr, c_adler);
508
509
6.65k
        if (sz_cpr < sz_unc) { // block was compressed
510
6.28k
            ph.set_method(hdr.b_method);
511
6.28k
            decompress(ibuf+j, ibuf+inlen, false);
512
6.28k
            if (12==szb_info) { // modern per-block filter
513
5.94k
                if (hdr.b_ftid) {
514
4.73k
                    Filter ft(ph.level);  // FIXME: ph.level for b_info?
515
4.73k
                    ft.init(hdr.b_ftid, 0);
516
4.73k
                    ft.cto = hdr.b_cto8;
517
4.73k
                    ft.unfilter(ibuf+inlen, sz_unc);
518
4.73k
                }
519
5.94k
            }
520
343
            else { // ancient per-file filter
521
343
                if (first_PF_X) { // Elf32_Ehdr is never filtered
522
0
                    first_PF_X = false;  // but everything else might be
523
0
                }
524
343
                else if (ph.filter) {
525
0
                    Filter ft(ph.level);
526
0
                    ft.init(ph.filter, 0);
527
0
                    ft.cto = (unsigned char) ph.filter_cto;
528
0
                    ft.unfilter(ibuf+inlen, sz_unc);
529
0
                }
530
343
            }
531
6.28k
        }
532
365
        else if (sz_cpr == sz_unc) { // slide literal (non-compressible) block
533
310
            memmove(&ibuf[inlen], &ibuf[j], sz_unc);
534
310
        }
535
        // update checksum of uncompressed data
536
6.65k
        u_adler = upx_adler32(ibuf + inlen, sz_unc, u_adler);
537
        // write block
538
6.65k
        if (fo) {
539
4.73k
            if (is_rewrite) {
540
126
                fo->rewrite(ibuf, sz_unc);
541
126
            }
542
4.61k
            else {
543
4.61k
                fo->write(ibuf, sz_unc);
544
4.61k
                total_out += sz_unc;
545
4.61k
            }
546
4.73k
        }
547
1.91k
        else if (is_rewrite < 0) { // append to &ibuf[inlen]
548
210
            inlen += sz_unc;  // accounting; data is already there
549
210
            if (wanted <= (unsigned)sz_unc)  // done
550
210
                break;
551
210
        }
552
1.70k
        else if (wanted < (unsigned)sz_unc) // mismatched end-of-block
553
0
            throwCantUnpack("corrupt b_info");
554
1.70k
        else { // "upx -t": (!fo && !(is_rewrite < 0))
555
            // No output.
556
1.70k
        }
557
6.44k
        wanted -= sz_unc;
558
6.44k
    }
559
1.45k
    return inlen;
560
1.55k
}
561
562
/*************************************************************************
563
// Generic Unix canUnpack().
564
**************************************************************************/
565
566
// The prize is the value of overlay_offset: the offset of compressed data
567
tribool PackUnix::canUnpack()
568
48.6k
{
569
48.6k
    int const small = 32 + sizeof(overlay_offset);
570
    // Allow zero-filled last page, for Mac OS X code signing.
571
48.6k
    int bufsize = 2*4096 + 2*small +1;
572
48.6k
    if (bufsize > fi->st_size())
573
41.2k
        bufsize = fi->st_size();
574
48.6k
    MemBuffer buf(bufsize);
575
576
48.6k
    fi->seek(-(off_t)bufsize, SEEK_END);
577
48.6k
    fi->readx(buf, bufsize);
578
48.6k
    return find_overlay_offset(buf);
579
48.6k
}
580
581
int PackUnix::find_overlay_offset(MemBuffer const &buf)
582
48.6k
{
583
48.6k
    int const small = 32 + sizeof(overlay_offset);
584
48.6k
    int const bufsize = buf.getSize();
585
48.6k
    int i = bufsize;
586
3.17M
    while (i > small && 0 == buf[--i]) { }
587
48.6k
    i -= small;
588
    // allow incompressible extents
589
48.6k
    if (i < 0 || !getPackHeader(buf + i, bufsize - i, true))
590
44.5k
        return false;
591
592
4.15k
    int l = ph.buf_offset + ph.getPackHeaderSize();
593
4.15k
    if (l < 0 || i + l + 4 > bufsize)
594
9
        throwCantUnpack("file corrupted");
595
4.14k
    overlay_offset = get_te32(buf + i + l);
596
4.14k
    if ((off_t)overlay_offset >= file_size)
597
56
        throwCantUnpack("file corrupted");
598
599
4.09k
    return true;
600
4.14k
}
601
602
/*************************************************************************
603
// Generic Unix unpack().
604
//
605
// This code looks much like the one in stub/l_linux.c
606
// See notes there.
607
**************************************************************************/
608
609
void PackUnix::unpack(OutputFile *fo)
610
2.01k
{
611
2.01k
    b_info bhdr;
612
2.01k
    unsigned c_adler = upx_adler32(nullptr, 0);
613
2.01k
    unsigned u_adler = upx_adler32(nullptr, 0);
614
615
2.01k
    if (ph.version <= 11) {
616
1
        szb_info = sizeof(bhdr.sz_unc) + sizeof(bhdr.sz_cpr);  // old style
617
1
    }
618
    // defaults for ph.version == 8
619
2.01k
    unsigned orig_file_size = 0;
620
2.01k
    blocksize = 512 * 1024;
621
622
2.01k
    fi->seek(overlay_offset, SEEK_SET);
623
2.01k
    if (ph.version > 8)
624
2.01k
    {
625
2.01k
        p_info hbuf;
626
2.01k
        fi->readx(&hbuf, sizeof(hbuf));
627
2.01k
        orig_file_size = get_te32(&hbuf.p_filesize);
628
2.01k
        blocksize = get_te32(&hbuf.p_blocksize);
629
2.01k
        off_t max_inflated = file_size * 273;  // zlib limit (256 + 16 + 1)
630
631
2.01k
        if (max_inflated < orig_file_size
632
1.96k
        ||  max_inflated < blocksize
633
1.94k
        ||  file_size > (off_t)orig_file_size
634
1.90k
        ||  blocksize > orig_file_size) {
635
122
            throwCantUnpack("file header corrupted");
636
122
        }
637
2.01k
    }
638
0
    else
639
0
    {
640
        // skip 4 bytes (program id)
641
0
        fi->seek(4, SEEK_CUR);
642
0
    }
643
644
1.89k
    if ((int)(blocksize + OVERHEAD) < 0)
645
0
        throwCantUnpack("blocksize corrupted");
646
1.89k
    ibuf.alloc(blocksize + OVERHEAD);
647
648
    // decompress blocks
649
1.89k
    total_in = 0;
650
1.89k
    total_out = 0;
651
1.89k
    memset(&bhdr, 0, sizeof(bhdr));
652
1.89k
    for (;;)
653
3.85k
    {
654
37.3k
#define buf ibuf
655
3.85k
        int i;
656
3.85k
        unsigned sz_unc, sz_cpr;
657
658
3.85k
        fi->readx(&bhdr, szb_info);
659
3.85k
        ph.u_len = sz_unc = get_te32(&bhdr.sz_unc);
660
3.85k
        ph.c_len = sz_cpr = get_te32(&bhdr.sz_cpr);
661
3.85k
        ph.set_method(bhdr.b_method);
662
663
3.85k
        if (sz_unc == 0)                   // uncompressed size 0 -> EOF
664
102
        {
665
            // note: must reload sz_cpr as magic is always stored le32
666
102
            sz_cpr = get_le32(&bhdr.sz_cpr);
667
102
            if (sz_cpr != UPX_MAGIC_LE32)  // sz_cpr must be h->magic
668
7
                throwCompressedDataViolation();
669
95
            break;
670
102
        }
671
        // Minimum compressed length: LZMA has 5-byte info header.
672
        // NRV_d8 has 1-byte initial 8 flag bits, plus end-of-block marker
673
        // (32 bit look-back offset of all 1s: encoded as 24 pairs of bits
674
        // {not last, 1} then low 8-bits of 0xff; total: 8 + 2*24 + 8 bits
675
        // ==> 8 bytes)
676
3.75k
        if (sz_unc <= 0 || sz_cpr <= 5u
677
3.58k
        ||  sz_cpr > sz_unc || sz_unc > blocksize)
678
127
            throwCantUnpack("corrupt b_info %#x %#x", sz_unc, sz_cpr);
679
680
        // Compressed output has control bytes such as the 32-bit
681
        // first flag bits of NRV_d32, the 5-byte info of LZMA, etc.
682
        // Fuzzers may try sz_cpr shorter than possible.
683
        // Use some OVERHEAD for safety.
684
3.62k
        i = blocksize + OVERHEAD - upx::umax(12u, sz_cpr);
685
3.62k
        if (i < 0)
686
0
            throwCantUnpack("corrupt b_info %#x %#x", sz_cpr, blocksize);
687
3.62k
        fi->readx(buf+i, sz_cpr);
688
        // update checksum of compressed data
689
3.62k
        c_adler = upx_adler32(buf + i, sz_cpr, c_adler);
690
        // decompress
691
3.62k
        if (sz_cpr < sz_unc) {
692
3.40k
            decompress(buf+i, buf, false);
693
3.40k
            if (0!=bhdr.b_ftid) {
694
1.90k
                Filter ft(ph.level);
695
1.90k
                ft.init(bhdr.b_ftid);
696
1.90k
                ft.cto = bhdr.b_cto8;
697
1.90k
                ft.unfilter(buf, sz_unc);
698
1.90k
            }
699
3.40k
            i = 0;
700
3.40k
        }
701
        // update checksum of uncompressed data
702
3.62k
        u_adler = upx_adler32(buf + i, sz_unc, u_adler);
703
3.62k
        total_in  += sz_cpr;
704
3.62k
        total_out += sz_unc;
705
        // write block
706
3.62k
        if (fo)
707
1.75k
            fo->write(buf + i, sz_unc);
708
3.62k
#undef buf
709
3.62k
    }
710
711
    // update header with totals
712
1.76k
    ph.c_len = total_in;
713
1.76k
    ph.u_len = total_out;
714
715
    // all bytes must be written
716
1.76k
    if (ph.version > 8 && total_out != orig_file_size)
717
3
        throwEOFException();
718
719
    // finally test the checksums
720
1.75k
    if (ph.c_adler != c_adler || ph.u_adler != u_adler)
721
84
        throwChecksumError();
722
1.75k
}
723
724
/* vim:set ts=4 sw=4 et: */