what you don't know can hurt you

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
MD5 | d82c47ee0ae57de226097bbbba93f262

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:

May 2020

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

Top Authors In Last 30 Days

File Tags


packet storm

© 2020 Packet Storm. All rights reserved.

Security Services
Hosting By