1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
|
From: Tobias Stoeckmann <tobias@stoeckmann.org>
Date: Sun, 29 Oct 2017 15:19:41 +0100
Subject: Bug 739133 - (CVE-2017-17785) Heap overflow while parsing FLI files.
Origin: https://git.gnome.org/browse/GIMP/commit/?id=1882bac996a20ab5c15c42b0c5e8f49033a1af54
Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2017-17785
Bug-Debian: https://bugs.debian.org/884836
Bug: https://bugzilla.gnome.org/show_bug.cgi?id=739133
It is possible to trigger a heap overflow while parsing FLI files. The
RLE decoder is vulnerable to out of boundary writes due to lack of
boundary checks.
The variable "framebuf" points to a memory area which was allocated
with fli_header->width * fli_header->height bytes. The RLE decoder
therefore must never write beyond that limit.
If an illegal frame is detected, the parser won't stop, which means
that the next valid sequence is properly parsed again. This should
allow GIMP to parse FLI files as good as possible even if they are
broken by an attacker or by accident.
While at it, I changed the variable xc to be of type size_t, because
the multiplication of width and height could overflow a 16 bit type.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
(cherry picked from commit edb251a7ef1602d20a5afcbf23f24afb163de63b)
---
plug-ins/file-fli/fli.c | 50 ++++++++++++++++++++++++++++++++++---------------
1 file changed, 35 insertions(+), 15 deletions(-)
diff --git a/plug-ins/file-fli/fli.c b/plug-ins/file-fli/fli.c
index 313efeb977..ffb651e2af 100644
--- a/plug-ins/file-fli/fli.c
+++ b/plug-ins/file-fli/fli.c
@@ -25,6 +25,8 @@
#include "config.h"
+#include <glib/gstdio.h>
+
#include <string.h>
#include <stdio.h>
@@ -461,23 +463,27 @@ void fli_read_brun(FILE *f, s_fli_header *fli_header, unsigned char *framebuf)
unsigned short yc;
unsigned char *pos;
for (yc=0; yc < fli_header->height; yc++) {
- unsigned short xc, pc, pcnt;
+ unsigned short pc, pcnt;
+ size_t n, xc;
pc=fli_read_char(f);
xc=0;
pos=framebuf+(fli_header->width * yc);
+ n=(size_t)fli_header->width * (fli_header->height-yc);
for (pcnt=pc; pcnt>0; pcnt--) {
unsigned short ps;
ps=fli_read_char(f);
if (ps & 0x80) {
unsigned short len;
- for (len=-(signed char)ps; len>0; len--) {
+ for (len=-(signed char)ps; len>0 && xc<n; len--) {
pos[xc++]=fli_read_char(f);
}
} else {
unsigned char val;
+ size_t len;
+ len=MIN(n-xc,ps);
val=fli_read_char(f);
- memset(&(pos[xc]), val, ps);
- xc+=ps;
+ memset(&(pos[xc]), val, len);
+ xc+=len;
}
}
}
@@ -564,25 +570,34 @@ void fli_read_lc(FILE *f, s_fli_header *fli_header, unsigned char *old_framebuf,
memcpy(framebuf, old_framebuf, fli_header->width * fli_header->height);
firstline = fli_read_short(f);
numline = fli_read_short(f);
+ if (numline > fli_header->height || fli_header->height-numline < firstline)
+ return;
+
for (yc=0; yc < numline; yc++) {
- unsigned short xc, pc, pcnt;
+ unsigned short pc, pcnt;
+ size_t n, xc;
pc=fli_read_char(f);
xc=0;
pos=framebuf+(fli_header->width * (firstline+yc));
+ n=(size_t)fli_header->width * (fli_header->height-firstline-yc);
for (pcnt=pc; pcnt>0; pcnt--) {
unsigned short ps,skip;
skip=fli_read_char(f);
ps=fli_read_char(f);
- xc+=skip;
+ xc+=MIN(n-xc,skip);
if (ps & 0x80) {
unsigned char val;
+ size_t len;
ps=-(signed char)ps;
val=fli_read_char(f);
- memset(&(pos[xc]), val, ps);
- xc+=ps;
+ len=MIN(n-xc,ps);
+ memset(&(pos[xc]), val, len);
+ xc+=len;
} else {
- fread(&(pos[xc]), ps, 1, f);
- xc+=ps;
+ size_t len;
+ len=MIN(n-xc,ps);
+ fread(&(pos[xc]), len, 1, f);
+ xc+=len;
}
}
}
@@ -689,7 +704,8 @@ void fli_read_lc_2(FILE *f, s_fli_header *fli_header, unsigned char *old_framebu
yc=0;
numline = fli_read_short(f);
for (lc=0; lc < numline; lc++) {
- unsigned short xc, pc, pcnt, lpf, lpn;
+ unsigned short pc, pcnt, lpf, lpn;
+ size_t n, xc;
pc=fli_read_short(f);
lpf=0; lpn=0;
while (pc & 0x8000) {
@@ -700,26 +716,30 @@ void fli_read_lc_2(FILE *f, s_fli_header *fli_header, unsigned char *old_framebu
}
pc=fli_read_short(f);
}
+ yc=MIN(yc, fli_header->height);
xc=0;
pos=framebuf+(fli_header->width * yc);
+ n=(size_t)fli_header->width * (fli_header->height-yc);
for (pcnt=pc; pcnt>0; pcnt--) {
unsigned short ps,skip;
skip=fli_read_char(f);
ps=fli_read_char(f);
- xc+=skip;
+ xc+=MIN(n-xc,skip);
if (ps & 0x80) {
unsigned char v1,v2;
ps=-(signed char)ps;
v1=fli_read_char(f);
v2=fli_read_char(f);
- while (ps>0) {
+ while (ps>0 && xc+1<n) {
pos[xc++]=v1;
pos[xc++]=v2;
ps--;
}
} else {
- fread(&(pos[xc]), ps, 2, f);
- xc+=ps << 1;
+ size_t len;
+ len=MIN((n-xc)/2,ps);
+ fread(&(pos[xc]), len, 2, f);
+ xc+=len << 1;
}
}
if (lpf) pos[xc]=lpn;
--
2.15.1
|