README | 17 ++++++++-- configure.ac | 4 +- fontconfig/fontconfig.h | 2 - src/fccache.c | 81 +++++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 98 insertions(+), 6 deletions(-) New commits: commit 6b222c52cdede497ffed629853f947948052dfc7 Author: Akira TAGOH <akira@xxxxxxxxx> Date: Fri Aug 5 14:47:02 2016 +0900 Bump version to 2.12.1 diff --git a/README b/README index 6ebca65..bf840a5 100644 --- a/README +++ b/README @@ -1,12 +1,25 @@ Fontconfig Font configuration and customization library - Version 2.12 - 2016-06-15 + Version 2.12.1 + 2016-08-05 Check INSTALL for compilation and installation instructions. Report bugs to https://bugs.freedesktop.org in the fontconfig module. +2.12.1 + +Akira TAGOH (6): + Add --with-default-hinting to configure + Update CaseFolding.txt to Unicode 9.0 + Check python installed in autogen.sh + Fix some errors related to python3 + Bug 96676 - Check range of FcWeightFromOpenType argument + Update libtool revision + +Tobias Stoeckmann (1): + Properly validate offsets in cache files. + 2.12 Akira TAGOH (8): diff --git a/configure.ac b/configure.ac index a048ba6..4948816 100644 --- a/configure.ac +++ b/configure.ac @@ -33,7 +33,7 @@ dnl This is the package version number, not the shared library dnl version. This same version number must appear in fontconfig/fontconfig.h dnl Yes, it is a pain to synchronize version numbers. Unfortunately, it's dnl not possible to extract the version number here from fontconfig.h -AC_INIT([fontconfig], [2.12.0], [https://bugs.freedesktop.org/enter_bug.cgi?product=fontconfig]) +AC_INIT([fontconfig], [2.12.1], [https://bugs.freedesktop.org/enter_bug.cgi?product=fontconfig]) AM_INIT_AUTOMAKE([1.11 parallel-tests dist-bzip2]) m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])]) diff --git a/fontconfig/fontconfig.h b/fontconfig/fontconfig.h index e760402..5c72b22 100644 --- a/fontconfig/fontconfig.h +++ b/fontconfig/fontconfig.h @@ -52,7 +52,7 @@ typedef int FcBool; #define FC_MAJOR 2 #define FC_MINOR 12 -#define FC_REVISION 0 +#define FC_REVISION 1 #define FC_VERSION ((FC_MAJOR * 10000) + (FC_MINOR * 100) + (FC_REVISION)) commit 68869149e36ae546ec428f345a552a6ad093e953 Author: Akira TAGOH <akira@xxxxxxxxx> Date: Fri Aug 5 14:45:36 2016 +0900 Update libtool revision diff --git a/configure.ac b/configure.ac index a04695b..a048ba6 100644 --- a/configure.ac +++ b/configure.ac @@ -75,7 +75,7 @@ dnl bump revision when fixing bugs dnl bump current and age, reset revision to zero when adding APIs dnl bump current, leave age, reset revision to zero when changing/removing APIS LIBT_CURRENT=10 -LIBT_REVISION=1 +LIBT_REVISION=2 AC_SUBST(LIBT_CURRENT) AC_SUBST(LIBT_REVISION) LIBT_AGE=9 commit 7a4a5bd7897d216f0794ca9dbce0a4a5c9d14940 Author: Tobias Stoeckmann <tobias@xxxxxxxxxxxxxx> Date: Sat Jun 25 19:18:53 2016 +0200 Properly validate offsets in cache files. The cache files are insufficiently validated. Even though the magic number at the beginning of the file as well as time stamps are checked, it is not verified if contained offsets are in legal ranges or are even pointers. The lack of validation allows an attacker to trigger arbitrary free() calls, which in turn allows double free attacks and therefore arbitrary code execution. Due to the conversion from offsets into pointers through macros, this even allows to circumvent ASLR protections. This attack vector allows privilege escalation when used with setuid binaries like fbterm. A user can create ~/.fonts or any other system-defined user-private font directory, run fc-cache and adjust cache files in ~/.cache/fontconfig. The execution of setuid binaries will scan these files and therefore are prone to attacks. If it's not about code execution, an endless loop can be created by letting linked lists become circular linked lists. This patch verifies that: - The file is not larger than the maximum addressable space, which basically only affects 32 bit systems. This allows out of boundary access into unallocated memory. - Offsets are always positive or zero - Offsets do not point outside file boundaries - No pointers are allowed in cache files, every "pointer or offset" field must be an offset or NULL - Iterating linked lists must not take longer than the amount of elements specified. A violation of this rule can break a possible endless loop. If one or more of these points are violated, the cache is recreated. This is current behaviour. Even though this patch fixes many issues, the use of mmap() shall be forbidden in setuid binaries. It is impossible to guarantee with these checks that a malicious user does not change cache files after verification. This should be handled in a different patch. Signed-off-by: Tobias Stoeckmann <tobias@xxxxxxxxxxxxxx> diff --git a/src/fccache.c b/src/fccache.c index 71e8f03..02ec301 100644 --- a/src/fccache.c +++ b/src/fccache.c @@ -27,6 +27,7 @@ #include <fcntl.h> #include <dirent.h> #include <string.h> +#include <limits.h> #include <sys/types.h> #include <sys/stat.h> #include <assert.h> @@ -587,6 +588,82 @@ FcCacheTimeValid (FcConfig *config, FcCache *cache, struct stat *dir_stat) return cache->checksum == (int) dir_stat->st_mtime && fnano; } +static FcBool +FcCacheOffsetsValid (FcCache *cache) +{ + char *base = (char *)cache; + char *end = base + cache->size; + intptr_t *dirs; + FcFontSet *fs; + int i, j; + + if (cache->dir < 0 || cache->dir > cache->size - sizeof (intptr_t) || + memchr (base + cache->dir, '\0', cache->size - cache->dir) == NULL) + return FcFalse; + + if (cache->dirs < 0 || cache->dirs >= cache->size || + cache->dirs_count < 0 || + cache->dirs_count > (cache->size - cache->dirs) / sizeof (intptr_t)) + return FcFalse; + + dirs = FcCacheDirs (cache); + if (dirs) + { + for (i = 0; i < cache->dirs_count; i++) + { + FcChar8 *dir; + + if (dirs[i] < 0 || + dirs[i] > end - (char *) dirs - sizeof (intptr_t)) + return FcFalse; + + dir = FcOffsetToPtr (dirs, dirs[i], FcChar8); + if (memchr (dir, '\0', end - (char *) dir) == NULL) + return FcFalse; + } + } + + if (cache->set < 0 || cache->set > cache->size - sizeof (FcFontSet)) + return FcFalse; + + fs = FcCacheSet (cache); + if (fs) + { + if (fs->nfont > (end - (char *) fs) / sizeof (FcPattern)) + return FcFalse; + + if (fs->fonts != 0 && !FcIsEncodedOffset(fs->fonts)) + return FcFalse; + + for (i = 0; i < fs->nfont; i++) + { + FcPattern *font = FcFontSetFont (fs, i); + FcPatternElt *e; + FcValueListPtr l; + + if ((char *) font < base || + (char *) font > end - sizeof (FcFontSet) || + font->elts_offset < 0 || + font->elts_offset > end - (char *) font || + font->num > (end - (char *) font - font->elts_offset) / sizeof (FcPatternElt)) + return FcFalse; + + + e = FcPatternElts(font); + if (e->values != 0 && !FcIsEncodedOffset(e->values)) + return FcFalse; + + for (j = font->num, l = FcPatternEltValues(e); j >= 0 && l; j--, l = FcValueListNext(l)) + if (l->next != NULL && !FcIsEncodedOffset(l->next)) + break; + if (j < 0) + return FcFalse; + } + } + + return FcTrue; +} + /* * Map a cache file into memory */ @@ -596,7 +673,8 @@ FcDirCacheMapFd (FcConfig *config, int fd, struct stat *fd_stat, struct stat *di FcCache *cache; FcBool allocated = FcFalse; - if (fd_stat->st_size < (int) sizeof (FcCache)) + if (fd_stat->st_size > INTPTR_MAX || + fd_stat->st_size < (int) sizeof (FcCache)) return NULL; cache = FcCacheFindByStat (fd_stat); if (cache) @@ -652,6 +730,7 @@ FcDirCacheMapFd (FcConfig *config, int fd, struct stat *fd_stat, struct stat *di if (cache->magic != FC_CACHE_MAGIC_MMAP || cache->version < FC_CACHE_VERSION_NUMBER || cache->size != (intptr_t) fd_stat->st_size || + !FcCacheOffsetsValid (cache) || !FcCacheTimeValid (config, cache, dir_stat) || !FcCacheInsert (cache, fd_stat)) { _______________________________________________ Fontconfig mailing list Fontconfig@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/fontconfig