** Please note that this issue is confidential and no information should be
disclosed until it is made public, see "Whiteboard" for a date **
Drew Yao writes:
We have found a security issue in libTIFF's handling of LZW-encoded
images. The use of uninitialized data could lead to a buffer
underflow and a crash or arbitrary code execution.
CVE-ID: CVE-2008-2327
Versions known to be affected:
v3.8.2
Timing:
This issue should remain embargoed until 8/25/2008.
Credit:
Drew Yao of Apple Product Security
We have contacted the libTIFF maintainers about this issue.
A brief introduction to LZW decoding:
http://www.fileformat.info/format/tiff/corion-lzw.htm
http://en.wikipedia.org/wiki/Lzw#Decoding
The LZW algorithm is based on a translation table, or string table,
that maps strings of input characters into codes. The TIFF
implementation uses variable-length codes, with a maximum code length
of 12 bits
The string table is initialized to contain all possible single-
character strings. There are 256 of them, numbered 0 through 255,
since our characters are bytes. There are also special codes:
#define CODE_CLEAR 256 /* code to clear string table */
#define CODE_EOI 257 /* end-of-information code */
#define CODE_FIRST 258 /* first free code entry */
As decoding continues, longer strings are added to the string table as
linked lists of characters. If the string table gets full(4096
entries), there is a "Clear code" which signals to reinitialize the
string table.
tif_lzw.c
In LZWSetupDecode():
sp->dec_codetab = (code_t*)_TIFFmalloc(CSIZE*sizeof (code_t));
In LZWPreDecode():
sp->dec_free_entp = sp->dec_codetab + CODE_FIRST;
/*
* Zero entries that are not yet filled in. We do
* this to guard against bogus input data that causes
* us to index into undefined entries. If you can
* come up with a way to safely bounds-check input codes
* while decoding then you can remove this operation.
*/
_TIFFmemset(sp->dec_free_entp, 0, (CSIZE-CODE_FIRST)*sizeof (code_t));
In LZWDecodeCompat(), LZWDecode(), and LZWDecodeVector(), when a
CODE_CLEAR code is received, the string table does not get re-zeroed,
leading to uninitialized data in the table being used, if the next
code after the CODE_CLEAR is > CODE_FIRST.
For example in LZWDecodeCompat():
while (occ > 0) {
NextCode(tif, sp, bp, code, GetNextCodeCompat);
if (code == CODE_EOI)
break;
if (code == CODE_CLEAR) {
free_entp = sp->dec_codetab + CODE_FIRST; <--
free_entp points to the first free entry
after the constant entries.
nbits = BITS_MIN;
nbitsmask = MAXCODE(BITS_MIN);
maxcodep = sp->dec_codetab + nbitsmask;
NextCode(tif, sp, bp, code, GetNextCodeCompat); <--
get the next code. The implementation
expects it to be < CODE_FIRST, but that might not be true in a
malicious file.
if (code == CODE_EOI)
break;
*op++ = code, occ--;
oldcodep = sp->dec_codetab + code;
continue;
}
codep = sp->dec_codetab + code; <--codep
points into uninitialized data
...
do {
*--tp = codep->value; <--
potential buffer underflow
} while( (codep = codep->next) != NULL);
[...]
A simple fix is to zero out the whole string table starting at
CODE_FIRST whenever a CODE_CLEAR is received.
The attached patch against 3.8.2 does this.
I have tested this fix to properly display a bunch of valid files, and
not crash after heavy fuzzing.
We have no upstream comment on either of the two patches, and probably won't
have until after the embargo timeline. I'll attach an ebuild applying the
existing patches, and we can update them later if upstream decides otherwise.
Arch Security Liaisons, please test the attached ebuild and report it stable on
this bug.
Target keywords : "alpha amd64 arm hppa ia64 m68k ppc ppc64 s390 sh sparc x86"
CC'ing current Liaisons:
alpha : yoswink, armin76
amd64 : keytoaster, tester
hppa : jer
ppc : dertobi123
ppc64 : corsair
sparc : fmccor
x86 : maekke, armin76
I combined both patches into one because it was decided to combine both issues
(Drew Yao and the upstream bug) into the CVE. Committed the ebuild to the tree
with the gathered keywords, still to do:
Arches, please test and mark stable:
=media-libs/tiff-3.8.2-r4
Target keywords : "alpha amd64 arm hppa ia64 m68k ppc ppc64 s390 sh sparc x86"
Already stabled : "alpha amd64 hppa ppc64 x86"
Missing keywords: "arm ia64 m68k ppc s390 sh sparc"