Control: tags 833570 + pending

Hi Keith,

I've prepared an NMU for fontconfig (versioned as 2.11.0-6.5) and
uploaded it to DELAYED/5. Please feel free to tell me if I
should delay it longer.

Regards,
Salvatore
diff -Nru fontconfig-2.11.0/debian/changelog fontconfig-2.11.0/debian/changelog
--- fontconfig-2.11.0/debian/changelog	2016-03-12 19:01:15.000000000 +0100
+++ fontconfig-2.11.0/debian/changelog	2016-08-06 10:24:50.000000000 +0200
@@ -1,3 +1,11 @@
+fontconfig (2.11.0-6.5) unstable; urgency=high
+
+  * Non-maintainer upload.
+  * CVE-2016-5384: Possible double free due to insufficiently validated cache
+    files (Closes: #833570)
+
+ -- Salvatore Bonaccorso <car...@debian.org>  Sat, 06 Aug 2016 10:24:50 +0200
+
 fontconfig (2.11.0-6.4) unstable; urgency=medium
 
   * Non-maintainer upload.
diff -Nru fontconfig-2.11.0/debian/patches/07_CVE-2016-5384-Properly-validate-offsets-in-cache-files.patch fontconfig-2.11.0/debian/patches/07_CVE-2016-5384-Properly-validate-offsets-in-cache-files.patch
--- fontconfig-2.11.0/debian/patches/07_CVE-2016-5384-Properly-validate-offsets-in-cache-files.patch	1970-01-01 01:00:00.000000000 +0100
+++ fontconfig-2.11.0/debian/patches/07_CVE-2016-5384-Properly-validate-offsets-in-cache-files.patch	2016-08-06 10:24:50.000000000 +0200
@@ -0,0 +1,160 @@
+From 7a4a5bd7897d216f0794ca9dbce0a4a5c9d14940 Mon Sep 17 00:00:00 2001
+From: Tobias Stoeckmann <tob...@stoeckmann.org>
+Date: Sat, 25 Jun 2016 19:18:53 +0200
+Subject: [PATCH] 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 <tob...@stoeckmann.org>
+---
+ src/fccache.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
+ 1 file changed, 80 insertions(+), 1 deletion(-)
+
+--- 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 <assert.h>
+ #if defined(HAVE_MMAP) || defined(__CYGWIN__)
+@@ -544,6 +545,82 @@ FcCacheTimeValid (FcCache *cache, struct
+     return cache->checksum == (int) dir_stat->st_mtime;
+ }
+ 
++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
+  */
+@@ -553,7 +630,8 @@ FcDirCacheMapFd (int fd, struct stat *fd
+     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)
+@@ -609,6 +687,7 @@ FcDirCacheMapFd (int fd, struct stat *fd
+     if (cache->magic != FC_CACHE_MAGIC_MMAP ||
+ 	cache->version < FC_CACHE_CONTENT_VERSION ||
+ 	cache->size != (intptr_t) fd_stat->st_size ||
++	!FcCacheOffsetsValid (cache) ||
+ 	!FcCacheTimeValid (cache, dir_stat) ||
+ 	!FcCacheInsert (cache, fd_stat))
+     {
diff -Nru fontconfig-2.11.0/debian/patches/series fontconfig-2.11.0/debian/patches/series
--- fontconfig-2.11.0/debian/patches/series	2016-03-12 18:56:21.000000000 +0100
+++ fontconfig-2.11.0/debian/patches/series	2016-08-06 10:24:50.000000000 +0200
@@ -4,3 +4,4 @@
 04_mgopen_fonts.patch
 05_doc_files.patch
 06_cross.patch
+07_CVE-2016-5384-Properly-validate-offsets-in-cache-files.patch

Reply via email to