648db22b |
1 | /* |
2 | * Copyright (c) Meta Platforms, Inc. and affiliates. |
3 | * All rights reserved. |
4 | * |
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). |
8 | * You may select, at your option, one of the above-listed licenses. |
9 | */ |
10 | |
11 | /** |
12 | * This fuzz target performs a zstd round-trip test (compress & decompress), |
13 | * compares the result with the original, and calls abort() on corruption. |
14 | */ |
15 | |
16 | #include <stddef.h> |
17 | #include <stdlib.h> |
18 | #include <stdio.h> |
19 | #include <string.h> |
20 | #include "common/cpu.h" |
21 | #include "compress/hist.h" |
22 | #include "common/huf.h" |
23 | #include "fuzz_helpers.h" |
24 | #include "fuzz_data_producer.h" |
25 | #include "common/bits.h" |
26 | |
27 | static size_t adjustTableLog(size_t tableLog, size_t maxSymbol) |
28 | { |
29 | size_t const alphabetSize = maxSymbol + 1; |
30 | size_t minTableLog = ZSTD_highbit32(alphabetSize) + 1; |
31 | if ((alphabetSize & (alphabetSize - 1)) != 0) { |
32 | ++minTableLog; |
33 | } |
34 | assert(minTableLog <= 9); |
35 | if (tableLog < minTableLog) |
36 | return minTableLog; |
37 | else |
38 | return tableLog; |
39 | } |
40 | |
41 | int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size) |
42 | { |
43 | FUZZ_dataProducer_t *producer = FUZZ_dataProducer_create(src, size); |
44 | /* Select random parameters: #streams, X1 or X2 decoding, bmi2 */ |
45 | int const streams = FUZZ_dataProducer_int32Range(producer, 0, 1); |
46 | int const symbols = FUZZ_dataProducer_int32Range(producer, 0, 1); |
47 | int const flags = 0 |
48 | | (ZSTD_cpuid_bmi2(ZSTD_cpuid()) && FUZZ_dataProducer_int32Range(producer, 0, 1) ? HUF_flags_bmi2 : 0) |
49 | | (FUZZ_dataProducer_int32Range(producer, 0, 1) ? HUF_flags_optimalDepth : 0) |
50 | | (FUZZ_dataProducer_int32Range(producer, 0, 1) ? HUF_flags_preferRepeat : 0) |
51 | | (FUZZ_dataProducer_int32Range(producer, 0, 1) ? HUF_flags_suspectUncompressible : 0) |
52 | | (FUZZ_dataProducer_int32Range(producer, 0, 1) ? HUF_flags_disableAsm : 0) |
53 | | (FUZZ_dataProducer_int32Range(producer, 0, 1) ? HUF_flags_disableFast : 0); |
54 | /* Select a random cBufSize - it may be too small */ |
55 | size_t const cBufSize = FUZZ_dataProducer_uint32Range(producer, 0, 4 * size); |
56 | /* Select a random tableLog - we'll adjust it up later */ |
57 | size_t tableLog = FUZZ_dataProducer_uint32Range(producer, 1, 12); |
58 | size_t const kMaxSize = 256 * 1024; |
59 | size = FUZZ_dataProducer_remainingBytes(producer); |
60 | if (size > kMaxSize) |
61 | size = kMaxSize; |
62 | |
63 | if (size <= 1) { |
64 | FUZZ_dataProducer_free(producer); |
65 | return 0; |
66 | } |
67 | |
68 | uint32_t maxSymbol = 255; |
69 | |
70 | U32 count[256]; |
71 | size_t const mostFrequent = HIST_count(count, &maxSymbol, src, size); |
72 | FUZZ_ZASSERT(mostFrequent); |
73 | if (mostFrequent == size) { |
74 | /* RLE */ |
75 | FUZZ_dataProducer_free(producer); |
76 | return 0; |
77 | |
78 | } |
79 | FUZZ_ASSERT(maxSymbol <= 255); |
80 | tableLog = adjustTableLog(tableLog, maxSymbol); |
81 | |
82 | size_t const wkspSize = HUF_WORKSPACE_SIZE; |
83 | void* wksp = FUZZ_malloc(wkspSize); |
84 | void* rBuf = FUZZ_malloc(size); |
85 | void* cBuf = FUZZ_malloc(cBufSize); |
86 | HUF_CElt* ct = (HUF_CElt*)FUZZ_malloc(HUF_CTABLE_SIZE(maxSymbol)); |
87 | HUF_DTable* dt = (HUF_DTable*)FUZZ_malloc(HUF_DTABLE_SIZE(tableLog) * sizeof(HUF_DTable)); |
88 | dt[0] = tableLog * 0x01000001; |
89 | |
90 | tableLog = HUF_optimalTableLog(tableLog, size, maxSymbol, wksp, wkspSize, ct, count, flags); |
91 | FUZZ_ASSERT(tableLog <= 12); |
92 | tableLog = HUF_buildCTable_wksp(ct, count, maxSymbol, tableLog, wksp, wkspSize); |
93 | FUZZ_ZASSERT(tableLog); |
94 | size_t const tableSize = HUF_writeCTable_wksp(cBuf, cBufSize, ct, maxSymbol, tableLog, wksp, wkspSize); |
95 | if (ERR_isError(tableSize)) { |
96 | /* Errors on uncompressible data or cBufSize too small */ |
97 | goto _out; |
98 | } |
99 | FUZZ_ZASSERT(tableSize); |
100 | if (symbols == 0) { |
101 | FUZZ_ZASSERT(HUF_readDTableX1_wksp(dt, cBuf, tableSize, wksp, wkspSize, flags)); |
102 | } else { |
103 | size_t const ret = HUF_readDTableX2_wksp(dt, cBuf, tableSize, wksp, wkspSize, flags); |
104 | if (ERR_getErrorCode(ret) == ZSTD_error_tableLog_tooLarge) { |
105 | FUZZ_ZASSERT(HUF_readDTableX1_wksp(dt, cBuf, tableSize, wksp, wkspSize, flags)); |
106 | } else { |
107 | FUZZ_ZASSERT(ret); |
108 | } |
109 | } |
110 | |
111 | size_t cSize; |
112 | size_t rSize; |
113 | if (streams == 0) { |
114 | cSize = HUF_compress1X_usingCTable(cBuf, cBufSize, src, size, ct, flags); |
115 | FUZZ_ZASSERT(cSize); |
116 | if (cSize != 0) |
117 | rSize = HUF_decompress1X_usingDTable(rBuf, size, cBuf, cSize, dt, flags); |
118 | } else { |
119 | cSize = HUF_compress4X_usingCTable(cBuf, cBufSize, src, size, ct, flags); |
120 | FUZZ_ZASSERT(cSize); |
121 | if (cSize != 0) |
122 | rSize = HUF_decompress4X_usingDTable(rBuf, size, cBuf, cSize, dt, flags); |
123 | } |
124 | if (cSize != 0) { |
125 | FUZZ_ZASSERT(rSize); |
126 | FUZZ_ASSERT_MSG(rSize == size, "Incorrect regenerated size"); |
127 | FUZZ_ASSERT_MSG(!FUZZ_memcmp(src, rBuf, size), "Corruption!"); |
128 | } |
129 | _out: |
130 | free(rBuf); |
131 | free(cBuf); |
132 | free(ct); |
133 | free(dt); |
134 | free(wksp); |
135 | FUZZ_dataProducer_free(producer); |
136 | return 0; |
137 | } |