Multiple checks and balances in Dispack to avoid buffer overlfow.

Allow filter variants to omit the standard header.
Use E8E9 in Dispack filter as a fallback.
Fix integer overflow for type value in thread data struct.
Do not inline functions in DEBUG build.
This commit is contained in:
Moinak Ghosh 2014-12-21 14:13:58 +05:30
parent 1db822d866
commit 73307c3996
8 changed files with 77 additions and 35 deletions

View file

@ -40,10 +40,12 @@ extern "C" {
#endif #endif
typedef unsigned char uchar_t; typedef unsigned char uchar_t;
#define DISPACK_MAGIC "DisPack "
#pragma pack(1) #pragma pack(1)
struct FileHeader struct FileHeader
{ {
char magic[8];
sU32 SizeBefore; // number of bytes before start of code section sU32 SizeBefore; // number of bytes before start of code section
sU32 SizeAfter; // number of bytes after code section sU32 SizeAfter; // number of bytes after code section
sU32 SizeTransformed; // size of transformed code section sU32 SizeTransformed; // size of transformed code section
@ -57,6 +59,7 @@ dispack_filter_encode(uchar_t *inData, size_t len, uchar_t **out_buf)
{ {
uchar_t *pos; uchar_t *pos;
FileHeader hdr; FileHeader hdr;
sU32 sizeNow;
*out_buf = (uchar_t *)malloc(len); *out_buf = (uchar_t *)malloc(len);
if (*out_buf == NULL) if (*out_buf == NULL)
@ -95,25 +98,48 @@ dispack_filter_encode(uchar_t *inData, size_t len, uchar_t **out_buf)
// transform code // transform code
sU32 transSize = len - sizeof (hdr); sU32 transSize = len - sizeof (hdr);
if (DisFilter(inData + fileOffs, codeSize, imageBase + codeStart, pos, transSize) == NULL) if (fileOffs + codeSize > len) {
return (0); codeSize = len - fileOffs;
}
if (DisFilter(inData + fileOffs, codeSize, imageBase + codeStart, pos, transSize) == NULL) {
goto cmp_E89;
}
pos += transSize; pos += transSize;
// Now plonk the header // Give up if dispack savings is not enough for header space and we can overflow the buffer.
memcpy(hdr.magic, DISPACK_MAGIC, strlen(DISPACK_MAGIC));
hdr.SizeBefore = fileOffs; hdr.SizeBefore = fileOffs;
hdr.SizeAfter = len - (fileOffs + codeSize); hdr.SizeAfter = len - (fileOffs + codeSize);
hdr.SizeTransformed = transSize; hdr.SizeTransformed = transSize;
hdr.SizeOriginal = codeSize; hdr.SizeOriginal = codeSize;
hdr.Origin = imageBase + codeStart; hdr.Origin = imageBase + codeStart;
sizeNow = pos - *out_buf;
if (sizeNow + hdr.SizeBefore + hdr.SizeAfter >= len) {
goto cmp_E89;
}
// Now plonk the header
memcpy(*out_buf, &hdr, sizeof (hdr)); memcpy(*out_buf, &hdr, sizeof (hdr));
if (hdr.SizeBefore > 0) {
// Copy rest of the data // Copy rest of the data
memcpy(pos, inData, hdr.SizeBefore); memcpy(pos, inData, hdr.SizeBefore);
pos += hdr.SizeBefore; pos += hdr.SizeBefore;
}
if (hdr.SizeAfter > 0) {
memcpy(pos, inData + (fileOffs + codeSize), hdr.SizeAfter); memcpy(pos, inData + (fileOffs + codeSize), hdr.SizeAfter);
pos += hdr.SizeAfter; pos += hdr.SizeAfter;
}
return (pos - *out_buf); return (pos - *out_buf);
cmp_E89:
// Apply an E8E9 filter this does not put the DISPACK_MAGIC into the
// file header. So, when decoding, that is detected and E8E9 decode
// is applied.
memcpy(*out_buf, inData, len);
if (Forward_E89(*out_buf, len) != 0)
return (0);
return (len);
} }
size_t size_t
@ -131,6 +157,9 @@ dispack_filter_decode(uchar_t *inData, size_t len, uchar_t **out_buf)
if (*out_buf == NULL) if (*out_buf == NULL)
return (0); return (0);
if (memcmp(hdr->magic, DISPACK_MAGIC, strlen(DISPACK_MAGIC)) != 0)
goto dec_E89;
decoded = *out_buf; decoded = *out_buf;
memcpy(decoded, before, hdr->SizeBefore); memcpy(decoded, before, hdr->SizeBefore);
decoded += hdr->SizeBefore; decoded += hdr->SizeBefore;
@ -144,6 +173,11 @@ dispack_filter_decode(uchar_t *inData, size_t len, uchar_t **out_buf)
decoded += hdr->SizeAfter; decoded += hdr->SizeAfter;
return (decoded - *out_buf); return (decoded - *out_buf);
dec_E89:
memcpy(*out_buf, inData, len);
if (Inverse_E89(*out_buf, len) == -1)
return (0);
return (len);
} }
#ifdef __cplusplus #ifdef __cplusplus

View file

@ -562,14 +562,7 @@ dispack_filter(struct filter_info *fi, void *filter_private)
log_msg(LOG_ERR, 0, "Failed to read archive data."); log_msg(LOG_ERR, 0, "Failed to read archive data.");
return (FILTER_RETURN_ERROR); return (FILTER_RETURN_ERROR);
} }
mapbuf = sdat->in_buff;
/*
* First 8 bytes in the data is the compressed size of the entry.
* LibArchive always zero-pads entries to their original size so
* we need to separately store the compressed size.
*/
in_size = LE64(U64_P(sdat->in_buff));
mapbuf = sdat->in_buff + 8;
/* /*
* No check for supported EXE types needed here since supported * No check for supported EXE types needed here since supported
@ -584,7 +577,7 @@ dispack_filter(struct filter_info *fi, void *filter_private)
if (fi->compressing) { if (fi->compressing) {
out = NULL; out = NULL;
len = dispack_filter_encode(mapbuf, len, &out); len = dispack_filter_encode(mapbuf, len, &out);
if (len == 0 || len >= (len1 - 8)) { if (len == 0) {
munmap(mapbuf, len1); munmap(mapbuf, len1);
free(out); free(out);
return (FILTER_RETURN_SKIP); return (FILTER_RETURN_SKIP);
@ -594,7 +587,8 @@ dispack_filter(struct filter_info *fi, void *filter_private)
fi->fout->output_type = FILTER_OUTPUT_MEM; fi->fout->output_type = FILTER_OUTPUT_MEM;
fi->fout->out = out; fi->fout->out = out;
fi->fout->out_size = len; fi->fout->out_size = len;
fi->fout->hdr.in_size = LE64(len1); fi->fout->hdr_valid = 0;
*(fi->type_ptr) = TYPE_UNKNOWN;
return (ARCHIVE_OK); return (ARCHIVE_OK);
} }

View file

@ -71,6 +71,7 @@ typedef struct _filter_output {
uint8_t *out; uint8_t *out;
size_t out_size; size_t out_size;
int out_fd; int out_fd;
int hdr_valid;
filter_std_hdr_t hdr; filter_std_hdr_t hdr;
} filter_output_t; } filter_output_t;

View file

@ -1028,6 +1028,7 @@ process_by_filter(int fd, int *typ, struct archive *target_arc,
struct filter_info fi; struct filter_info fi;
int64_t wrtn; int64_t wrtn;
fout->hdr_valid = 1;
fi.source_arc = source_arc; fi.source_arc = source_arc;
fi.target_arc = target_arc; fi.target_arc = target_arc;
fi.entry = entry; fi.entry = entry;
@ -1100,7 +1101,6 @@ copy_file_data(pc_ctx_t *pctx, struct archive *arc, struct archive_entry *entry,
&fout, 1, pctx->level); &fout, 1, pctx->level);
if (rv != FILTER_RETURN_SKIP && if (rv != FILTER_RETURN_SKIP &&
rv != FILTER_RETURN_ERROR) { rv != FILTER_RETURN_ERROR) {
pctx->ctype = TYPE_UNKNOWN; // Force analyzer on filter output
if (fout.output_type == FILTER_OUTPUT_MEM) { if (fout.output_type == FILTER_OUTPUT_MEM) {
archive_entry_xattr_add_entry(entry, FILTER_XATTR_ENTRY, archive_entry_xattr_add_entry(entry, FILTER_XATTR_ENTRY,
fname, strlen(fname)); fname, strlen(fname));
@ -1108,10 +1108,12 @@ copy_file_data(pc_ctx_t *pctx, struct archive *arc, struct archive_entry *entry,
close(fd); close(fd);
return (-1); return (-1);
} }
if (fout.hdr_valid) {
rv = archive_write_data(arc, &(fout.hdr), rv = archive_write_data(arc, &(fout.hdr),
sizeof (fout.hdr)); sizeof (fout.hdr));
if (rv != sizeof (fout.hdr)) if (rv != sizeof (fout.hdr))
return (rv); return (rv);
}
rv = archive_write_data(arc, fout.out, rv = archive_write_data(arc, fout.out,
fout.out_size); fout.out_size);
free(fout.out); free(fout.out);
@ -1177,7 +1179,6 @@ do_map:
&fout, 1, pctx->level); &fout, 1, pctx->level);
if (rv != FILTER_RETURN_SKIP && if (rv != FILTER_RETURN_SKIP &&
rv != FILTER_RETURN_ERROR) { rv != FILTER_RETURN_ERROR) {
pctx->ctype = TYPE_UNKNOWN; // Force analyzer on filter output
if (fout.output_type == FILTER_OUTPUT_MEM) { if (fout.output_type == FILTER_OUTPUT_MEM) {
archive_entry_xattr_add_entry(entry, archive_entry_xattr_add_entry(entry,
FILTER_XATTR_ENTRY, FILTER_XATTR_ENTRY,
@ -1186,10 +1187,12 @@ do_map:
close(fd); close(fd);
return (-1); return (-1);
} }
if (fout.hdr_valid) {
rv = archive_write_data(arc, &(fout.hdr), rv = archive_write_data(arc, &(fout.hdr),
sizeof (fout.hdr)); sizeof (fout.hdr));
if (rv != sizeof (fout.hdr)) if (rv != sizeof (fout.hdr))
return (rv); return (rv);
}
rv = archive_write_data(arc, fout.out, rv = archive_write_data(arc, fout.out,
fout.out_size); fout.out_size);
free(fout.out); free(fout.out);

1
config
View file

@ -262,6 +262,7 @@ then
salsa20_stream_c='\$\(XSALSA20_STREAM_C\)' salsa20_stream_c='\$\(XSALSA20_STREAM_C\)'
salsa20_stream_asm= salsa20_stream_asm=
salsa20_debug='\$\(XSALSA20_DEBUG\)' salsa20_debug='\$\(XSALSA20_DEBUG\)'
extra_opt_flags="${extra_opt_flags} -fno-inline"
else else
typ="RELEASE" typ="RELEASE"
fi fi

View file

@ -429,12 +429,17 @@ struct DisFilterCtx
Buffer[i].ResetBuffer(); Buffer[i].ResetBuffer();
} }
sInt DetectJumpTable(sU8 *instr,sU32 addr) sInt DetectJumpTable(sU8 *instr, sU32 addr, sU8 *srcEnd)
{ {
assert(addr < CodeEnd); assert(addr < CodeEnd);
sInt nMax = (CodeEnd - addr) / 4; sInt nMax = (CodeEnd - addr) / 4;
sInt count = 0; sInt count = 0;
// Check for overflow
if (instr + (nMax + 1) * 4 > srcEnd) {
return (0);
}
while(count<nMax) while(count<nMax)
{ {
sU32 codedAddr = Load32(instr + count*4); sU32 codedAddr = Load32(instr + count*4);
@ -450,9 +455,10 @@ struct DisFilterCtx
return count; return count;
} }
sInt ProcessInstr(sU8 *instr,sU32 memory) sInt ProcessInstr(sU8 *instr, sU32 memory, sU8 *srcEnd)
{ {
if(sInt nJump = DetectJumpTable(instr,memory))
if(sInt nJump = DetectJumpTable(instr, memory, srcEnd))
{ {
// probable jump table with nJump entries // probable jump table with nJump entries
sInt remaining = nJump; sInt remaining = nJump;
@ -640,13 +646,16 @@ struct DisFilterCtx
sU8 * sU8 *
DisFilter(sU8 *src, sU32 size, sU32 origin, sU8 *dst, sU32 &outputSize) DisFilter(sU8 *src, sU32 size, sU32 origin, sU8 *dst, sU32 &outputSize)
{ {
sU8 *srcEnd = src + size;
DisFilterCtx ctx(origin,origin+size); DisFilterCtx ctx(origin,origin+size);
if (size < MAXINSTR)
return (NULL);
// main loop: handle everything but the last few bytes // main loop: handle everything but the last few bytes
sU32 pos = 0; sU32 pos = 0;
while(pos < size - MAXINSTR) while(pos < size - MAXINSTR)
{ {
sInt bytes = ctx.ProcessInstr(src + pos,origin + pos); sInt bytes = ctx.ProcessInstr(src + pos, origin + pos, srcEnd);
pos += bytes; pos += bytes;
} }
@ -666,7 +675,7 @@ DisFilter(sU8 *src, sU32 size, sU32 origin, sU8 *dst, sU32 &outputSize)
checkpt[i] = ctx.Buffer[i].Size; checkpt[i] = ctx.Buffer[i].Size;
// process the instruction // process the instruction
sInt bytes = ctx.ProcessInstr(instrBuf,origin + pos); sInt bytes = ctx.ProcessInstr(instrBuf, origin + pos, srcEnd);
if(pos + bytes <= size) // valid instruction if(pos + bytes <= size) // valid instruction
pos += bytes; pos += bytes;

View file

@ -237,7 +237,7 @@ preproc_compress(pc_ctx_t *pctx, compress_func_ptr cmp_func, void *src, uint64_t
*/ */
if (pctx->exe_preprocess) { if (pctx->exe_preprocess) {
if (stype == TYPE_EXE32 || stype == TYPE_EXE64 || if (stype == TYPE_EXE32 || stype == TYPE_EXE64 ||
stype == TYPE_ARCHIVE_AR) { stype == TYPE_ARCHIVE_AR || stype == TYPE_EXE32_PE) {
_dstlen = fromlen; _dstlen = fromlen;
memcpy(to, from, fromlen); memcpy(to, from, fromlen);
if (Forward_E89(to, fromlen) == 0) { if (Forward_E89(to, fromlen) == 0) {

View file

@ -305,7 +305,7 @@ struct cmp_data {
mac_ctx_t chunk_hmac; mac_ctx_t chunk_hmac;
algo_props_t *props; algo_props_t *props;
int decompressing; int decompressing;
uchar_t btype; int btype;
pc_ctx_t *pctx; pc_ctx_t *pctx;
}; };