libimobiledevice / libplist

A library to handle Apple Property List format in binary or XML
https://libimobiledevice.org
GNU Lesser General Public License v2.1
532 stars 304 forks source link

Assertion failed when converting certain profiles from STDIN #186

Closed ghost closed 3 years ago

ghost commented 3 years ago

plistutil seems to be buggy when reading from standard input and converting between XML and binary formats.

❯ wget 'https://femto.pw/gjg9' -O test.mobileconfig
--2021-02-06 01:29:13--  https://femto.pw/gjg9
Resolving femto.pw (femto.pw)... 188.165.215.31
Connecting to femto.pw (femto.pw)|188.165.215.31|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 44200 (43K) [text/plain]
Saving to: ‘test.mobileconfig’

test.mobileconfig                               100%[=====================================================================================================>]  43.16K  --.-KB/s    in 0.03s

2021-02-06 01:29:13 (1.63 MB/s) - ‘test.mobileconfig’ saved [44200/44200]

❯ ./plistutil -i test.mobileconfig
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
[snip]
</dict>
</plist>

❯ ./plistutil < test.mobileconfig                                                                                                                                                  [1140/1350]
plistutil: malloc.c:2385: sysmalloc: Assertion `(old_top == initial_top (av) && old_size == 0) || ((unsigned long) (old_size) >= MINSIZE && prev_inuse (old_top) && ((unsigned long) old_end $ (pagesize - 1)) == 0)' failed.
zsh: abort (core dumped)  ./plistutil < test.mobileconfig

❯ gdb ./plistutil
GNU gdb (Debian 8.2.1-2+b3) 8.2.1
Copyright (C) 2018 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./plistutil...done.
(gdb) run < test.mobileconfig
Starting program: /home/nyuszika7h/src/libplist/tools/.libs/plistutil < test.mobileconfig
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
plistutil: malloc.c:2385: sysmalloc: Assertion `(old_top == initial_top (av) && old_size == 0) || ((unsigned long) (old_size) >= MINSIZE && prev_inuse (old_top) && ((unsigned long) old_end & (pagesize - 1)) == 0)' failed.

Program received signal SIGABRT, Aborted.
__GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50
50      ../sysdeps/unix/sysv/linux/raise.c: No such file or directory.
(gdb) bt full
#0  __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50
        set = {__val = {0, 140737353769112, 140737488348256, 140737354048890, 0, 4214800, 140737488348300, 140737488348304, 4259856, 31, 4259632, 0, 0, 0, 0, 281470681751424}}
        pid = <optimized out>
        tid = <optimized out>
        ret = <optimized out>
#1  0x00007ffff7c62535 in __GI_abort () at abort.c:79
        save_stage = 1
        act = {__sigaction_handler = {sa_handler = 0x0, sa_sigaction = 0x0}, sa_mask = {__val = {0 <repeats 12 times>, 47, 24, 140737352025728, 140737352008352}}, sa_flags = -137691859,
          sa_restorer = 0x7ffff7dc6940 <__PRETTY_FUNCTION__.12983>}
        sigs = {__val = {32, 0 <repeats 15 times>}}
#2  0x00007ffff7cbfa68 in __malloc_assert (
    assertion=assertion@entry=0x7ffff7dc6230 "(old_top == initial_top (av) && old_size == 0) || ((unsigned long) (old_size) >= MINSIZE && prev_inuse (old_top) && ((unsigned long) old_end & (pagesize - 1)) == 0)", file=file@entry=0x7ffff7dc23b0 "malloc.c", line=line@entry=2385, function=function@entry=0x7ffff7dc6940 <__PRETTY_FUNCTION__.12983> "sysmalloc") at malloc.c:298
No locals.
#3  0x00007ffff7cc1e6f in sysmalloc (nb=nb@entry=65552, av=av@entry=0x7ffff7dfbc40 <main_arena>) at malloc.c:2382
        old_top = 0x41bde0
        old_size = 41280
        old_end = 0x425f20 ""
        size = <optimized out>
        brk = 0x0
        correction = <optimized out>
        snd_brk = 0x0
        front_misalign = <optimized out>
        end_misalign = <optimized out>
        aligned_brk = <optimized out>
        p = <optimized out>
        remainder = <optimized out>
        remainder_size = <optimized out>
        pagesize = 4096
        tried_mmap = <optimized out>
        __PRETTY_FUNCTION__ = "sysmalloc"
#4  0x00007ffff7cc32c9 in _int_malloc (av=av@entry=0x7ffff7dfbc40 <main_arena>, bytes=bytes@entry=65536) at malloc.c:4133
        p = <optimized out>
        iters = <optimized out>
        nb = 65552
        idx = 122
        bin = <optimized out>
        victim = <optimized out>
        size = <optimized out>
        victim_index = <optimized out>
        remainder = <optimized out>
        remainder_size = <optimized out>
        block = <optimized out>
--Type <RET> for more, q to quit, c to continue without paging--c
        bit = <optimized out>
        map = <optimized out>
        fwd = <optimized out>
        bck = <optimized out>
        tcache_unsorted_count = 0
        tcache_nb = 0
        tc_idx = 4095
        return_cached = 0
        __PRETTY_FUNCTION__ = "_int_malloc"
#5  0x00007ffff7cc43e3 in __GI___libc_malloc (bytes=65536) at malloc.c:3049
        ar_ptr = <optimized out>
        victim = <optimized out>
        hook = <optimized out>
        tbytes = 65552
        tc_idx = 4095
        __PRETTY_FUNCTION__ = "__libc_malloc"
#6  0x00007ffff7fa860a in byte_array_new (initial=63842) at bytearray.c:30
        a = 0x41bdd0
#7  0x00007ffff7fa9f5b in plist_to_xml (plist=0x410010, plist_xml=0x7fffffffe490, length=0x7fffffffe48c) at xplist.c:519
        size = 2
        outbuf = <optimized out>
#8  0x0000000000401744 in main (argc=<optimized out>, argv=<optimized out>) at plistutil.c:259
        size = 0
        read_size = 44200
        options = 0x405260
        iplist = <optimized out>
        filestats = <optimized out>
        plist_entire = 0x405280 "bplist00\330\001\003\005\a\t\v\r\017\002\004\006\b\n\f\016\020_\020\022PayloadDisplayNameUGmail_\020\023PayloadOrganization[Icon Themer_\020\022PayloadDescription_\020\064Created by Icon Themer 0.9.3 on 2021-02-05 19:28 UTC_\020\021PayloadIdentifier_\020\063app.iconthemer."...
        root_node = 0x2
        plist_out = 0x0
(gdb) quit
A debugging session is active.

        Inferior 1 [process 4410] will be killed.

Quit anyway? (y or n) y
nikias commented 3 years ago

Thanks for reporting this, good catch. I pushed commit 501f8c8 that should prevent the heap overflow, and also improves the buffer reallocation.

ghost commented 3 years ago

Just ran the new commit through ~8000 profiles, seems to be fine now. :)

nikias commented 3 years ago

This was where it overflowed for me: https://github.com/libimobiledevice/libplist/blob/ced16994f3787853bb74f8ec3baa7f2338292340/tools/plistutil.c#L188

But the reallocation was also pretty inefficient, once the BUF_SIZE was reached it would reallocate with +1 byte every time it reads another byte from STDIN, so I changed it to start with 4k and realloc +4k when needed.