Move the checks around to avoid missing cases where we might access
memory that is no longer valid. Also avoid touching the underlying
stream implicitly (e.g. via the destructor) as it might also no
longer be valid.
A malicious server could theoretically use this for remote code
execution in the client.
Issue found by Pavel Cheremushkin from Kaspersky Lab
(cherry picked from commit d61a767d68
)
tags/v1.10.1
return offset + ptr - start; | return offset + ptr - start; | ||||
} | } | ||||
void ZlibInStream::removeUnderlying() | |||||
void ZlibInStream::flushUnderlying() | |||||
{ | { | ||||
ptr = end = start; | ptr = end = start; | ||||
if (!underlying) return; | |||||
while (bytesIn > 0) { | while (bytesIn > 0) { | ||||
decompress(true); | decompress(true); | ||||
end = start; // throw away any data | end = start; // throw away any data | ||||
} | } | ||||
underlying = 0; | |||||
setUnderlying(NULL, 0); | |||||
} | } | ||||
void ZlibInStream::reset() | void ZlibInStream::reset() | ||||
void ZlibInStream::deinit() | void ZlibInStream::deinit() | ||||
{ | { | ||||
assert(zs != NULL); | assert(zs != NULL); | ||||
removeUnderlying(); | |||||
setUnderlying(NULL, 0); | |||||
inflateEnd(zs); | inflateEnd(zs); | ||||
delete zs; | delete zs; | ||||
zs = NULL; | zs = NULL; | ||||
{ | { | ||||
if (itemSize > bufSize) | if (itemSize > bufSize) | ||||
throw Exception("ZlibInStream overrun: max itemSize exceeded"); | throw Exception("ZlibInStream overrun: max itemSize exceeded"); | ||||
if (!underlying) | |||||
throw Exception("ZlibInStream overrun: no underlying stream"); | |||||
if (end - ptr != 0) | if (end - ptr != 0) | ||||
memmove(start, ptr, end - ptr); | memmove(start, ptr, end - ptr); | ||||
bool ZlibInStream::decompress(bool wait) | bool ZlibInStream::decompress(bool wait) | ||||
{ | { | ||||
if (!underlying) | |||||
throw Exception("ZlibInStream overrun: no underlying stream"); | |||||
zs->next_out = (U8*)end; | zs->next_out = (U8*)end; | ||||
zs->avail_out = start + bufSize - end; | zs->avail_out = start + bufSize - end; | ||||
virtual ~ZlibInStream(); | virtual ~ZlibInStream(); | ||||
void setUnderlying(InStream* is, int bytesIn); | void setUnderlying(InStream* is, int bytesIn); | ||||
void removeUnderlying(); | |||||
void flushUnderlying(); | |||||
int pos(); | int pos(); | ||||
void reset(); | void reset(); | ||||
num++; | num++; | ||||
} | } | ||||
zis.removeUnderlying(); | |||||
zis.flushUnderlying(); | |||||
zis.setUnderlying(NULL, 0); | |||||
handler->handleClipboardProvide(flags, lengths, buffers); | handler->handleClipboardProvide(flags, lengths, buffers); | ||||
num++; | num++; | ||||
} | } | ||||
zis.removeUnderlying(); | |||||
zis.flushUnderlying(); | |||||
zis.setUnderlying(NULL, 0); | |||||
handler->handleClipboardProvide(flags, lengths, buffers); | handler->handleClipboardProvide(flags, lengths, buffers); | ||||
zis[streamId].readBytes(netbuf, dataSize); | zis[streamId].readBytes(netbuf, dataSize); | ||||
zis[streamId].removeUnderlying(); | |||||
zis[streamId].flushUnderlying(); | |||||
zis[streamId].setUnderlying(NULL, 0); | |||||
delete ms; | delete ms; | ||||
bufptr = netbuf; | bufptr = netbuf; |
} | } | ||||
} | } | ||||
zis->removeUnderlying(); | |||||
zis->flushUnderlying(); | |||||
zis->setUnderlying(NULL, 0); | |||||
} | } | ||||
#undef ZRLE_DECODE | #undef ZRLE_DECODE |