Fixed (some) segmentation faults when parsing broken OLE structure
authorVictor Wagner <vitus@wagner.pp.ru>
Tue, 26 Apr 2016 17:11:16 +0000 (20:11 +0300)
committerVictor Wagner <vitus@wagner.pp.ru>
Tue, 26 Apr 2016 17:11:16 +0000 (20:11 +0300)
src/analyze.c
src/ole.c
src/ole.h

index 1ac927ed722f057dcb96a958ba1282eac09e284a..6ed7ea60844b8960dc57d3b99d837c3f5c343d9a 100644 (file)
@@ -70,7 +70,7 @@ int analyze_format(FILE *f) {
                        set_std_func();
                        ole_finish();
                } else {
-                       fprintf(stderr,"Broken OLE file. Try using -b switch");
+                       fprintf(stderr,"Broken OLE file. Try using -b switch\n");
                        exit(1);
                }       
        } else {
index 805d8c716c9694bba2a4afc7c7045bb00953a0e4..1e26dd797a190d98712ddce6d5a1927471bfae3c 100644 (file)
--- a/src/ole.c
+++ b/src/ole.c
@@ -4,12 +4,12 @@
  * @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 <config.h>
@@ -40,28 +40,29 @@ long int fileLength=0;
 char ole_sign[]={0xD0,0xCF,0x11,0xE0,0xA1,0xB1,0x1A,0xE1,0};
 char zip_sign[]="PK\003\004";
 
-/** 
+/**
  * Initializes ole structure
- * 
+ *
  * @param f (FILE *) compound document file, positioned at bufSize
- *           byte. Might be pipe or socket 
+ *           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 
+ *                than 512
+ *
+ * @return
  */
 FILE* ole_init(FILE *f, void *buffer, size_t bufSize)  {
        unsigned char oleBuf[BBD_BLOCK_SIZE];
        unsigned char *tmpBuf;
        FILE *newfile;
        int ret=0, i;
+       long int bbdSize;
        long int sbdMaxLen, sbdCurrent, propMaxLen, propCurrent, mblock, msat_size;
        oleEntry *tEntry;
 
        /* deleting old data (if it was allocated) */
        ole_finish();
-       
+
        if (fseek(f,0,SEEK_SET) == -1) {
                if ( errno == ESPIPE ) {
                        /* We got non-seekable file, create temp file */
@@ -76,7 +77,7 @@ FILE* ole_init(FILE *f, void *buffer, size_t bufSize)  {
                                        return NULL;
                                }
                        }
-                       
+
                        while(!feof(f)){
                                ret=fread(oleBuf,1,BBD_BLOCK_SIZE,f);
                                fwrite(oleBuf, 1, ret, newfile);
@@ -88,7 +89,7 @@ FILE* ole_init(FILE *f, void *buffer, size_t bufSize)  {
                }
        } else {
                newfile=f;
-       }       
+       }
        fseek(newfile,0,SEEK_END);
        fileLength=ftell(newfile);
 /*     fprintf(stderr, "fileLength=%ld\n", fileLength); */
@@ -105,20 +106,26 @@ FILE* ole_init(FILE *f, void *buffer, size_t bufSize)  {
        }
        sectorSize = 1<<getshort(oleBuf,0x1e);
        shortSectorSize=1<<getshort(oleBuf,0x20);
-       
+
 /* Read BBD into memory */
        bbdNumBlocks = getulong(oleBuf,0x2c);
+       bbdSize = bbdNumBlocks * sectorSize;
+       if (bbdSize > fileLength) {
+               /* broken file, BBD size greater than entire file*/
+               return NULL;
+       }
+
        if((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);
-
+       if (msat_size * sectorSize)
 /*     fprintf(stderr, "msat_size=%ld\n", msat_size); */
 
        i=0;
@@ -133,7 +140,7 @@ FILE* ole_init(FILE *f, void *buffer, size_t bufSize)  {
                        ole_finish();
                        return NULL;
                }
-               
+
                fseek(newfile, 512+mblock*sectorSize, SEEK_SET);
                if(fread(tmpBuf+MSAT_ORIG_SIZE+(sectorSize-4)*i,
                                                 1, sectorSize, newfile) != sectorSize) {
@@ -145,11 +152,11 @@ FILE* ole_init(FILE *f, void *buffer, size_t bufSize)  {
                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 >= fileLength/sectorSize || bbdSector < 0) {
                        fprintf(stderr, "Bad BBD entry!\n");
                        ole_finish();
@@ -164,7 +171,7 @@ FILE* ole_init(FILE *f, void *buffer, size_t bufSize)  {
                }
        }
        free(tmpBuf);
-       
+
 /* Read SBD into memory */
        sbdLen=0;
        sbdMaxLen=10;
@@ -180,7 +187,7 @@ FILE* ole_init(FILE *f, void *buffer, size_t bufSize)  {
                        sbdLen++;
                        if (sbdLen >= sbdMaxLen) {
                                unsigned char *newSBD;
-                               
+
                                sbdMaxLen+=5;
                                if ((newSBD=realloc(SBD, sectorSize*sbdMaxLen)) != NULL) {
                                        SBD=newSBD;
@@ -190,6 +197,10 @@ FILE* ole_init(FILE *f, void *buffer, size_t bufSize)  {
                                        return NULL;
                                }
                        }
+                       if (sbdCurrent * 4 > bbdSize) {
+                               ole_finish();
+                               return NULL;
+                       }
                        sbdCurrent = getlong(BBD, sbdCurrent*4);
                        if(sbdCurrent < 0 ||
                                sbdCurrent >= fileLength/sectorSize)
@@ -212,12 +223,19 @@ FILE* ole_init(FILE *f, void *buffer, size_t bufSize)  {
                while(1) {
 /*                     fprintf(stderr, "propCurrent=%ld\n",propCurrent); */
                        fseek(newfile, 512+propCurrent*sectorSize, SEEK_SET);
-                       fread(properties+propLen*sectorSize,
-                                 1, sectorSize, newfile);
+                       errno=0;
+                       if (fread(properties+propLen*sectorSize,
+                                 1, sectorSize, newfile)!=sectorSize) {
+                                 if (errno != 0) {
+                                       perror("reading properties catalog");
+                                 }
+                                 ole_finish();
+                                 return NULL;
+                       }
                        propLen++;
                        if (propLen >= propMaxLen) {
                                unsigned char *newProp;
-                               
+
                                propMaxLen+=5;
                                if ((newProp=realloc(properties, propMaxLen*sectorSize)) != NULL)
                                        properties=newProp;
@@ -227,11 +245,11 @@ FILE* ole_init(FILE *f, void *buffer, size_t bufSize)  {
                                        return NULL;
                                }
                        }
-                       
+
                        propCurrent = getlong(BBD, propCurrent*4);
                        if(propCurrent < 0 ||
-                          propCurrent >= fileLength/sectorSize ) {
-                               break;
+                          propCurrent >= bbdSize/4 ) {
+                          break;
                        }
                }
 /*             fprintf(stderr, "propLen=%ld\n",propLen); */
@@ -242,8 +260,8 @@ FILE* ole_init(FILE *f, void *buffer, size_t bufSize)  {
                properties = NULL;
                return NULL;
        }
-       
-       
+
+
 /* Find Root Entry */
        while((tEntry=(oleEntry*)ole_readdir(newfile)) != NULL) {
                if (tEntry->type == oleRootDir ) {
@@ -257,39 +275,39 @@ FILE* ole_init(FILE *f, void *buffer, size_t bufSize)  {
        if (!rootEntry) {
                fprintf(stderr,"Broken OLE structure. Cannot find root entry in this file!\n");         ole_finish();
                return NULL;
-       }       
+       }
        return newfile;
 }
 
-/** 
- * 
- * 
- * @param oleBuf 
- * 
- * @return 
+/**
+ *
+ *
+ * @param oleBuf
+ *
+ * @return
  */
 int rightOleType(unsigned char *oleBuf) {
        return (oleBuf[0x42] == 1 || oleBuf[0x42] == 2 ||
                        oleBuf[0x42] == 3 || oleBuf[0x42] == 5 );
 }
 
-/** 
- * 
- * 
- * @param oleBuf 
- * 
- * @return 
+/**
+ *
+ *
+ * @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 
- * 
+ * @param size size of file
+ *
  * @return 0 if everything is ok -1 on error
  */
 FILE *ole_readdir(FILE *f) {
@@ -297,13 +315,13 @@ FILE *ole_readdir(FILE *f) {
        unsigned char *oleBuf;
        oleEntry *e=NULL;
        long int chainMaxLen, chainCurrent;
-       
+
        if ( properties == NULL || propCurNumber >= propNumber || f == NULL )
                return NULL;
        oleBuf=properties + propCurNumber*PROP_BLOCK_SIZE;
        if( !rightOleType(oleBuf))
                return NULL;
-       if ((e = (oleEntry*)malloc(sizeof(oleEntry))) == NULL) {
+       if ((e = (oleEntry*) calloc(sizeof(oleEntry),1)) == NULL) {
                perror("Can\'t allocate memory");
                return NULL;
        }
@@ -312,8 +330,12 @@ FILE *ole_readdir(FILE *f) {
        e->file=f;
        e->startBlock=getlong(oleBuf,0x74);
        e->blocks=NULL;
-       
+
        nLen=getshort(oleBuf,0x40);
+       if (nLen > OLENAMELENGTH) {
+               free(e);
+               return NULL;
+       }
        for (i=0 ; i < nLen /2; i++)
                e->name[i]=(char)oleBuf[i*2];
        e->name[i]='\0';
@@ -358,9 +380,9 @@ FILE *ole_readdir(FILE *f) {
                                long int *newChain;
                                chainMaxLen+=25;
                                if ((newChain=realloc(e->blocks,
-                                                                         chainMaxLen*sizeof(long int))) != NULL)
+                                                                         chainMaxLen*sizeof(long int))) != NULL) {
                                        e->blocks=newChain;
-                               else {
+                               else {
                                        perror("Properties realloc error");
                                        free(e->blocks);
                                        e->blocks=NULL;
@@ -376,39 +398,39 @@ FILE *ole_readdir(FILE *f) {
                        }
                }
        }
-       
+
        if(e->length > (e->isBigBlock ? sectorSize : shortSectorSize)*e->numOfBlocks)
                e->length = (e->isBigBlock ? sectorSize : 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 
- * 
- * 
+ * 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 
+/**
+ *
+ *
+ * @param e
+ * @param blk
+ *
+ * @return
  */
 long int calcFileBlockOffset(oleEntry *e, long int blk) {
        long int res;
@@ -418,7 +440,7 @@ long int calcFileBlockOffset(oleEntry *e, long int blk) {
                long int sbdPerSector=sectorSize/shortSectorSize;
                long int sbdSecNum=e->blocks[blk]/sbdPerSector;
                long int sbdSecMod=e->blocks[blk]%sbdPerSector;
-/*             fprintf(stderr, "calcoffset: e->name=%s e->numOfBlocks=%ld length=%ld sbdSecNum=%ld rootEntry->blocks=%p\n", 
+/*             fprintf(stderr, "calcoffset: e->name=%s e->numOfBlocks=%ld length=%ld sbdSecNum=%ld rootEntry->blocks=%p\n",
                                                e->name, e->numOfBlocks, e->length, sbdSecNum, rootEntry->blocks);*/
                res=512 + rootEntry->blocks[sbdSecNum]*sectorSize + sbdSecMod*shortSectorSize;
        }
@@ -426,14 +448,14 @@ long int calcFileBlockOffset(oleEntry *e, long int blk) {
 }
 
 
-/** 
+/**
  * 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 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) {
@@ -442,22 +464,22 @@ size_t ole_read(void *ptr, size_t size, size_t nmemb, FILE *stream) {
        long int blockNumber, modBlock, toReadBlocks, toReadBytes, bytesInBlock;
        long int ssize;                         /**< Size of block */
        long int newoffset;
-       unsigned char *cptr = ptr;      
+       unsigned char *cptr = ptr;
        if( e->ole_offset+llen > e->length )
                llen= e->length - e->ole_offset;
-       
+
        ssize = (e->isBigBlock ? sectorSize : 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; 
+               toReadBytes = (llen-bytesInBlock)%ssize;
        } else {
                toReadBlocks = toReadBytes = 0;
        }
@@ -492,14 +514,14 @@ size_t ole_read(void *ptr, size_t size, size_t nmemb, FILE *stream) {
        e->ole_offset, rread, llen);*/
        e->ole_offset+=rread;
        return rread;
-}      
-
-/** 
- * 
- * 
- * @param stream 
- * 
- * @return 
+}
+
+/**
+ *
+ *
+ * @param stream
+ *
+ * @return
  */
 int ole_eof(FILE *stream) {
        oleEntry *e=(oleEntry*)stream;
@@ -508,9 +530,9 @@ int ole_eof(FILE *stream) {
        return (e->ole_offset >=  e->length);
 }
 
-/** 
- * 
- * 
+/**
+ *
+ *
  */
 void ole_finish(void) {
        if ( BBD != NULL ) free(BBD);
@@ -521,12 +543,12 @@ void ole_finish(void) {
        rootEntry = NULL;
 }
 
-/** 
- * 
- * 
- * @param stream 
- * 
- * @return 
+/**
+ *
+ *
+ * @param stream
+ *
+ * @return
  */
 int ole_close(FILE *stream) {
        oleEntry *e=(oleEntry*)stream;
@@ -539,32 +561,32 @@ int ole_close(FILE *stream) {
 }
 
 /**
- * 
- * 
+ *
+ *
  * @param stream pointer to OLE stream structure
- * @param offset 
- * @param whence 
- * 
- * @return 
+ * @param offset
+ * @param whence
+ *
+ * @return
  */
 int ole_seek(FILE *stream, long offset, int whence) {
        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;
@@ -578,20 +600,20 @@ int ole_seek(FILE *stream, long offset, int whence) {
        blockNumber=new_ole_offset/ssize;
        if ( blockNumber >= e->numOfBlocks )
                return -1;
-       
+
        modBlock=new_ole_offset%ssize;
        new_file_offset = calcFileBlockOffset(e,blockNumber)+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) {
@@ -601,8 +623,8 @@ long ole_tell(FILE *stream) {
 
 
 /**
- * 
- * 
+ *
+ *
  */
 size_t (*catdoc_read)(void *ptr, size_t size, size_t nmemb, FILE *stream);
 int (*catdoc_eof)(FILE *stream);
@@ -619,10 +641,10 @@ void set_ole_func(void) {
 #ifdef feof
 /* feof is macro in Turbo C, so we need a real function to assign to
  * pointer
- */ 
+ */
 int my_feof(FILE *f) {
     return feof(f);
-}    
+}
 #define FEOF my_feof
 #else
 #define FEOF feof
index 181c7e7f71e10af020bde31ff557c322be1842cf..b0d6e8eebe0be3cfb413b21902ee970701339907 100644 (file)
--- a/src/ole.h
+++ b/src/ole.h
@@ -16,7 +16,7 @@
 #define BBD_BLOCK_SIZE     512
 #define SBD_BLOCK_SIZE      64
 #define PROP_BLOCK_SIZE    128
-#define OLENAMELENGHT       32
+#define OLENAMELENGTH       32
 #define MSAT_ORIG_SIZE     436
 
 typedef enum {
@@ -28,7 +28,7 @@ typedef enum {
 
 typedef struct {
        FILE *file;
-       char name[OLENAMELENGHT+1];
+       char name[OLENAMELENGTH+1];
        long int startBlock;
        long int curBlock;
        unsigned long int length;