1
/* fs-fs-fuzzy-test.c --- fuzzing tests for the FSFS filesystem
3
* ====================================================================
4
* Licensed to the Apache Software Foundation (ASF) under one
5
* or more contributor license agreements. See the NOTICE file
6
* distributed with this work for additional information
7
* regarding copyright ownership. The ASF licenses this file
8
* to you under the Apache License, Version 2.0 (the
9
* "License"); you may not use this file except in compliance
10
* with the License. You may obtain a copy of the License at
12
* http://www.apache.org/licenses/LICENSE-2.0
14
* Unless required by applicable law or agreed to in writing,
15
* software distributed under the License is distributed on an
16
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17
* KIND, either express or implied. See the License for the
18
* specific language governing permissions and limitations
20
* ====================================================================
25
#include <apr_pools.h>
27
#include "../svn_test.h"
28
#include "../../libsvn_fs_fs/fs.h"
29
#include "../../libsvn_fs_fs/fs_fs.h"
30
#include "../../libsvn_fs_fs/rev_file.h"
33
#include "svn_pools.h"
34
#include "svn_props.h"
36
#include "private/svn_string_private.h"
37
#include "private/svn_string_private.h"
39
#include "../svn_test_fs.h"
43
/*** Helper Functions ***/
45
/* We won't log or malfunction() upon errors. */
47
dont_filter_warnings(void *baton, svn_error_t *err)
53
/*** Test core code ***/
55
/* Verify that a modification of any single byte in REVISION of FS at
56
* REPO_NAME using MODIFIER with BATON will be detected. */
58
fuzzing_1_byte_1_rev(const char *repo_name,
60
svn_revnum_t revision,
61
unsigned char (* modifier)(unsigned char c, void *baton),
66
apr_hash_t *fs_config;
67
svn_fs_fs__revision_file_t *rev_file;
68
apr_off_t filesize = 0, offset;
70
unsigned char footer_len;
72
apr_pool_t *iterpool = svn_pool_create(pool);
74
/* Open the revision file for modification. */
75
SVN_ERR(svn_fs_fs__open_pack_or_rev_file_writable(&rev_file, fs, revision,
77
SVN_ERR(svn_fs_fs__auto_read_footer(rev_file));
78
SVN_ERR(svn_io_file_seek(rev_file->file, APR_END, &filesize, iterpool));
80
offset = filesize - 1;
81
SVN_ERR(svn_io_file_seek(rev_file->file, APR_SET, &offset, iterpool));
82
SVN_ERR(svn_io_file_getc((char *)&footer_len, rev_file->file, iterpool));
84
/* We want all the caching we can get. More importantly, we want to
85
change the cache namespace before each test iteration. */
86
fs_config = apr_hash_make(pool);
87
svn_hash_sets(fs_config, SVN_FS_CONFIG_FSFS_CACHE_DELTAS, "1");
88
svn_hash_sets(fs_config, SVN_FS_CONFIG_FSFS_CACHE_FULLTEXTS, "1");
89
svn_hash_sets(fs_config, SVN_FS_CONFIG_FSFS_CACHE_REVPROPS, "2");
90
svn_hash_sets(fs_config, SVN_FS_CONFIG_FSFS_BLOCK_READ, "0");
92
/* Manipulate all bytes one at a time. */
93
for (i = 0; i < filesize; ++i)
95
svn_error_t *err = SVN_NO_ERROR;
98
unsigned char c_old, c_new;
99
SVN_ERR(svn_io_file_seek(rev_file->file, APR_SET, &i, iterpool));
100
SVN_ERR(svn_io_file_getc((char *)&c_old, rev_file->file, iterpool));
102
/* What to replace it with. Skip if there is no change. */
103
c_new = modifier(c_old, baton);
107
/* Modify / corrupt the data. */
108
SVN_ERR(svn_io_file_seek(rev_file->file, APR_SET, &i, iterpool));
109
SVN_ERR(svn_io_file_putc((char)c_new, rev_file->file, iterpool));
110
SVN_ERR(svn_io_file_flush(rev_file->file, iterpool));
112
/* Make sure we use a different namespace for the caches during
114
svn_hash_sets(fs_config, SVN_FS_CONFIG_FSFS_CACHE_NS,
115
svn_uuid_generate(iterpool));
116
SVN_ERR(svn_repos_open3(&repos, repo_name, fs_config, iterpool, iterpool));
117
svn_fs_set_warning_func(svn_repos_fs(repos), dont_filter_warnings, NULL);
119
/* This shall detect the corruption and return an error. */
120
err = svn_repos_verify_fs3(repos, revision, revision, FALSE, FALSE,
121
NULL, NULL, NULL, NULL, NULL, NULL,
124
/* Case-only changes in checksum digests are not an error.
125
* We allow upper case chars to be used in MD5 checksums in all other
126
* places, thus restricting them here would be inconsistent. */
127
if ( i >= filesize - footer_len /* Within footer */
128
&& c_old >= 'a' && c_old <= 'f' /* 'a' to 'f', only appear
129
in checksum digests */
130
&& c_new == c_old - 'a' + 'A') /* respective upper case */
134
/* Let us know where we were too strict ... */
135
printf("Detected case change in checksum digest at offset 0x%"
136
APR_UINT64_T_HEX_FMT " (%" APR_OFF_T_FMT ") in r%ld: "
137
"%c -> %c\n", (apr_uint64_t)i, i, revision, c_old, c_new);
144
/* Let us know where we miss changes ... */
145
printf("Undetected mod at offset 0x%"APR_UINT64_T_HEX_FMT
146
" (%"APR_OFF_T_FMT") in r%ld: 0x%02x -> 0x%02x\n",
147
(apr_uint64_t)i, i, revision, c_old, c_new);
149
SVN_TEST_ASSERT(err);
152
svn_error_clear(err);
154
/* Undo the corruption. */
155
SVN_ERR(svn_io_file_seek(rev_file->file, APR_SET, &i, iterpool));
156
SVN_ERR(svn_io_file_putc((char)c_old, rev_file->file, iterpool));
158
svn_pool_clear(iterpool);
161
svn_pool_destroy(iterpool);
166
/* Create a greek repo with OPTS at REPO_NAME. Verify that a modification
167
* of any single byte using MODIFIER with BATON will be detected. */
169
fuzzing_1_byte_test(const svn_test_opts_t *opts,
170
const char *repo_name,
171
unsigned char (* modifier)(unsigned char c, void *baton),
178
svn_fs_root_t *txn_root;
182
apr_pool_t *iterpool = svn_pool_create(pool);
184
/* Bail (with success) on known-untestable scenarios */
185
if (strcmp(opts->fs_type, "fsfs") != 0)
186
return svn_error_create(SVN_ERR_TEST_SKIPPED, NULL,
187
"this will test FSFS repositories only");
188
/* Create a filesystem */
189
SVN_ERR(svn_test__create_repos(&repos, repo_name, opts, pool));
190
fs = svn_repos_fs(repos);
192
/* Revision 1 (one and only revision): the Greek tree */
193
SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, pool));
194
SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
195
SVN_ERR(svn_test__create_greek_tree(txn_root, pool));
196
SVN_ERR(svn_fs_commit_txn(NULL, &rev, txn, pool));
197
SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(rev));
199
for (i = 0; i <= rev; ++i)
201
svn_pool_clear(iterpool);
202
SVN_ERR(fuzzing_1_byte_1_rev(repo_name, fs, i, modifier, baton,
206
svn_pool_destroy(iterpool);
211
/* Modifier function to be used with fuzzing_set_byte_test.
212
* We return the fixed char value given as *BATON. */
214
set_byte(unsigned char c, void *baton)
216
return *(const unsigned char *)baton;
219
/* Run the fuzzing test setting any byte in the repo to all values MIN to
222
fuzzing_set_byte_test(const svn_test_opts_t *opts,
227
apr_pool_t *iterpool = svn_pool_create(pool);
229
for (i = min; i < max; ++i)
232
const char *repo_name;
233
svn_pool_clear(iterpool);
235
repo_name = apr_psprintf(iterpool, "test-repo-fuzzing_set_byte_%d_%d",
237
SVN_ERR(fuzzing_1_byte_test(opts, repo_name, set_byte, &c, iterpool));
240
svn_pool_destroy(iterpool);
248
/* ------------------------------------------------------------------------ */
251
invert_byte(unsigned char c, void *baton)
257
fuzzing_invert_byte_test(const svn_test_opts_t *opts,
260
SVN_ERR(fuzzing_1_byte_test(opts, "test-repo-fuzzing_invert_byte",
261
invert_byte, NULL, pool));
266
/* ------------------------------------------------------------------------ */
269
increment_byte(unsigned char c, void *baton)
275
fuzzing_increment_byte_test(const svn_test_opts_t *opts,
278
SVN_ERR(fuzzing_1_byte_test(opts, "test-repo-fuzzing_increment_byte",
279
increment_byte, NULL, pool));
284
/* ------------------------------------------------------------------------ */
287
decrement_byte(unsigned char c, void *baton)
293
fuzzing_decrement_byte_test(const svn_test_opts_t *opts,
296
SVN_ERR(fuzzing_1_byte_test(opts, "test-repo-fuzzing_decrement_byte",
297
decrement_byte, NULL, pool));
302
/* ------------------------------------------------------------------------ */
305
null_byte(unsigned char c, void *baton)
311
fuzzing_null_byte_test(const svn_test_opts_t *opts,
314
SVN_ERR(fuzzing_1_byte_test(opts, "test-repo-fuzzing_null_byte",
315
null_byte, NULL, pool));
320
/* ------------------------------------------------------------------------ */
322
/* Generator macro: define a test function covering byte values N to M-1 */
323
#define FUZZING_SET_BYTE_TEST_N(N,M)\
324
static svn_error_t * \
325
fuzzing_set_byte_test_ ##N(const svn_test_opts_t *opts, \
328
return svn_error_trace(fuzzing_set_byte_test(opts, N, M, pool)); \
331
/* Add the test function declared above to the test_funcs array. */
332
#define TEST_FUZZING_SET_BYTE_TEST_N(N,M)\
333
SVN_TEST_OPTS_PASS(fuzzing_set_byte_test_ ##N, \
334
"set any byte to any value between " #N " and " #M)
336
/* Declare tests that will cover all possible byte values. */
337
FUZZING_SET_BYTE_TEST_N(0,16)
338
FUZZING_SET_BYTE_TEST_N(16,32)
339
FUZZING_SET_BYTE_TEST_N(32,48)
340
FUZZING_SET_BYTE_TEST_N(48,64)
341
FUZZING_SET_BYTE_TEST_N(64,80)
342
FUZZING_SET_BYTE_TEST_N(80,96)
343
FUZZING_SET_BYTE_TEST_N(96,112)
344
FUZZING_SET_BYTE_TEST_N(112,128)
345
FUZZING_SET_BYTE_TEST_N(128,144)
346
FUZZING_SET_BYTE_TEST_N(144,160)
347
FUZZING_SET_BYTE_TEST_N(160,176)
348
FUZZING_SET_BYTE_TEST_N(176,192)
349
FUZZING_SET_BYTE_TEST_N(192,208)
350
FUZZING_SET_BYTE_TEST_N(208,224)
351
FUZZING_SET_BYTE_TEST_N(224,240)
352
FUZZING_SET_BYTE_TEST_N(240,256)
355
/* The test table. */
357
/* Allow for any number of tests to run in parallel. */
358
static int max_threads = 0;
360
static struct svn_test_descriptor_t test_funcs[] =
363
SVN_TEST_OPTS_PASS(fuzzing_invert_byte_test,
364
"fuzzing: invert any byte"),
365
SVN_TEST_OPTS_PASS(fuzzing_increment_byte_test,
366
"fuzzing: increment any byte"),
367
SVN_TEST_OPTS_PASS(fuzzing_decrement_byte_test,
368
"fuzzing: decrement any byte"),
369
SVN_TEST_OPTS_PASS(fuzzing_null_byte_test,
370
"fuzzing: set any byte to 0"),
372
/* Register generated tests. */
373
TEST_FUZZING_SET_BYTE_TEST_N(0,16),
374
TEST_FUZZING_SET_BYTE_TEST_N(16,32),
375
TEST_FUZZING_SET_BYTE_TEST_N(32,48),
376
TEST_FUZZING_SET_BYTE_TEST_N(48,64),
377
TEST_FUZZING_SET_BYTE_TEST_N(64,80),
378
TEST_FUZZING_SET_BYTE_TEST_N(80,96),
379
TEST_FUZZING_SET_BYTE_TEST_N(96,112),
380
TEST_FUZZING_SET_BYTE_TEST_N(112,128),
381
TEST_FUZZING_SET_BYTE_TEST_N(128,144),
382
TEST_FUZZING_SET_BYTE_TEST_N(144,160),
383
TEST_FUZZING_SET_BYTE_TEST_N(160,176),
384
TEST_FUZZING_SET_BYTE_TEST_N(176,192),
385
TEST_FUZZING_SET_BYTE_TEST_N(192,208),
386
TEST_FUZZING_SET_BYTE_TEST_N(208,224),
387
TEST_FUZZING_SET_BYTE_TEST_N(224,240),
388
TEST_FUZZING_SET_BYTE_TEST_N(240,256),