exploit the possibilities
Home Files News &[SERVICES_TAB]About Contact Add New

Microsoft DirectWrite / AFDKO dnaGrow Insufficient Integer Overflow Check

Microsoft DirectWrite / AFDKO dnaGrow Insufficient Integer Overflow Check
Posted Jul 11, 2019
Authored by Google Security Research, mjurczyk

Microsoft DirectWrite / AFDKO suffers from having an insufficient integer overflow check in dnaGrow.

tags | advisory, overflow
SHA-256 | 51c785aaeac307b6f004977e9dda66854c854edace9664c3df8a5c5e0aa2a972

Microsoft DirectWrite / AFDKO dnaGrow Insufficient Integer Overflow Check

Change Mirror Download
Microsoft DirectWrite / AFDKO insufficient integer overflow check in dnaGrow 

-----=====[ Background ]=====-----

AFDKO (Adobe Font Development Kit for OpenType) is a set of tools for examining, modifying and building fonts. The core part of this toolset is a font handling library written in C, which provides interfaces for reading and writing Type 1, OpenType, TrueType (to some extent) and several other font formats. While the library existed as early as 2000, it was open-sourced by Adobe in 2014 on GitHub [1, 2], and is still actively developed. The font parsing code can be generally found under afdko/c/public/lib/source/*read/*.c in the project directory tree.

At the time of this writing, based on the available source code, we conclude that AFDKO was originally developed to only process valid, well-formatted font files. It contains very few to no sanity checks of the input data, which makes it susceptible to memory corruption issues (e.g. buffer overflows) and other memory safety problems, if the input file doesn't conform to the format specification.

We have recently discovered that starting with Windows 10 1709 (Fall Creators Update, released in October 2017), Microsoft's DirectWrite library [3] includes parts of AFDKO, and specifically the modules for reading and writing OpenType/CFF fonts (internally called cfr/cfw). The code is reachable through dwrite!AdobeCFF2Snapshot, called by methods of the FontInstancer class, called by dwrite!DWriteFontFace::CreateInstancedStream and dwrite!DWriteFactory::CreateInstancedStream. This strongly indicates that the code is used for instancing the relatively new variable fonts [4], i.e. building a single instance of a variable font with a specific set of attributes. The CreateInstancedStream method is not a member of a public COM interface, but we have found that it is called by d2d1!dxc::TextConvertor::InstanceFontResources, which led us to find out that it can be reached through the Direct2D printing interface. It is unclear if there are other ways to trigger the font instancing functionality.

One example of a client application which uses Direct2D printing is Microsoft Edge. If a user opens a specially crafted website with an embedded OpenType variable font and decides to print it (to PDF, XPS, or another physical or virtual printer), the AFDKO code will execute with the attacker's font file as input.

-----=====[ Description ]=====-----

The AFDKO library has its own implementation of dynamic arrays, semantically resembling e.g. std::vector from C++. These objects are implemented in c/public/lib/source/dynarr/dynarr.c and c/public/lib/api/dynarr.h. One of the more important functions operating on dynarrays is dnaGrow(), designed to extend a dynamic array or set its initial size. It is used by numerous other routines such as dnaSetCnt, dnaIndex, dnaMax, dnaNext, dnaExtend, or the dnaSET_CNT macro. It should be noted that it is potentially possible for the function's arguments to be unsanitized values loaded directly from an input font.

In order to prevent security vulnerabilities stemming from arithmetic errors while calculating buffer lengths, dnaGrow() explicitly checks for integer overflow conditions. One example of such check is shown below:

--- cut ---
79 } else if (da->size == 0) {
80 /* Initial allocation */
81 size_t init = (size_t)da->array;
82 size_t new_mem_size;
83 new_size = ((size_t)index < init) ? init : init + ((index - init) + da->incr) / da->incr * da->incr;
84 new_mem_size = new_size * elemsize;
85 if (new_mem_size / elemsize == new_size) /* check math overflow */
86 new_ptr = h->mem.manage(&h->mem, NULL, new_mem_size);
87 else
88 new_ptr = NULL;
89 } else {
--- cut ---

The if statement in line 85 guarantees that \"new_mem_size\" is exactly the product of \"new_size\" and \"elemsize\", and an integer overflow doesn't take place. There is also a similar check in another code branch a few lines below:

--- cut ---
89 } else {
90 /* Incremental allocation */
91 new_size = da->size +
92 ((index - da->size) + da->incr) / da->incr * da->incr;
93 if (new_size * elemsize >= new_size) /* check math overflow */
94 new_ptr = h->mem.manage(&h->mem, da->array, new_size * elemsize);
95 else
96 new_ptr = NULL;
97 }
--- cut ---

Here, the overflow check in line 93 is incorrect. It is possible to craft the \"new_size\" and \"elemsize\" values such that an undetected integer overflow takes place. For instance, on 32-bit platforms, if new_size=0x58000000 and elemsize=0x4, then (size_t)(new_size * elemsize) is 0x60000000, truncated from the actual result of 0x160000000. These example values pass the above sanity check, but still lead to the allocation of a too small buffer in relation to the requested number of elements.

While there is an evident problem in the code, we haven't found an obvious way to exploit it after a brief analysis. We believe that this is mostly caused by the fact that the faulty code is found in a code branch responsible for incrementally extending the size of a non-empty array, and not the one performing the initial allocation. There are few arrays which are grown dynamically during the library run time; most of them have their length set only once and never changed later on. It is even more difficult to find a dynamically extended array which accepts completely arbitrary element counts.

Nevertheless, given the right set of conditions (currently or in the future), this bug might facilitate the exploitation of an integer overflow condition that would otherwise be impossible to trigger. As such an integer overflow could subsequently lead to memory corruption, we decided to report the problem despite the apparent lack of an attack vector at this moment. We recommend fixing it by using the correct overflow check construct from line 85.

-----=====[ References ]=====-----

[1] https://blog.typekit.com/2014/09/19/new-from-adobe-type-open-sourced-font-development-tools/
[2] https://github.com/adobe-type-tools/afdko
[3] https://docs.microsoft.com/en-us/windows/desktop/directwrite/direct-write-portal
[4] https://medium.com/variable-fonts/https-medium-com-tiro-introducing-opentype-variable-fonts-12ba6cd2369

This bug is subject to a 90 day disclosure deadline. After 90 days elapse or a patch has been made broadly available (whichever is earlier), the bug report will become visible to the public.

Found by: mjurczyk@google.com

Login or Register to add favorites

File Archive:

February 2023

  • Su
  • Mo
  • Tu
  • We
  • Th
  • Fr
  • Sa
  • 1
    Feb 1st
    11 Files
  • 2
    Feb 2nd
    9 Files
  • 3
    Feb 3rd
    5 Files
  • 4
    Feb 4th
    0 Files
  • 5
    Feb 5th
    0 Files
  • 6
    Feb 6th
    0 Files
  • 7
    Feb 7th
    0 Files
  • 8
    Feb 8th
    0 Files
  • 9
    Feb 9th
    0 Files
  • 10
    Feb 10th
    0 Files
  • 11
    Feb 11th
    0 Files
  • 12
    Feb 12th
    0 Files
  • 13
    Feb 13th
    0 Files
  • 14
    Feb 14th
    0 Files
  • 15
    Feb 15th
    0 Files
  • 16
    Feb 16th
    0 Files
  • 17
    Feb 17th
    0 Files
  • 18
    Feb 18th
    0 Files
  • 19
    Feb 19th
    0 Files
  • 20
    Feb 20th
    0 Files
  • 21
    Feb 21st
    0 Files
  • 22
    Feb 22nd
    0 Files
  • 23
    Feb 23rd
    0 Files
  • 24
    Feb 24th
    0 Files
  • 25
    Feb 25th
    0 Files
  • 26
    Feb 26th
    0 Files
  • 27
    Feb 27th
    0 Files
  • 28
    Feb 28th
    0 Files

Top Authors In Last 30 Days

File Tags


packet storm

© 2022 Packet Storm. All rights reserved.

Hosting By