]> git.ipfire.org Git - thirdparty/zstd.git/commitdiff
Added : utility `roundTripCrash`
authorYann Collet <yann.collet.73@gmail.com>
Sat, 28 May 2016 13:30:01 +0000 (15:30 +0200)
committerYann Collet <yann.collet.73@gmail.com>
Sat, 28 May 2016 13:30:01 +0000 (15:30 +0200)
which generates a crash (double-free) on detecting a round-trip corruption

programs/.gitignore
programs/Makefile
programs/roundTripCrash.c [new file with mode: 0644]

index 886af777cedff2c59d40651a86c87e87f17b76c4..87e9e5301397f41ae1a8c069907f7d015a1b121f 100644 (file)
@@ -9,6 +9,7 @@ zbufftest
 zbufftest32
 datagen
 paramgrill
+roundTripCrash
 
 # Object files
 *.o
index c4e19229aecb4beba5f0910e97f43ff45a6616d7..debabfde6064b5f49f155e04d64e9545b64b545b 100644 (file)
@@ -83,7 +83,7 @@ zstd  : $(ZSTD_FILES) $(ZSTD_FILES_LEGACY) $(ZBUFF_FILES) $(ZDICT_FILES) \
        $(CC)      $(FLAGS) -DZSTD_LEGACY_SUPPORT=$(ZSTD_LEGACY_SUPPORT) $^ -o $@$(EXT)
 
 zstd32: $(ZSTD_FILES) $(ZSTD_FILES_LEGACY) $(ZBUFF_FILES) $(ZDICT_FILES) \
-        zstdcli.c fileio.c bench.c xxhash.c datagen.c dibio.c 
+        zstdcli.c fileio.c bench.c xxhash.c datagen.c dibio.c
        $(CC) -m32 $(FLAGS) -DZSTD_LEGACY_SUPPORT=$(ZSTD_LEGACY_SUPPORT) $^ -o $@$(EXT)
 
 zstd_nolegacy :
@@ -109,8 +109,8 @@ zstd-compress: $(ZSTDCOMP_FILES) $(ZSTDDIR)/compress/zbuff_compress.c zstdcli.c
 zstd-decompress: $(ZSTDDECOMP_FILES) $(ZSTDDIR)/decompress/zbuff_decompress.c zstdcli.c fileio.c
        $(CC)      $(FLAGS) -DZSTD_NOBENCH -DZSTD_NODICT -DZSTD_NOCOMPRESS -DZSTD_LEGACY_SUPPORT=0 $^ -o $@$(EXT)
 
-zstd-small: clean 
-       CFLAGS="-Os -s" $(MAKE) zstd-frugal 
+zstd-small: clean
+       CFLAGS="-Os -s" $(MAKE) zstd-frugal
 
 fullbench  : $(ZSTD_FILES) $(ZBUFF_FILES) datagen.c fullbench.c
        $(CC)      $(FLAGS) $^ -o $@$(EXT)
@@ -141,12 +141,16 @@ paramgrill : $(ZSTD_FILES) \
 datagen : datagen.c datagencli.c
        $(CC)      $(FLAGS) $^ -o $@$(EXT)
 
+roundTripCrash  : $(ZSTD_FILES) \
+      roundTripCrash.c
+       $(CC)      $(FLAGS) $^ -o $@$(EXT)
+
 clean:
        @rm -f core *.o tmp* result* *.gcda dictionary *.zst \
         zstd$(EXT) zstd32$(EXT) zstd-compress$(EXT) zstd-decompress$(EXT) \
         fullbench$(EXT) fullbench32$(EXT) \
         fuzzer$(EXT) fuzzer32$(EXT) zbufftest$(EXT) zbufftest32$(EXT) \
-       datagen$(EXT) paramgrill$(EXT)
+        datagen$(EXT) paramgrill$(EXT) roundTripCrash($EXT)
        @echo Cleaning completed
 
 
diff --git a/programs/roundTripCrash.c b/programs/roundTripCrash.c
new file mode 100644 (file)
index 0000000..1b6e1d7
--- /dev/null
@@ -0,0 +1,185 @@
+/*
+  roundTripCrash
+  Copyright (C) Yann Collet 2013-2016
+
+  GPL v2 License
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License along
+  with this program; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+  You can contact the author at :
+  - zstd homepage : http://www.zstd.net
+*/
+/*
+  This program takes a file in input,
+  performs a zstd round-trip test (compression - decompress)
+  compares the result with original
+  and generates a crash (double free) on corruption detection.
+*/
+
+/*===========================================
+*   Dependencies
+*==========================================*/
+#include <stddef.h>     /* size_t */
+#include <stdlib.h>     /* malloc, free, exit */
+#include <stdio.h>      /* fprintf */
+#include <sys/types.h>  /* stat */
+#include <sys/stat.h>   /* stat */
+#include "zstd.h"
+
+/** roundTripTest() :
+*   Compresses `srcBuff` into `compressedBuff`,
+*   then decompresses `compressedBuff` into `resultBuff`.
+*   Compression level used is derived from first content byte.
+*   @return : result of decompression, which should be == `srcSize`
+*          or an error code if either compression or decompression fails.
+*   Note : `compressedBuffCapacity` should be `>= ZSTD_compressBound(srcSize)`
+*          for compression to be guaranteed to work */
+static size_t roundTripTest(void* resultBuff, size_t resultBuffCapacity,
+                            void* compressedBuff, size_t compressedBuffCapacity,
+                      const void* srcBuff, size_t srcBuffSize)
+{
+    static const int maxClevel = 19;
+    int const cLevel = (!srcBuffSize) ? 1 : (*(const unsigned char*)srcBuff) % maxClevel;
+    size_t const cSize = ZSTD_compress(compressedBuff, compressedBuffCapacity, srcBuff, srcBuffSize, cLevel);
+    if (ZSTD_isError(cSize)) {
+        fprintf(stderr, "Compression error : %s \n", ZSTD_getErrorName(cSize));
+        return cSize;
+    }
+    return ZSTD_decompress(resultBuff, resultBuffCapacity, compressedBuff, cSize);
+}
+
+
+static size_t checkBuffers(const void* buff1, const void* buff2, size_t buffSize)
+{
+    const char* ip1 = (const char*)buff1;
+    const char* ip2 = (const char*)buff2;
+    size_t pos;
+
+    for (pos=0; pos<buffSize; pos++)
+        if (ip1[pos]!=ip2[pos])
+            break;
+
+    return pos;
+}
+
+
+static void roundTripCheck(const void* srcBuff, size_t srcBuffSize)
+{
+    size_t const cBuffSize = ZSTD_compressBound(srcBuffSize);
+    void* cBuff = malloc(cBuffSize);
+    void* rBuff = malloc(cBuffSize);
+    #define CRASH { free(cBuff); free(cBuff); }   /* double free, to crash program */
+
+    if (!cBuff || !rBuff) {
+        fprintf(stderr, "not enough memory ! \n");
+        exit (1);
+    }
+
+    {   size_t const result = roundTripTest(rBuff, cBuffSize, cBuff, cBuffSize, srcBuff, srcBuffSize);
+        if (ZSTD_isError(result)) {
+            fprintf(stderr, "roundTripTest error : %s \n", ZSTD_getErrorName(result));
+            CRASH;
+        }
+        if (result != srcBuffSize) {
+            fprintf(stderr, "Incorrect regenerated size : %u != %u\n", (unsigned)result, (unsigned)srcBuffSize);
+            CRASH;
+        }
+        if (checkBuffers(srcBuff, rBuff, srcBuffSize) != srcBuffSize) {
+            fprintf(stderr, "Silent decoding corruption !!!");
+            CRASH;
+        }
+    }
+
+    free(cBuff);
+    free(rBuff);
+}
+
+
+static size_t getFileSize(const char* infilename)
+{
+    int r;
+#if defined(_MSC_VER)
+    struct _stat64 statbuf;
+    r = _stat64(infilename, &statbuf);
+    if (r || !(statbuf.st_mode & S_IFREG)) return 0;   /* No good... */
+#else
+    struct stat statbuf;
+    r = stat(infilename, &statbuf);
+    if (r || !S_ISREG(statbuf.st_mode)) return 0;   /* No good... */
+#endif
+    return (size_t)statbuf.st_size;
+}
+
+
+static int isDirectory(const char* infilename)
+{
+    int r;
+#if defined(_MSC_VER)
+    struct _stat64 statbuf;
+    r = _stat64(infilename, &statbuf);
+    if (!r && (statbuf.st_mode & _S_IFDIR)) return 1;
+#else
+    struct stat statbuf;
+    r = stat(infilename, &statbuf);
+    if (!r && S_ISDIR(statbuf.st_mode)) return 1;
+#endif
+    return 0;
+}
+
+
+/** loadFile() :
+*   requirement : `buffer` size >= `fileSize` */
+static void loadFile(void* buffer, const char* fileName, size_t fileSize)
+{
+    FILE* const f = fopen(fileName, "rb");
+    if (isDirectory(fileName)) {
+        fprintf(stderr, "Ignoring %s directory \n", fileName);
+        exit(2);
+    }
+    if (f==NULL) {
+        fprintf(stderr, "Impossible to open %s \n", fileName);
+        exit(3);
+    }
+    {   size_t const readSize = fread(buffer, 1, fileSize, f);
+        if (readSize != fileSize) {
+            fprintf(stderr, "Error reading %s \n", fileName);
+            exit(5);
+    }   }
+    fclose(f);
+}
+
+
+static void fileCheck(const char* fileName)
+{
+    size_t const fileSize = getFileSize(fileName);
+    void* buffer = malloc(fileSize);
+    if (!buffer) {
+        fprintf(stderr, "not enough memory \n");
+        exit(4);
+    }
+    loadFile(buffer, fileName, fileSize);
+    roundTripCheck(buffer, fileSize);
+    free (buffer);
+}
+
+int main(int argCount, const char** argv) {
+    if (argCount < 2) {
+        fprintf(stderr, "Error : no argument : need input file \n");
+        exit(9);
+    }
+    fileCheck(argv[1]);
+    fprintf(stderr, "no pb detected\n");
+    return 0;
+}