From 4c639409022bb02270fb70be5623b9614361db1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Sat, 2 May 2020 22:22:31 +0200 Subject: [PATCH] Add bug hunting test case for CVE-2019-7156 --- lib/exprengine.cpp | 2 + .../cve/CVE-2019-7156/expected.txt | 4 + test/bug-hunting/cve/CVE-2019-7156/ole.c | 605 ++++++++++++++++++ 3 files changed, 611 insertions(+) create mode 100644 test/bug-hunting/cve/CVE-2019-7156/expected.txt create mode 100644 test/bug-hunting/cve/CVE-2019-7156/ole.c diff --git a/lib/exprengine.cpp b/lib/exprengine.cpp index f5771cc1b..65aff465c 100644 --- a/lib/exprengine.cpp +++ b/lib/exprengine.cpp @@ -1593,6 +1593,8 @@ static ExprEngine::ValuePtr executeDot(const Token *tok, Data &data) } if (!structValue) { auto v = getValueRangeFromValueType(data.getNewSymbolName(), tok->valueType(), *data.settings); + if (!v) + v = std::make_shared(); call(data.callbacks, tok, v, &data); return v; } diff --git a/test/bug-hunting/cve/CVE-2019-7156/expected.txt b/test/bug-hunting/cve/CVE-2019-7156/expected.txt new file mode 100644 index 000000000..ee1ba9430 --- /dev/null +++ b/test/bug-hunting/cve/CVE-2019-7156/expected.txt @@ -0,0 +1,4 @@ +ole.c:398:bughuntingDivByZero +ole.c:399:bughuntingDivByZero +ole.c:400:bughuntingDivByZero + diff --git a/test/bug-hunting/cve/CVE-2019-7156/ole.c b/test/bug-hunting/cve/CVE-2019-7156/ole.c new file mode 100644 index 000000000..a760b3079 --- /dev/null +++ b/test/bug-hunting/cve/CVE-2019-7156/ole.c @@ -0,0 +1,605 @@ +/** + * @file ole.c + * @author Alex Ott, Victor B Wagner + * @date Wed Jun 11 12:33:01 2003 + * Version: $Id: ole.c,v 1.2 2006/02/25 15:28:14 vitus Exp $ + * Copyright: Victor B Wagner, 1996-2003 Alex Ott, 2003 + * + * @brief Parsing structure of MS Office compound document + * + * This file is part of catdoc project + * and distributed under GNU Public License + * + */ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include "catdoc.h" + +#define min(a,b) ((a) < (b) ? (a) : (b)) + +const static unsigned char ole_sign[]={0xD0,0xCF,0x11,0xE0,0xA1,0xB1,0x1A,0xE1,0}; + + +/** + * Initializes ole structure + * + * @param f (FILE *) compound document file, positioned at bufSize + * byte. Might be pipe or socket + * @param buffer (void *) bytes already read from f + * @param bufSize number of bytes already read from f should be less + * than 512 + * + * @return + */ +FILE* ole_init(FILE *f, void *buffer, size_t bufSize, struct ole_params_t *ole_params) { + unsigned char oleBuf[BBD_BLOCK_SIZE]; + unsigned char *tmpBuf; + FILE *newfile; + int ret=0, i; + long int sbdMaxLen, sbdCurrent, propMaxLen, propCurrent, mblock, msat_size; + oleEntry *tEntry; + long int sectorSize; + long int shortSectorSize; + long int bbdNumBlocks; + + /* deleting old data (if it was allocated) */ + ole_finish(ole_params); + + if (fseek(f,0,SEEK_SET) == -1) { + if ( errno == ESPIPE ) { + /* We got non-seekable file, create temp file */ + if((newfile=tmpfile()) == NULL) { + return NULL; + } + if (bufSize > 0) { + ret=fwrite(buffer, 1, bufSize, newfile); + if(ret != bufSize) { + return NULL; + } + } + + while(!feof(f)){ + ret=fread(oleBuf,1,BBD_BLOCK_SIZE,f); + fwrite(oleBuf, 1, ret, newfile); + } + fseek(newfile,0,SEEK_SET); + } else { + return NULL; + } + } else { + newfile=f; + } + fseek(newfile,0,SEEK_END); + ole_params->fileLength=ftell(newfile); + + fseek(newfile,0,SEEK_SET); + ret=fread(oleBuf,1,BBD_BLOCK_SIZE,newfile); + if ( ret != BBD_BLOCK_SIZE ) { + return NULL; + } + if (strncmp(oleBuf,ole_sign,8) != 0) { + return NULL; + } + ole_params->sectorSize = 1<sectorSize == 0) { + return NULL; + } + sectorSize = ole_params->sectorSize; + ole_params->shortSectorSize = 1<shortSectorSize; + if (shortSectorSize > sectorSize) { + return NULL; + } +/* Read BBD into memory */ + ole_params->bbdNumBlocks = getulong(oleBuf,0x2c); + bbdNumBlocks = ole_params->bbdNumBlocks; + if((ole_params->BBD=malloc(bbdNumBlocks*sectorSize)) == NULL ) { + return NULL; + } + + if((tmpBuf=malloc(MSAT_ORIG_SIZE)) == NULL ) { + return NULL; + } + memcpy(tmpBuf,oleBuf+0x4c,MSAT_ORIG_SIZE); + mblock=getlong(oleBuf,0x44); + msat_size=getlong(oleBuf,0x48); + +/* fprintf(stderr, "msat_size=%ld\n", msat_size); */ + + i=0; + while((mblock >= 0) && (i < msat_size)) { + unsigned char *newbuf; +/* fprintf(stderr, "i=%d mblock=%ld\n", i, mblock); */ + if ((newbuf=realloc(tmpBuf, sectorSize*(i+1)+MSAT_ORIG_SIZE)) != NULL) { + tmpBuf=newbuf; + } else { + free(tmpBuf); + ole_finish(ole_params); + return NULL; + } + + fseek(newfile, 512+mblock*sectorSize, SEEK_SET); + if(fread(tmpBuf+MSAT_ORIG_SIZE+(sectorSize-4)*i, + 1, sectorSize, newfile) != sectorSize) { + ole_finish(ole_params); + return NULL; + } + + i++; + mblock=getlong(tmpBuf, MSAT_ORIG_SIZE+(sectorSize-4)*i); + } + +/* fprintf(stderr, "bbdNumBlocks=%ld\n", bbdNumBlocks); */ + for(i=0; i< bbdNumBlocks; i++) { + long int bbdSector=getlong(tmpBuf,4*i); + + if (bbdSector >= ole_params->fileLength/sectorSize || bbdSector < 0) { + errno = EINVAL; + ole_finish(ole_params); + return NULL; + } + fseek(newfile, 512+bbdSector*sectorSize, SEEK_SET); + if ( fread(ole_params->BBD+i*sectorSize, 1, sectorSize, newfile) != sectorSize ) { + free(tmpBuf); + ole_finish(ole_params); + return NULL; + } + } + free(tmpBuf); + +/* Read SBD into memory */ + ole_params->sbdLen=0; + sbdMaxLen=10; + sbdCurrent = ole_params->sbdStart = getlong(oleBuf,0x3c); + if (ole_params->sbdStart > 0) { + if((ole_params->SBD=malloc(sectorSize*sbdMaxLen)) == NULL ) { + ole_finish(ole_params); + return NULL; + } + while(1) { + fseek(newfile, 512+sbdCurrent*sectorSize, SEEK_SET); + fread(ole_params->SBD+ole_params->sbdLen*sectorSize, 1, sectorSize, newfile); + ole_params->sbdLen++; + if (ole_params->sbdLen >= sbdMaxLen) { + unsigned char *newSBD; + + sbdMaxLen+=5; + if ((newSBD=realloc(ole_params->SBD, sectorSize*sbdMaxLen)) != NULL) { + ole_params->SBD=newSBD; + } else { + ole_finish(ole_params); + return NULL; + } + } + if (sbdCurrent < 0 || sbdCurrent * 4 >= bbdNumBlocks * sectorSize) + { + break; + } + sbdCurrent = getlong(ole_params->BBD, sbdCurrent*4); + if(sbdCurrent < 0 || + sbdCurrent >= ole_params->fileLength/sectorSize) + break; + } + ole_params->sbdNumber = (ole_params->sbdLen*sectorSize)/shortSectorSize; + } else { + ole_params->SBD=NULL; + } +/* Read property catalog into memory */ + ole_params->propLen = 0; + propMaxLen = 5; + propCurrent = ole_params->propStart = getlong(oleBuf,0x30); + if (ole_params->propStart >= 0) { + if((ole_params->properties=malloc(propMaxLen*sectorSize)) == NULL ) { + ole_finish(ole_params); + return NULL; + } + while(1) { +/* fprintf(stderr, "propCurrent=%ld\n",propCurrent); */ + fseek(newfile, 512+propCurrent*sectorSize, SEEK_SET); + fread(ole_params->properties+ole_params->propLen*sectorSize, + 1, sectorSize, newfile); + (ole_params->propLen)++; + if (ole_params->propLen >= propMaxLen) { + unsigned char *newProp; + + propMaxLen+=5; + if ((newProp=realloc(ole_params->properties, propMaxLen*sectorSize)) != NULL) + ole_params->properties=newProp; + else { + ole_finish(ole_params); + return NULL; + } + } + + propCurrent = getlong(ole_params->BBD, propCurrent*4); + if(propCurrent < 0 || + propCurrent >= ole_params->fileLength/sectorSize ) { + break; + } + } + + ole_params->propNumber = (ole_params->propLen*sectorSize)/PROP_BLOCK_SIZE; + ole_params->propCurNumber = 0; + } else { + ole_finish(ole_params); + ole_params->properties = NULL; + return NULL; + } + + +/* Find Root Entry */ + while((tEntry=(oleEntry*)ole_readdir(newfile, ole_params)) != NULL) { + if (tEntry->type == oleRootDir ) { + ole_params->rootEntry=tEntry; + break; + } + ole_close((FILE*)tEntry); + } + ole_params->propCurNumber = 0; + fseek(newfile, 0, SEEK_SET); + if (!ole_params->rootEntry) { + errno = EINVAL; + ole_finish(ole_params); + return NULL; + } + return newfile; +} + +/** + * + * + * @param oleBuf + * + * @return + */ +int rightOleType(unsigned char *oleBuf) { + return (oleBuf[0x42] == 1 || oleBuf[0x42] == 2 || + oleBuf[0x42] == 3 || oleBuf[0x42] == 5 ); +} + +/** + * + * + * @param oleBuf + * + * @return + */ +oleType getOleType(unsigned char *oleBuf) { + return (oleType)((unsigned char)oleBuf[0x42]); +} + +/** + * Reads next directory entry from file + * + * @param name buffer for name converted to us-ascii should be at least 33 chars long + * @param size size of file + * + * @return 0 if everything is ok -1 on error + */ +FILE *ole_readdir(FILE *f, struct ole_params_t *ole_params) { + int i, nLen; + unsigned char *oleBuf; + oleEntry *e=NULL; + long int chainMaxLen, chainCurrent; + + if ( ole_params->properties == NULL || ole_params->propCurNumber >= ole_params->propNumber || f == NULL ) + return NULL; + oleBuf=ole_params->properties + ole_params->propCurNumber*PROP_BLOCK_SIZE; + if( !rightOleType(oleBuf)) + return NULL; + if ((e = (oleEntry*)malloc(sizeof(oleEntry))) == NULL) { + return NULL; + } + e->dirPos=oleBuf; + e->type=getOleType(oleBuf); + e->file=f; + e->startBlock=getlong(oleBuf,0x74); + e->blocks=NULL; + + nLen=getshort(oleBuf,0x40); + for (i=0 ; i < nLen/2 && i < OLENAMELENGHT; i++) + e->name[i]=(char)oleBuf[i*2]; + e->name[i]='\0'; + (ole_params->propCurNumber)++; + e->length=getulong(oleBuf,0x78); +/* Read sector chain for object */ + chainMaxLen = 25; + e->numOfBlocks = 0; + chainCurrent = e->startBlock; + e->isBigBlock = (e->length >= 0x1000) || !strcmp(e->name, "Root Entry"); +/* fprintf(stderr, "e->name=%s e->length=%ld\n", e->name, e->length); */ +/* fprintf(stderr, "e->startBlock=%ld BBD=%p\n", e->startBlock, BBD); */ + if (e->startBlock >= 0 && + e->length >= 0 && + (e->startBlock <= + ole_params->fileLength/(e->isBigBlock ? ole_params->sectorSize : ole_params->shortSectorSize))) { + if((e->blocks=malloc(chainMaxLen*sizeof(long int))) == NULL ) { + return NULL; + } + while(1) { +/* fprintf(stderr, "chainCurrent=%ld\n", chainCurrent); */ + e->blocks[e->numOfBlocks++] = chainCurrent; + if (e->numOfBlocks >= chainMaxLen) { + long int *newChain; + chainMaxLen+=25; + if ((newChain=realloc(e->blocks, + chainMaxLen*sizeof(long int))) != NULL) + e->blocks=newChain; + else { + free(e->blocks); + e->blocks=NULL; + return NULL; + } + } + if ( e->isBigBlock ) { + chainCurrent = getlong(ole_params->BBD, chainCurrent*4); + } else if ( ole_params->SBD != NULL ) { + chainCurrent = getlong(ole_params->SBD, chainCurrent*4); + } else { + chainCurrent=-1; + } + if(chainCurrent <= 0 || + chainCurrent >= ( e->isBigBlock ? + ((ole_params->bbdNumBlocks*ole_params->sectorSize)/4) + : ((ole_params->sbdNumber*ole_params->shortSectorSize)/4) ) || + (e->numOfBlocks > + e->length/(e->isBigBlock ? ole_params->sectorSize : ole_params->shortSectorSize))) { +/* fprintf(stderr, "chain End=%ld\n", chainCurrent); */ + break; + } + } + } + + if(e->length > (e->isBigBlock ? ole_params->sectorSize : ole_params->shortSectorSize)*e->numOfBlocks) + e->length = (e->isBigBlock ? ole_params->sectorSize : ole_params->shortSectorSize)*e->numOfBlocks; +/* fprintf(stderr, "READDIR: e->name=%s e->numOfBlocks=%ld length=%ld\n", */ +/* e->name, e->numOfBlocks, e->length); */ + + return (FILE*)e; +} + +/** + * Open stream, which correspond to directory entry last read by + * ole_readdir + * + * + * @return opaque pointer to pass to ole_read, casted to (FILE *) + */ +int ole_open(FILE *stream) { + oleEntry *e=(oleEntry *)stream; + if ( e->type != oleStream) + return -2; + + e->ole_offset=0; + e->file_offset= ftell(e->file); + return 0; +} + +/** + * + * + * @param e + * @param blk + * + * @return + */ +long int calcFileBlockOffset(oleEntry *e, long int blk, struct ole_params_t *ole_params) { + long int res; + if ( e->isBigBlock ) { + res=512+e->blocks[blk]*ole_params->sectorSize; + } else { + long int sbdPerSector=(ole_params->sectorSize)/(ole_params->shortSectorSize); + long int sbdSecNum=e->blocks[blk]/sbdPerSector; + long int sbdSecMod=e->blocks[blk]%sbdPerSector; + + res=512 + ole_params->rootEntry->blocks[sbdSecNum]*ole_params->sectorSize + sbdSecMod*ole_params->shortSectorSize; + } + return res; +} + + +/** + * Reads block from open ole stream interface-compatible with fread + * + * @param ptr pointer to buffer for read to + * @param size size of block + * @param nmemb size in blocks + * @param stream pointer to FILE* structure + * + * @return number of readed blocks + */ +size_t ole_read(void *ptr, size_t size, size_t nmemb, FILE *stream, struct ole_params_t *ole_params) { + oleEntry *e = (oleEntry*)stream; + long int llen = size*nmemb, rread=0, i; + long int blockNumber, modBlock, toReadBlocks, toReadBytes, bytesInBlock; + long int ssize; /**< Size of block */ + long int newoffset; + unsigned char *cptr = ptr; + if( e->ole_offset+llen > e->length ) + llen= e->length - e->ole_offset; + + ssize = (e->isBigBlock ? ole_params->sectorSize : ole_params->shortSectorSize); + blockNumber=e->ole_offset/ssize; +/* fprintf(stderr, "blockNumber=%ld e->numOfBlocks=%ld llen=%ld\n", */ +/* blockNumber, e->numOfBlocks, llen); */ + if ( blockNumber >= e->numOfBlocks || llen <=0 ) + return 0; + + modBlock=e->ole_offset%ssize; + bytesInBlock = ssize - modBlock; + if(bytesInBlock < llen) { + toReadBlocks = (llen-bytesInBlock)/ssize; + toReadBytes = (llen-bytesInBlock)%ssize; + } else { + toReadBlocks = toReadBytes = 0; + } +/* fprintf(stderr, "llen=%ld toReadBlocks=%ld toReadBytes=%ld bytesInBlock=%ld blockNumber=%ld modBlock=%ld\n", */ +/* llen, toReadBlocks, toReadBytes, bytesInBlock, blockNumber, modBlock); */ + newoffset = calcFileBlockOffset(e,blockNumber, ole_params)+modBlock; + if (e->file_offset != newoffset) { + fseek(e->file, e->file_offset=newoffset, SEEK_SET); + } + rread=fread(ptr, 1, min(llen,bytesInBlock), e->file); + e->file_offset += rread; + for(i=0; ifile_offset); + fseek(e->file, e->file_offset=newoffset , SEEK_SET); + readbytes=fread(cptr+rread, 1, min(llen-rread, ssize), e->file); + rread +=readbytes; + e->file_offset +=readbytes; + } + if(toReadBytes > 0) { + int readbytes; + blockNumber++; + newoffset = calcFileBlockOffset(e,blockNumber, ole_params); + fseek(e->file, e->file_offset=newoffset, SEEK_SET); + readbytes=fread(cptr+rread, 1, toReadBytes,e ->file); + rread +=readbytes; + e->file_offset +=readbytes; + } +/* fprintf(stderr, "ole_offset=%ld rread=%ld llen=%ld\n", + e->ole_offset, rread, llen);*/ + e->ole_offset+=rread; + return rread; +} + +/** + * + * + * @param stream + * + * @return + */ +int ole_eof(FILE *stream) { + oleEntry *e=(oleEntry*)stream; +/* fprintf(stderr, "EOF: e->ole_offset=%ld e->length=%ld\n", + e->ole_offset, e->length);*/ + return (e->ole_offset >= e->length); +} + +/** + * + * + */ +void ole_finish(struct ole_params_t *ole_params) { + if ( ole_params->BBD != NULL ) free(ole_params->BBD); + if ( ole_params->SBD != NULL ) free(ole_params->SBD); + if ( ole_params->properties != NULL ) free(ole_params->properties); + if ( ole_params->rootEntry != NULL ) ole_close((FILE*)(ole_params->rootEntry)); + ole_params->properties = ole_params->SBD = ole_params->BBD = NULL; + ole_params->rootEntry = NULL; +} + +/** + * + * + * @param stream + * + * @return + */ +int ole_close(FILE *stream) { + oleEntry *e=(oleEntry*)stream; + if(e == NULL) + return -1; + if (e->blocks != NULL) + free(e->blocks); + free(e); + return 0; +} + +/** + * + * + * @param stream pointer to OLE stream structure + * @param offset + * @param whence + * + * @return + */ +int ole_seek(FILE *stream, long offset, int whence, struct ole_params_t *ole_params) { + oleEntry *e=(oleEntry*)stream; + long int new_ole_offset=0, new_file_offset; + int ssize, modBlock, blockNumber; + + switch(whence) { + case SEEK_SET: + new_ole_offset=offset; + break; + + case SEEK_CUR: + new_ole_offset=e->ole_offset+offset; + break; + + case SEEK_END: + new_ole_offset=e->length+offset; + break; + + default: + errno=EINVAL; + return -1; + } + if(new_ole_offset<0) + new_ole_offset=0; + if(new_ole_offset >= e->length) + new_ole_offset=e->length; + + ssize = (e->isBigBlock ? ole_params->sectorSize : ole_params->shortSectorSize); + blockNumber=new_ole_offset/ssize; + if ( blockNumber >= e->numOfBlocks ) + return -1; + + modBlock=new_ole_offset%ssize; + new_file_offset = calcFileBlockOffset(e,blockNumber, ole_params)+modBlock; + fseek(e->file, e->file_offset=new_file_offset, SEEK_SET); + e->ole_offset=new_ole_offset; + + return 0; +} + +/** + * Tell position inside OLE stream + * + * @param stream pointer to OLE stream + * + * @return current position inside OLE stream + */ +long ole_tell(FILE *stream) { + oleEntry *e=(oleEntry*)stream; + return e->ole_offset; +} + + +void set_ole_func(struct io_funcs_t *io_funcs) { + io_funcs->catdoc_read=ole_read; + io_funcs->catdoc_eof=ole_eof; + io_funcs->catdoc_seek=ole_seek; + io_funcs->catdoc_tell=ole_tell; +} + + +size_t my_fread(void *ptr, size_t size, size_t nmemb, FILE *stream, struct ole_params_t *ole_params) +{ + return fread(ptr, size, nmemb, stream); +} + +int my_fseek(FILE *stream, long offset, int whence, struct ole_params_t *ole_params) +{ + return fseek(stream, offset, whence); +} + +void set_std_func(struct io_funcs_t *io_funcs) { + io_funcs->catdoc_read=my_fread; + io_funcs->catdoc_eof=feof; + io_funcs->catdoc_seek=my_fseek; + io_funcs->catdoc_tell=ftell; +}