Bug 703366 - Double free pdf_new_dict object in mutool
Summary: Double free pdf_new_dict object in mutool
Status: RESOLVED FIXED
Alias: None
Product: MuPDF
Classification: Unclassified
Component: mupdf (show other bugs)
Version: 1.18.0
Hardware: PC Linux
: P4 major
Assignee: Robin Watts
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2021-01-22 10:18 UTC by Mishechkin Maxim
Modified: 2024-07-11 20:36 UTC (History)
1 user (show)

See Also:
Customer:
Word Size: ---


Attachments
Crash (33.28 KB, application/x-gzip)
2021-01-22 10:18 UTC, Mishechkin Maxim
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Mishechkin Maxim 2021-01-22 10:18:28 UTC
Created attachment 20460 [details]
Crash

Hello!
This bug was found by Crusher (developing at ISP RAS), thanks to following colleagues: Maxim Mishechkin, Vitalii Akolzin, Shamil Kurmangaleev, Denis Straghkov, Fedor Nis'kov, Ivan Gulakov.

Product version:

mupdf-1.18.0 

Environment:

18.04

To reproduce:

1. Download file crash crash_mutool.tar.gz
2. Extract crash_mutool.tar.gz 
3. Run mutool with options: clean -s -d -a -l -z

./mutool clean -s -d -a -l -z id_crash_0 out_file.pdf

Program crashes with SIGABRT.

Error message:

warning: first object in xref is not free
warning: object out of range (0 0 R); xref size 61
corrupted double-linked list
[1]    55708 abort      ./mutool clean -s -d -a -l -z ../../../id_crash_0 out_file.pdf

GDB output:

Program received signal SIGABRT, Aborted.
__GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:51
51      ../sysdeps/unix/sysv/linux/raise.c: No such file or directory.
(gdb) bt
#0  __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:51
#1  0x00007ffff6f9a921 in __GI_abort () at abort.c:79
#2  0x00007ffff6fe3967 in __libc_message (action=action@entry=do_abort, fmt=fmt@entry=0x7ffff7110b0d "%s\n") at ../sysdeps/posix/libc_fatal.c:181
#3  0x00007ffff6fea9da in malloc_printerr (str=str@entry=0x7ffff710ec3a "corrupted double-linked list") at malloc.c:5342
#4  0x00007ffff6ff225f in _int_free (have_lock=0, p=<optimized out>, av=0x7ffff7345c40 <main_arena>) at malloc.c:4325
#5  __GI___libc_free (mem=<optimized out>) at malloc.c:3134
#6  0x000055555560fa14 in ?? ()
#7  0x0000555555671752 in ?? ()
#8  0x0000555555671738 in ?? ()
#9  0x0000555555691825 in ?? ()
#10 0x0000555555691923 in ?? ()
#11 0x000055555569222d in ?? ()
#12 0x00005555555d0a66 in ?? ()
#13 0x000055555564a28e in ?? ()
#14 0x00005555555be8c5 in ?? ()
#15 0x00007ffff6f7bbf7 in __libc_start_main (main=0x55555559e870, argc=9, argv=0x7fffffffe418, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffe408) at ../csu/libc-start.c:310
#16 0x000055555559eb9a in ?? ()

Valgrind output:

==147614== Memcheck, a memory error detector
==147614== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==147614== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==147614== Command: ./mutool clean -s -d -a -l -z ../../../id_crash_0 out_file.pdf
==147614==
warning: first object in xref is not free
==147614== Invalid read of size 2
==147614==    at 0x225672: ??? (in ./mutool)
==147614==    by 0x245824: ??? (in ./mutool)
==147614==    by 0x245922: ??? (in ./mutool)
==147614==    by 0x24ABA3: ??? (in ./mutool)
==147614==    by 0x23BE49: ??? (in ./mutool)
==147614==    by 0x241DF3: ??? (in ./mutool)
==147614==    by 0x2435AD: ??? (in ./mutool)
==147614==    by 0x1FE6CE: ??? (in ./mutool)
==147614==    by 0x1728C4: ??? (in ./mutool)
==147614==    by 0x58E7BF6: (below main) (libc-start.c:310)
==147614==  Address 0x5f4cc80 is 0 bytes inside a block of size 40 free'd
==147614==    at 0x4C32D3B: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==147614==    by 0x1C3A13: ??? (in ./mutool)
==147614==    by 0x24587B: ??? (in ./mutool)
==147614==    by 0x245922: ??? (in ./mutool)
==147614==    by 0x24ABA3: ??? (in ./mutool)
==147614==    by 0x23BE49: ??? (in ./mutool)
==147614==    by 0x241B3F: ??? (in ./mutool)
==147614==    by 0x2435AD: ??? (in ./mutool)
==147614==    by 0x1FE6CE: ??? (in ./mutool)
==147614==    by 0x1728C4: ??? (in ./mutool)
==147614==    by 0x58E7BF6: (below main) (libc-start.c:310)
==147614==  Block was alloc'd at
==147614==    at 0x4C31B0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==147614==    by 0x1C386B: ??? (in ./mutool)
==147614==    by 0x1C38B5: ??? (in ./mutool)
==147614==    by 0x221E1E: ??? (in ./mutool)
==147614==    by 0x233B0D: ??? (in ./mutool)
==147614==    by 0x234626: ??? (in ./mutool)
==147614==    by 0x24758E: ??? (in ./mutool)
==147614==    by 0x2476EC: ??? (in ./mutool)
==147614==    by 0x247CD6: ??? (in ./mutool)
==147614==    by 0x247FBA: ??? (in ./mutool)
==147614==    by 0x248506: ??? (in ./mutool)
==147614==    by 0x24DEA7: ??? (in ./mutool)
==147614==
==147614== Invalid read of size 2
==147614==    at 0x225672: ??? (in ./mutool)
==147614==    by 0x245824: ??? (in ./mutool)
==147614==    by 0x245922: ??? (in ./mutool)
==147614==    by 0x24622C: ??? (in ./mutool)
==147614==    by 0x184A65: ??? (in ./mutool)
==147614==    by 0x1FE28D: ??? (in ./mutool)
==147614==    by 0x1728C4: ??? (in ./mutool)
==147614==    by 0x58E7BF6: (below main) (libc-start.c:310)
==147614==  Address 0x5f4cc80 is 0 bytes inside a block of size 40 free'd
==147614==    at 0x4C32D3B: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==147614==    by 0x1C3A13: ??? (in ./mutool)
==147614==    by 0x24587B: ??? (in ./mutool)
==147614==    by 0x245922: ??? (in ./mutool)
==147614==    by 0x24ABA3: ??? (in ./mutool)
==147614==    by 0x23BE49: ??? (in ./mutool)
==147614==    by 0x241B3F: ??? (in ./mutool)
==147614==    by 0x2435AD: ??? (in ./mutool)
==147614==    by 0x1FE6CE: ??? (in ./mutool)
==147614==    by 0x1728C4: ??? (in ./mutool)
==147614==    by 0x58E7BF6: (below main) (libc-start.c:310)
==147614==  Block was alloc'd at
==147614==    at 0x4C31B0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==147614==    by 0x1C386B: ??? (in ./mutool)
==147614==    by 0x1C38B5: ??? (in ./mutool)
==147614==    by 0x221E1E: ??? (in ./mutool)
==147614==    by 0x233B0D: ??? (in ./mutool)
==147614==    by 0x234626: ??? (in ./mutool)
==147614==    by 0x24758E: ??? (in ./mutool)
==147614==    by 0x2476EC: ??? (in ./mutool)
==147614==    by 0x247CD6: ??? (in ./mutool)
==147614==    by 0x247FBA: ??? (in ./mutool)
==147614==    by 0x248506: ??? (in ./mutool)
==147614==    by 0x24DEA7: ??? (in ./mutool)
==147614==
==147614==
==147614== HEAP SUMMARY:
==147614==     in use at exit: 0 bytes in 0 blocks
==147614==   total heap usage: 8,334 allocs, 8,334 frees, 7,601,622 bytes allocated
==147614==
==147614== All heap blocks were freed -- no leaks are possible
==147614==
==147614== For counts of detected and suppressed errors, rerun with: -v
==147614== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)

Analysis

1. In the “pdf_new_dict” function
source/pdf/pdf-object.c: 907
obj = Memento_label (fz_malloc (ctx, sizeof (pdf_obj_dict)), "pdf_obj (dict)");
In this line, memory allocated for dictionary (data type in a pdf file).
2. Then, in the process of garbage collection, duplicate objects (various entities of the pdf format) deleted, and they renumbered. The previously created dictionary object is deleted in the pdf_drop_dict (pdf-object.c: 1702)) function.
source/pdf/pdf-object.c: 1702
fz_free (ctx, obj);
3. Valgrind detected a call to the field of size 2 bytes of the dictionary object, which deleted in the function fz_drop_imp16.
include/mupdf/fitz/context.h: 702
if (* refs> 0)
4. After that, the dictionary object freed again, which leads to SIGABORT because the address of the freed chunk has already freed.
5. For further exploitation, it is necessary to place data of the same size in this chunk and select a situation with the dictionary, such that this will lead to exploitation.
Comment 1 Mishechkin Maxim 2021-01-22 10:41:54 UTC
GDB with debug:
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
warning: first object in xref is not free
warning: object out of range (0 0 R); xref size 61
corrupted double-linked list

Program received signal SIGABRT, Aborted.
__GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:51
51      ../sysdeps/unix/sysv/linux/raise.c: No such file or directory.
(gdb) bt
#0  __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:51
#1  0x00007ffff6f9a921 in __GI_abort () at abort.c:79
#2  0x00007ffff6fe3967 in __libc_message (action=action@entry=do_abort, fmt=fmt@entry=0x7ffff7110b0d "%s\n") at ../sysdeps/posix/libc_fatal.c:181
#3  0x00007ffff6fea9da in malloc_printerr (str=str@entry=0x7ffff710ec3a "corrupted double-linked list") at malloc.c:5342
#4  0x00007ffff6ff225f in _int_free (have_lock=0, p=<optimized out>, av=0x7ffff7345c40 <main_arena>) at malloc.c:4325
#5  __GI___libc_free (mem=<optimized out>) at malloc.c:3134
#6  0x000055555563095a in fz_free_default (opaque=0x0, ptr=0x555557e9d670) at source/fitz/memory.c:170
#7  0x000055555563088b in fz_free (ctx=0x555557e74260, p=0x555557e9d670) at source/fitz/memory.c:141
#8  0x00005555556b64d5 in pdf_drop_dict (ctx=0x555557e74260, obj=0x555557e9ea50) at source/pdf/pdf-object.c:1701
#9  0x00005555556b65a1 in pdf_drop_obj (ctx=0x555557e74260, obj=0x555557e9ea50) at source/pdf/pdf-object.c:1723
#10 0x00005555556b64ae in pdf_drop_dict (ctx=0x555557e74260, obj=0x555557e9e880) at source/pdf/pdf-object.c:1698
#11 0x00005555556b65a1 in pdf_drop_obj (ctx=0x555557e74260, obj=0x555557e9e880) at source/pdf/pdf-object.c:1723
#12 0x00005555556df241 in pdf_drop_xref_sections_imp (ctx=0x555557e74260, doc=0x555557e8c400, xref_sections=0x555557e9d930, num_xref_sections=1) at source/pdf/pdf-xref.c:47
#13 0x00005555556df3b8 in pdf_drop_xref_sections (ctx=0x555557e74260, doc=0x555557e8c400) at source/pdf/pdf-xref.c:74
#14 0x00005555556e3d62 in pdf_drop_document_imp (ctx=0x555557e74260, doc=0x555557e8c400) at source/pdf/pdf-xref.c:1562
#15 0x00005555555dc267 in fz_drop_document (ctx=0x555557e74260, doc=0x555557e8c400) at source/fitz/document.c:278
#16 0x00005555556e40ac in pdf_drop_document (ctx=0x555557e74260, doc=0x555557e8c400) at source/pdf/pdf-xref.c:1621
#17 0x00005555556837c9 in pdf_clean_file (ctx=0x555557e74260, infile=0x7fffffffe71a "../id_crash_0", outfile=0x7fffffffe728 "out_file.pdf", password=0x555555a6327e "", opts=0x7fffffffe1b0, argc=0, argv=0x7fffffffe490) at source/pdf/pdf-clean-file.c:337
#18 0x00005555555c52a1 in pdfclean_main (argc=8, argv=0x7fffffffe450) at source/tools/pdfclean.c:117
#19 0x00005555555a00a0 in main (argc=9, argv=0x7fffffffe448) at source/tools/mutool.c:130
Comment 2 Mishechkin Maxim 2021-01-22 10:44:20 UTC
Valgrind with --leak-check=full

==177009== Memcheck, a memory error detector
==177009== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==177009== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==177009== Command: build/debug/mutool clean -s -d -a -l -z ../id_crash_0 out_file.pdf
==177009==
warning: first object in xref is not free
==177009== Invalid read of size 2
==177009==    at 0x265699: fz_drop_imp16 (context.h:702)
==177009==    by 0x26A558: pdf_drop_obj (pdf-object.c:1718)
==177009==    by 0x293240: pdf_drop_xref_sections_imp (pdf-xref.c:47)
==177009==    by 0x2933B7: pdf_drop_xref_sections (pdf-xref.c:74)
==177009==    by 0x294A69: pdf_replace_xref (pdf-xref.c:536)
==177009==    by 0x2887E2: renumberobjs (pdf-write.c:943)
==177009==    by 0x28A57C: linearize (pdf-write.c:1516)
==177009==    by 0x290C5C: do_pdf_save_document (pdf-write.c:3421)
==177009==    by 0x291968: pdf_save_document (pdf-write.c:3639)
==177009==    by 0x2377A5: pdf_clean_file (pdf-clean-file.c:333)
==177009==    by 0x1792A0: pdfclean_main (pdfclean.c:117)
==177009==    by 0x15409F: main (mutool.c:130)
==177009==  Address 0x5f4cc90 is 0 bytes inside a block of size 40 free'd
==177009==    at 0x4C32D3B: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==177009==    by 0x1E4959: fz_free_default (memory.c:170)
==177009==    by 0x1E488A: fz_free (memory.c:141)
==177009==    by 0x26A4E7: pdf_drop_dict (pdf-object.c:1702)
==177009==    by 0x26A5A0: pdf_drop_obj (pdf-object.c:1723)
==177009==    by 0x2932D2: pdf_drop_xref_sections_imp (pdf-xref.c:57)
==177009==    by 0x2933B7: pdf_drop_xref_sections (pdf-xref.c:74)
==177009==    by 0x294A69: pdf_replace_xref (pdf-xref.c:536)
==177009==    by 0x2887E2: renumberobjs (pdf-write.c:943)
==177009==    by 0x290BC1: do_pdf_save_document (pdf-write.c:3409)
==177009==    by 0x291968: pdf_save_document (pdf-write.c:3639)
==177009==    by 0x2377A5: pdf_clean_file (pdf-clean-file.c:333)
==177009==  Block was alloc'd at
==177009==    at 0x4C31B0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==177009==    by 0x1E4912: fz_malloc_default (memory.c:158)
==177009==    by 0x1E4438: do_scavenging_malloc (memory.c:29)
==177009==    by 0x1E4593: fz_malloc (memory.c:67)
==177009==    by 0x267BFF: pdf_new_dict (pdf-object.c:907)
==177009==    by 0x27D15C: pdf_parse_dict (pdf-parse.c:602)
==177009==    by 0x27D8C0: pdf_parse_ind_obj (pdf-parse.c:770)
==177009==    by 0x296400: pdf_read_new_xref (pdf-xref.c:1014)
==177009==    by 0x2968BB: pdf_read_xref (pdf-xref.c:1105)
==177009==    by 0x29692C: read_xref_section (pdf-xref.c:1120)
==177009==    by 0x296C11: pdf_read_xref_sections (pdf-xref.c:1189)
==177009==    by 0x296DC3: pdf_load_xref (pdf-xref.c:1245)
==177009==
==177009==
==177009== HEAP SUMMARY:
==177009==     in use at exit: 0 bytes in 0 blocks
==177009==   total heap usage: 8,334 allocs, 8,334 frees, 7,601,638 bytes allocated
==177009==
==177009== All heap blocks were freed -- no leaks are possible
==177009==
==177009== For counts of detected and suppressed errors, rerun with: -v
==177009== ERROR SUMMARY: 2 errors from 1 contexts (suppressed: 0 from 0)
Comment 3 Robin Watts 2021-01-25 18:32:08 UTC
Solved in:

commit cee7cefc610d42fd383b3c80c12cbc675443176a
Author: Robin Watts <Robin.Watts@artifex.com>
Date:   Fri Jan 22 17:05:15 2021 +0000

    Bug 703366: Fix double free of object during linearization.

    This appears to happen because we parse an illegal object from
    a broken file and assign it to object 0, which is defined to
    be free.

    Here, we fix the parsing code so this can't happen.
Comment 4 Mishechkin Maxim 2021-01-25 19:26:16 UTC
What about registration CVE for this vulnerability?
Comment 5 Mishechkin Maxim 2021-02-02 13:36:56 UTC
Ping