gnu: Add lci.
[jackhill/guix/guix.git] / gnu / packages / patches / fontconfig-CVE-2016-5384.patch
CommitLineData
6b5e654d
LF
1Fix CVE-2016-5384 (double-free resulting in arbitrary code execution):
2
3<https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-5384>
4
5Copied from upstream code repository:
6
7<https://cgit.freedesktop.org/fontconfig/commit/?id=7a4a5bd7897d216f0794ca9dbce0a4a5c9d14940>
8
9From 7a4a5bd7897d216f0794ca9dbce0a4a5c9d14940 Mon Sep 17 00:00:00 2001
10From: Tobias Stoeckmann <tobias@stoeckmann.org>
11Date: Sat, 25 Jun 2016 19:18:53 +0200
12Subject: Properly validate offsets in cache files.
13
14The cache files are insufficiently validated. Even though the magic
15number at the beginning of the file as well as time stamps are checked,
16it is not verified if contained offsets are in legal ranges or are
17even pointers.
18
19The lack of validation allows an attacker to trigger arbitrary free()
20calls, which in turn allows double free attacks and therefore arbitrary
21code execution. Due to the conversion from offsets into pointers through
22macros, this even allows to circumvent ASLR protections.
23
24This attack vector allows privilege escalation when used with setuid
25binaries like fbterm. A user can create ~/.fonts or any other
26system-defined user-private font directory, run fc-cache and adjust
27cache files in ~/.cache/fontconfig. The execution of setuid binaries will
28scan these files and therefore are prone to attacks.
29
30If it's not about code execution, an endless loop can be created by
31letting linked lists become circular linked lists.
32
33This patch verifies that:
34
35- The file is not larger than the maximum addressable space, which
36 basically only affects 32 bit systems. This allows out of boundary
37 access into unallocated memory.
38- Offsets are always positive or zero
39- Offsets do not point outside file boundaries
40- No pointers are allowed in cache files, every "pointer or offset"
41 field must be an offset or NULL
42- Iterating linked lists must not take longer than the amount of elements
43 specified. A violation of this rule can break a possible endless loop.
44
45If one or more of these points are violated, the cache is recreated.
46This is current behaviour.
47
48Even though this patch fixes many issues, the use of mmap() shall be
49forbidden in setuid binaries. It is impossible to guarantee with these
50checks that a malicious user does not change cache files after
51verification. This should be handled in a different patch.
52
53Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
54
55diff --git a/src/fccache.c b/src/fccache.c
56index 71e8f03..02ec301 100644
57--- a/src/fccache.c
58+++ b/src/fccache.c
59@@ -27,6 +27,7 @@
60 #include <fcntl.h>
61 #include <dirent.h>
62 #include <string.h>
63+#include <limits.h>
64 #include <sys/types.h>
65 #include <sys/stat.h>
66 #include <assert.h>
67@@ -587,6 +588,82 @@ FcCacheTimeValid (FcConfig *config, FcCache *cache, struct stat *dir_stat)
68 return cache->checksum == (int) dir_stat->st_mtime && fnano;
69 }
70
71+static FcBool
72+FcCacheOffsetsValid (FcCache *cache)
73+{
74+ char *base = (char *)cache;
75+ char *end = base + cache->size;
76+ intptr_t *dirs;
77+ FcFontSet *fs;
78+ int i, j;
79+
80+ if (cache->dir < 0 || cache->dir > cache->size - sizeof (intptr_t) ||
81+ memchr (base + cache->dir, '\0', cache->size - cache->dir) == NULL)
82+ return FcFalse;
83+
84+ if (cache->dirs < 0 || cache->dirs >= cache->size ||
85+ cache->dirs_count < 0 ||
86+ cache->dirs_count > (cache->size - cache->dirs) / sizeof (intptr_t))
87+ return FcFalse;
88+
89+ dirs = FcCacheDirs (cache);
90+ if (dirs)
91+ {
92+ for (i = 0; i < cache->dirs_count; i++)
93+ {
94+ FcChar8 *dir;
95+
96+ if (dirs[i] < 0 ||
97+ dirs[i] > end - (char *) dirs - sizeof (intptr_t))
98+ return FcFalse;
99+
100+ dir = FcOffsetToPtr (dirs, dirs[i], FcChar8);
101+ if (memchr (dir, '\0', end - (char *) dir) == NULL)
102+ return FcFalse;
103+ }
104+ }
105+
106+ if (cache->set < 0 || cache->set > cache->size - sizeof (FcFontSet))
107+ return FcFalse;
108+
109+ fs = FcCacheSet (cache);
110+ if (fs)
111+ {
112+ if (fs->nfont > (end - (char *) fs) / sizeof (FcPattern))
113+ return FcFalse;
114+
115+ if (fs->fonts != 0 && !FcIsEncodedOffset(fs->fonts))
116+ return FcFalse;
117+
118+ for (i = 0; i < fs->nfont; i++)
119+ {
120+ FcPattern *font = FcFontSetFont (fs, i);
121+ FcPatternElt *e;
122+ FcValueListPtr l;
123+
124+ if ((char *) font < base ||
125+ (char *) font > end - sizeof (FcFontSet) ||
126+ font->elts_offset < 0 ||
127+ font->elts_offset > end - (char *) font ||
128+ font->num > (end - (char *) font - font->elts_offset) / sizeof (FcPatternElt))
129+ return FcFalse;
130+
131+
132+ e = FcPatternElts(font);
133+ if (e->values != 0 && !FcIsEncodedOffset(e->values))
134+ return FcFalse;
135+
136+ for (j = font->num, l = FcPatternEltValues(e); j >= 0 && l; j--, l = FcValueListNext(l))
137+ if (l->next != NULL && !FcIsEncodedOffset(l->next))
138+ break;
139+ if (j < 0)
140+ return FcFalse;
141+ }
142+ }
143+
144+ return FcTrue;
145+}
146+
147 /*
148 * Map a cache file into memory
149 */
150@@ -596,7 +673,8 @@ FcDirCacheMapFd (FcConfig *config, int fd, struct stat *fd_stat, struct stat *di
151 FcCache *cache;
152 FcBool allocated = FcFalse;
153
154- if (fd_stat->st_size < (int) sizeof (FcCache))
155+ if (fd_stat->st_size > INTPTR_MAX ||
156+ fd_stat->st_size < (int) sizeof (FcCache))
157 return NULL;
158 cache = FcCacheFindByStat (fd_stat);
159 if (cache)
160@@ -652,6 +730,7 @@ FcDirCacheMapFd (FcConfig *config, int fd, struct stat *fd_stat, struct stat *di
161 if (cache->magic != FC_CACHE_MAGIC_MMAP ||
162 cache->version < FC_CACHE_VERSION_NUMBER ||
163 cache->size != (intptr_t) fd_stat->st_size ||
164+ !FcCacheOffsetsValid (cache) ||
165 !FcCacheTimeValid (config, cache, dir_stat) ||
166 !FcCacheInsert (cache, fd_stat))
167 {
168--
169cgit v0.10.2
170