Yann Collet | 32fb407 | 2017-08-18 16:52:05 -0700 | [diff] [blame] | 1 | /* |
Yann Collet | 4ded9e5 | 2016-08-30 10:04:33 -0700 | [diff] [blame] | 2 | * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. |
| 3 | * All rights reserved. |
| 4 | * |
Yann Collet | 32fb407 | 2017-08-18 16:52:05 -0700 | [diff] [blame] | 5 | * This source code is licensed under both the BSD-style license (found in the |
| 6 | * LICENSE file in the root directory of this source tree) and the GPLv2 (found |
| 7 | * in the COPYING file in the root directory of this source tree). |
Yann Collet | 3128e03 | 2017-09-08 00:09:23 -0700 | [diff] [blame] | 8 | * You may select, at your option, one of the above-listed licenses. |
Yann Collet | 4ded9e5 | 2016-08-30 10:04:33 -0700 | [diff] [blame] | 9 | */ |
Yann Collet | fb0ba59 | 2016-05-28 15:30:01 +0200 | [diff] [blame] | 10 | |
Yann Collet | fb0ba59 | 2016-05-28 15:30:01 +0200 | [diff] [blame] | 11 | /* |
| 12 | This program takes a file in input, |
| 13 | performs a zstd round-trip test (compression - decompress) |
| 14 | compares the result with original |
| 15 | and generates a crash (double free) on corruption detection. |
| 16 | */ |
| 17 | |
| 18 | /*=========================================== |
| 19 | * Dependencies |
| 20 | *==========================================*/ |
| 21 | #include <stddef.h> /* size_t */ |
| 22 | #include <stdlib.h> /* malloc, free, exit */ |
| 23 | #include <stdio.h> /* fprintf */ |
Stella Lau | 1c81f72 | 2017-08-23 15:47:15 -0700 | [diff] [blame] | 24 | #include <string.h> /* strcmp */ |
Yann Collet | fb0ba59 | 2016-05-28 15:30:01 +0200 | [diff] [blame] | 25 | #include <sys/types.h> /* stat */ |
| 26 | #include <sys/stat.h> /* stat */ |
Yann Collet | cbf8c52 | 2016-06-20 16:18:19 +0200 | [diff] [blame] | 27 | #include "xxhash.h" |
Stella Lau | 1c81f72 | 2017-08-23 15:47:15 -0700 | [diff] [blame] | 28 | |
| 29 | #define ZSTD_STATIC_LINKING_ONLY |
Yann Collet | fb0ba59 | 2016-05-28 15:30:01 +0200 | [diff] [blame] | 30 | #include "zstd.h" |
| 31 | |
Yann Collet | cbf8c52 | 2016-06-20 16:18:19 +0200 | [diff] [blame] | 32 | /*=========================================== |
| 33 | * Macros |
| 34 | *==========================================*/ |
| 35 | #define MIN(a,b) ( (a) < (b) ? (a) : (b) ) |
| 36 | |
Stella Lau | 1c81f72 | 2017-08-23 15:47:15 -0700 | [diff] [blame] | 37 | static void crash(int errorCode){ |
| 38 | /* abort if AFL/libfuzzer, exit otherwise */ |
| 39 | #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION /* could also use __AFL_COMPILER */ |
| 40 | abort(); |
| 41 | #else |
| 42 | exit(errorCode); |
| 43 | #endif |
| 44 | } |
| 45 | |
| 46 | #define CHECK_Z(f) { \ |
| 47 | size_t const err = f; \ |
| 48 | if (ZSTD_isError(err)) { \ |
| 49 | fprintf(stderr, \ |
| 50 | "Error=> %s: %s", \ |
| 51 | #f, ZSTD_getErrorName(err)); \ |
| 52 | crash(1); \ |
| 53 | } } |
| 54 | |
Yann Collet | fb0ba59 | 2016-05-28 15:30:01 +0200 | [diff] [blame] | 55 | /** roundTripTest() : |
| 56 | * Compresses `srcBuff` into `compressedBuff`, |
| 57 | * then decompresses `compressedBuff` into `resultBuff`. |
| 58 | * Compression level used is derived from first content byte. |
| 59 | * @return : result of decompression, which should be == `srcSize` |
| 60 | * or an error code if either compression or decompression fails. |
| 61 | * Note : `compressedBuffCapacity` should be `>= ZSTD_compressBound(srcSize)` |
| 62 | * for compression to be guaranteed to work */ |
| 63 | static size_t roundTripTest(void* resultBuff, size_t resultBuffCapacity, |
| 64 | void* compressedBuff, size_t compressedBuffCapacity, |
| 65 | const void* srcBuff, size_t srcBuffSize) |
| 66 | { |
| 67 | static const int maxClevel = 19; |
Yann Collet | cbf8c52 | 2016-06-20 16:18:19 +0200 | [diff] [blame] | 68 | size_t const hashLength = MIN(128, srcBuffSize); |
| 69 | unsigned const h32 = XXH32(srcBuff, hashLength, 0); |
| 70 | int const cLevel = h32 % maxClevel; |
Yann Collet | fb0ba59 | 2016-05-28 15:30:01 +0200 | [diff] [blame] | 71 | size_t const cSize = ZSTD_compress(compressedBuff, compressedBuffCapacity, srcBuff, srcBuffSize, cLevel); |
| 72 | if (ZSTD_isError(cSize)) { |
| 73 | fprintf(stderr, "Compression error : %s \n", ZSTD_getErrorName(cSize)); |
| 74 | return cSize; |
| 75 | } |
| 76 | return ZSTD_decompress(resultBuff, resultBuffCapacity, compressedBuff, cSize); |
| 77 | } |
| 78 | |
Stella Lau | 1c81f72 | 2017-08-23 15:47:15 -0700 | [diff] [blame] | 79 | /** cctxParamRoundTripTest() : |
| 80 | * Same as roundTripTest() except allows experimenting with ZSTD_CCtx_params. */ |
| 81 | static size_t cctxParamRoundTripTest(void* resultBuff, size_t resultBuffCapacity, |
| 82 | void* compressedBuff, size_t compressedBuffCapacity, |
| 83 | const void* srcBuff, size_t srcBuffSize) |
| 84 | { |
| 85 | ZSTD_CCtx* const cctx = ZSTD_createCCtx(); |
| 86 | ZSTD_CCtx_params* const cctxParams = ZSTD_createCCtxParams(); |
| 87 | ZSTD_inBuffer inBuffer = { srcBuff, srcBuffSize, 0 }; |
Yann Collet | d8e215c | 2018-11-30 11:16:26 -0800 | [diff] [blame^] | 88 | ZSTD_outBuffer outBuffer = { compressedBuff, compressedBuffCapacity, 0 }; |
Stella Lau | 1c81f72 | 2017-08-23 15:47:15 -0700 | [diff] [blame] | 89 | |
| 90 | static const int maxClevel = 19; |
| 91 | size_t const hashLength = MIN(128, srcBuffSize); |
| 92 | unsigned const h32 = XXH32(srcBuff, hashLength, 0); |
| 93 | int const cLevel = h32 % maxClevel; |
| 94 | |
| 95 | /* Set parameters */ |
| 96 | CHECK_Z( ZSTD_CCtxParam_setParameter(cctxParams, ZSTD_p_compressionLevel, cLevel) ); |
Yann Collet | 209df52 | 2018-02-01 19:29:30 -0800 | [diff] [blame] | 97 | CHECK_Z( ZSTD_CCtxParam_setParameter(cctxParams, ZSTD_p_nbWorkers, 2) ); |
Stella Lau | 9911153 | 2017-08-25 13:14:51 -0700 | [diff] [blame] | 98 | CHECK_Z( ZSTD_CCtxParam_setParameter(cctxParams, ZSTD_p_overlapSizeLog, 5) ); |
| 99 | |
Stella Lau | 1c81f72 | 2017-08-23 15:47:15 -0700 | [diff] [blame] | 100 | |
| 101 | /* Apply parameters */ |
Stella Lau | 82d636b | 2017-08-29 18:03:06 -0700 | [diff] [blame] | 102 | CHECK_Z( ZSTD_CCtx_setParametersUsingCCtxParams(cctx, cctxParams) ); |
Stella Lau | 1c81f72 | 2017-08-23 15:47:15 -0700 | [diff] [blame] | 103 | |
Yann Collet | d8e215c | 2018-11-30 11:16:26 -0800 | [diff] [blame^] | 104 | CHECK_Z (ZSTD_compressStream2(cctx, &outBuffer, &inBuffer, ZSTD_e_end) ); |
Stella Lau | 1c81f72 | 2017-08-23 15:47:15 -0700 | [diff] [blame] | 105 | |
| 106 | ZSTD_freeCCtxParams(cctxParams); |
| 107 | ZSTD_freeCCtx(cctx); |
| 108 | |
| 109 | return ZSTD_decompress(resultBuff, resultBuffCapacity, compressedBuff, outBuffer.pos); |
| 110 | } |
Yann Collet | fb0ba59 | 2016-05-28 15:30:01 +0200 | [diff] [blame] | 111 | |
| 112 | static size_t checkBuffers(const void* buff1, const void* buff2, size_t buffSize) |
| 113 | { |
| 114 | const char* ip1 = (const char*)buff1; |
| 115 | const char* ip2 = (const char*)buff2; |
| 116 | size_t pos; |
| 117 | |
| 118 | for (pos=0; pos<buffSize; pos++) |
| 119 | if (ip1[pos]!=ip2[pos]) |
| 120 | break; |
| 121 | |
| 122 | return pos; |
| 123 | } |
| 124 | |
Stella Lau | 1c81f72 | 2017-08-23 15:47:15 -0700 | [diff] [blame] | 125 | static void roundTripCheck(const void* srcBuff, size_t srcBuffSize, int testCCtxParams) |
Yann Collet | fb0ba59 | 2016-05-28 15:30:01 +0200 | [diff] [blame] | 126 | { |
| 127 | size_t const cBuffSize = ZSTD_compressBound(srcBuffSize); |
| 128 | void* cBuff = malloc(cBuffSize); |
| 129 | void* rBuff = malloc(cBuffSize); |
Yann Collet | fb0ba59 | 2016-05-28 15:30:01 +0200 | [diff] [blame] | 130 | |
| 131 | if (!cBuff || !rBuff) { |
| 132 | fprintf(stderr, "not enough memory ! \n"); |
| 133 | exit (1); |
| 134 | } |
| 135 | |
Stella Lau | 1c81f72 | 2017-08-23 15:47:15 -0700 | [diff] [blame] | 136 | { size_t const result = testCCtxParams ? |
| 137 | cctxParamRoundTripTest(rBuff, cBuffSize, cBuff, cBuffSize, srcBuff, srcBuffSize) |
| 138 | : roundTripTest(rBuff, cBuffSize, cBuff, cBuffSize, srcBuff, srcBuffSize); |
Yann Collet | fb0ba59 | 2016-05-28 15:30:01 +0200 | [diff] [blame] | 139 | if (ZSTD_isError(result)) { |
| 140 | fprintf(stderr, "roundTripTest error : %s \n", ZSTD_getErrorName(result)); |
Paul Cruz | 37e1b14 | 2017-06-09 13:57:18 -0700 | [diff] [blame] | 141 | crash(1); |
Yann Collet | fb0ba59 | 2016-05-28 15:30:01 +0200 | [diff] [blame] | 142 | } |
| 143 | if (result != srcBuffSize) { |
| 144 | fprintf(stderr, "Incorrect regenerated size : %u != %u\n", (unsigned)result, (unsigned)srcBuffSize); |
Paul Cruz | 37e1b14 | 2017-06-09 13:57:18 -0700 | [diff] [blame] | 145 | crash(1); |
Yann Collet | fb0ba59 | 2016-05-28 15:30:01 +0200 | [diff] [blame] | 146 | } |
| 147 | if (checkBuffers(srcBuff, rBuff, srcBuffSize) != srcBuffSize) { |
| 148 | fprintf(stderr, "Silent decoding corruption !!!"); |
Paul Cruz | 37e1b14 | 2017-06-09 13:57:18 -0700 | [diff] [blame] | 149 | crash(1); |
Yann Collet | fb0ba59 | 2016-05-28 15:30:01 +0200 | [diff] [blame] | 150 | } |
| 151 | } |
| 152 | |
| 153 | free(cBuff); |
| 154 | free(rBuff); |
| 155 | } |
| 156 | |
| 157 | |
| 158 | static size_t getFileSize(const char* infilename) |
| 159 | { |
| 160 | int r; |
| 161 | #if defined(_MSC_VER) |
| 162 | struct _stat64 statbuf; |
| 163 | r = _stat64(infilename, &statbuf); |
| 164 | if (r || !(statbuf.st_mode & S_IFREG)) return 0; /* No good... */ |
| 165 | #else |
| 166 | struct stat statbuf; |
| 167 | r = stat(infilename, &statbuf); |
| 168 | if (r || !S_ISREG(statbuf.st_mode)) return 0; /* No good... */ |
| 169 | #endif |
| 170 | return (size_t)statbuf.st_size; |
| 171 | } |
| 172 | |
| 173 | |
| 174 | static int isDirectory(const char* infilename) |
| 175 | { |
| 176 | int r; |
| 177 | #if defined(_MSC_VER) |
| 178 | struct _stat64 statbuf; |
| 179 | r = _stat64(infilename, &statbuf); |
| 180 | if (!r && (statbuf.st_mode & _S_IFDIR)) return 1; |
| 181 | #else |
| 182 | struct stat statbuf; |
| 183 | r = stat(infilename, &statbuf); |
| 184 | if (!r && S_ISDIR(statbuf.st_mode)) return 1; |
| 185 | #endif |
| 186 | return 0; |
| 187 | } |
| 188 | |
| 189 | |
| 190 | /** loadFile() : |
| 191 | * requirement : `buffer` size >= `fileSize` */ |
| 192 | static void loadFile(void* buffer, const char* fileName, size_t fileSize) |
| 193 | { |
| 194 | FILE* const f = fopen(fileName, "rb"); |
| 195 | if (isDirectory(fileName)) { |
| 196 | fprintf(stderr, "Ignoring %s directory \n", fileName); |
| 197 | exit(2); |
| 198 | } |
| 199 | if (f==NULL) { |
| 200 | fprintf(stderr, "Impossible to open %s \n", fileName); |
| 201 | exit(3); |
| 202 | } |
| 203 | { size_t const readSize = fread(buffer, 1, fileSize, f); |
| 204 | if (readSize != fileSize) { |
| 205 | fprintf(stderr, "Error reading %s \n", fileName); |
| 206 | exit(5); |
| 207 | } } |
| 208 | fclose(f); |
| 209 | } |
| 210 | |
| 211 | |
Stella Lau | 1c81f72 | 2017-08-23 15:47:15 -0700 | [diff] [blame] | 212 | static void fileCheck(const char* fileName, int testCCtxParams) |
Yann Collet | fb0ba59 | 2016-05-28 15:30:01 +0200 | [diff] [blame] | 213 | { |
| 214 | size_t const fileSize = getFileSize(fileName); |
Yann Collet | 3692c31 | 2018-08-14 16:56:07 -0700 | [diff] [blame] | 215 | void* const buffer = malloc(fileSize + !fileSize /* avoid 0 */); |
Yann Collet | fb0ba59 | 2016-05-28 15:30:01 +0200 | [diff] [blame] | 216 | if (!buffer) { |
| 217 | fprintf(stderr, "not enough memory \n"); |
| 218 | exit(4); |
| 219 | } |
| 220 | loadFile(buffer, fileName, fileSize); |
Stella Lau | 1c81f72 | 2017-08-23 15:47:15 -0700 | [diff] [blame] | 221 | roundTripCheck(buffer, fileSize, testCCtxParams); |
Yann Collet | fb0ba59 | 2016-05-28 15:30:01 +0200 | [diff] [blame] | 222 | free (buffer); |
| 223 | } |
| 224 | |
| 225 | int main(int argCount, const char** argv) { |
Stella Lau | 1c81f72 | 2017-08-23 15:47:15 -0700 | [diff] [blame] | 226 | int argNb = 1; |
| 227 | int testCCtxParams = 0; |
Yann Collet | fb0ba59 | 2016-05-28 15:30:01 +0200 | [diff] [blame] | 228 | if (argCount < 2) { |
| 229 | fprintf(stderr, "Error : no argument : need input file \n"); |
| 230 | exit(9); |
| 231 | } |
Stella Lau | 1c81f72 | 2017-08-23 15:47:15 -0700 | [diff] [blame] | 232 | |
| 233 | if (!strcmp(argv[argNb], "--cctxParams")) { |
| 234 | testCCtxParams = 1; |
| 235 | argNb++; |
| 236 | } |
| 237 | |
| 238 | fileCheck(argv[argNb], testCCtxParams); |
Yann Collet | fb0ba59 | 2016-05-28 15:30:01 +0200 | [diff] [blame] | 239 | fprintf(stderr, "no pb detected\n"); |
| 240 | return 0; |
| 241 | } |