summaryrefslogtreecommitdiff
path: root/subversion/tests/libsvn_fs_fs/fs-fs-fuzzy-test.c
diff options
context:
space:
mode:
Diffstat (limited to 'subversion/tests/libsvn_fs_fs/fs-fs-fuzzy-test.c')
-rw-r--r--subversion/tests/libsvn_fs_fs/fs-fs-fuzzy-test.c393
1 files changed, 393 insertions, 0 deletions
diff --git a/subversion/tests/libsvn_fs_fs/fs-fs-fuzzy-test.c b/subversion/tests/libsvn_fs_fs/fs-fs-fuzzy-test.c
new file mode 100644
index 0000000..818c1e0
--- /dev/null
+++ b/subversion/tests/libsvn_fs_fs/fs-fs-fuzzy-test.c
@@ -0,0 +1,393 @@
+/* fs-fs-fuzzy-test.c --- fuzzing tests for the FSFS filesystem
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <apr_pools.h>
+
+#include "../svn_test.h"
+#include "../../libsvn_fs_fs/fs.h"
+#include "../../libsvn_fs_fs/fs_fs.h"
+#include "../../libsvn_fs_fs/rev_file.h"
+
+#include "svn_hash.h"
+#include "svn_pools.h"
+#include "svn_props.h"
+#include "svn_fs.h"
+#include "private/svn_string_private.h"
+#include "private/svn_string_private.h"
+
+#include "../svn_test_fs.h"
+
+
+
+/*** Helper Functions ***/
+
+/* We won't log or malfunction() upon errors. */
+static void
+dont_filter_warnings(void *baton, svn_error_t *err)
+{
+ return;
+}
+
+
+/*** Test core code ***/
+
+/* Verify that a modification of any single byte in REVISION of FS at
+ * REPO_NAME using MODIFIER with BATON will be detected. */
+static svn_error_t *
+fuzzing_1_byte_1_rev(const char *repo_name,
+ svn_fs_t *fs,
+ svn_revnum_t revision,
+ unsigned char (* modifier)(unsigned char c, void *baton),
+ void *baton,
+ apr_pool_t *pool)
+{
+ svn_repos_t *repos;
+ apr_hash_t *fs_config;
+ svn_fs_fs__revision_file_t *rev_file;
+ apr_off_t filesize = 0, offset;
+ apr_off_t i;
+ unsigned char footer_len;
+
+ apr_pool_t *iterpool = svn_pool_create(pool);
+
+ /* Open the revision file for modification. */
+ SVN_ERR(svn_fs_fs__open_pack_or_rev_file_writable(&rev_file, fs, revision,
+ pool, iterpool));
+ SVN_ERR(svn_fs_fs__auto_read_footer(rev_file));
+ SVN_ERR(svn_io_file_seek(rev_file->file, APR_END, &filesize, iterpool));
+
+ offset = filesize - 1;
+ SVN_ERR(svn_io_file_seek(rev_file->file, APR_SET, &offset, iterpool));
+ SVN_ERR(svn_io_file_getc((char *)&footer_len, rev_file->file, iterpool));
+
+ /* We want all the caching we can get. More importantly, we want to
+ change the cache namespace before each test iteration. */
+ fs_config = apr_hash_make(pool);
+ svn_hash_sets(fs_config, SVN_FS_CONFIG_FSFS_CACHE_DELTAS, "1");
+ svn_hash_sets(fs_config, SVN_FS_CONFIG_FSFS_CACHE_FULLTEXTS, "1");
+ svn_hash_sets(fs_config, SVN_FS_CONFIG_FSFS_CACHE_REVPROPS, "2");
+ svn_hash_sets(fs_config, SVN_FS_CONFIG_FSFS_BLOCK_READ, "0");
+
+ /* Manipulate all bytes one at a time. */
+ for (i = 0; i < filesize; ++i)
+ {
+ svn_error_t *err = SVN_NO_ERROR;
+
+ /* Read byte */
+ unsigned char c_old, c_new;
+ SVN_ERR(svn_io_file_seek(rev_file->file, APR_SET, &i, iterpool));
+ SVN_ERR(svn_io_file_getc((char *)&c_old, rev_file->file, iterpool));
+
+ /* What to replace it with. Skip if there is no change. */
+ c_new = modifier(c_old, baton);
+ if (c_new == c_old)
+ continue;
+
+ /* Modify / corrupt the data. */
+ SVN_ERR(svn_io_file_seek(rev_file->file, APR_SET, &i, iterpool));
+ SVN_ERR(svn_io_file_putc((char)c_new, rev_file->file, iterpool));
+ SVN_ERR(svn_io_file_flush(rev_file->file, iterpool));
+
+ /* Make sure we use a different namespace for the caches during
+ this iteration. */
+ svn_hash_sets(fs_config, SVN_FS_CONFIG_FSFS_CACHE_NS,
+ svn_uuid_generate(iterpool));
+ SVN_ERR(svn_repos_open3(&repos, repo_name, fs_config, iterpool, iterpool));
+ svn_fs_set_warning_func(svn_repos_fs(repos), dont_filter_warnings, NULL);
+
+ /* This shall detect the corruption and return an error. */
+ err = svn_repos_verify_fs3(repos, revision, revision, FALSE, FALSE,
+ NULL, NULL, NULL, NULL, NULL, NULL,
+ iterpool);
+
+ /* Case-only changes in checksum digests are not an error.
+ * We allow upper case chars to be used in MD5 checksums in all other
+ * places, thus restricting them here would be inconsistent. */
+ if ( i >= filesize - footer_len /* Within footer */
+ && c_old >= 'a' && c_old <= 'f' /* 'a' to 'f', only appear
+ in checksum digests */
+ && c_new == c_old - 'a' + 'A') /* respective upper case */
+ {
+ if (err)
+ {
+ /* Let us know where we were too strict ... */
+ printf("Detected case change in checksum digest at offset 0x%"
+ APR_UINT64_T_HEX_FMT " (%" APR_OFF_T_FMT ") in r%ld: "
+ "%c -> %c\n", (apr_uint64_t)i, i, revision, c_old, c_new);
+
+ SVN_ERR(err);
+ }
+ }
+ else if (!err)
+ {
+ /* Let us know where we miss changes ... */
+ printf("Undetected mod at offset 0x%"APR_UINT64_T_HEX_FMT
+ " (%"APR_OFF_T_FMT") in r%ld: 0x%02x -> 0x%02x\n",
+ (apr_uint64_t)i, i, revision, c_old, c_new);
+
+ SVN_TEST_ASSERT(err);
+ }
+
+ svn_error_clear(err);
+
+ /* Undo the corruption. */
+ SVN_ERR(svn_io_file_seek(rev_file->file, APR_SET, &i, iterpool));
+ SVN_ERR(svn_io_file_putc((char)c_old, rev_file->file, iterpool));
+
+ svn_pool_clear(iterpool);
+ }
+
+ svn_pool_destroy(iterpool);
+
+ return SVN_NO_ERROR;
+}
+
+/* Create a greek repo with OPTS at REPO_NAME. Verify that a modification
+ * of any single byte using MODIFIER with BATON will be detected. */
+static svn_error_t *
+fuzzing_1_byte_test(const svn_test_opts_t *opts,
+ const char *repo_name,
+ unsigned char (* modifier)(unsigned char c, void *baton),
+ void *baton,
+ apr_pool_t *pool)
+{
+ svn_repos_t *repos;
+ svn_fs_t *fs;
+ svn_fs_txn_t *txn;
+ svn_fs_root_t *txn_root;
+ svn_revnum_t rev;
+ svn_revnum_t i;
+
+ apr_pool_t *iterpool = svn_pool_create(pool);
+
+ /* Bail (with success) on known-untestable scenarios */
+ if (strcmp(opts->fs_type, "fsfs") != 0)
+ return svn_error_create(SVN_ERR_TEST_SKIPPED, NULL,
+ "this will test FSFS repositories only");
+ /* Create a filesystem */
+ SVN_ERR(svn_test__create_repos(&repos, repo_name, opts, pool));
+ fs = svn_repos_fs(repos);
+
+ /* Revision 1 (one and only revision): the Greek tree */
+ SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, pool));
+ SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
+ SVN_ERR(svn_test__create_greek_tree(txn_root, pool));
+ SVN_ERR(svn_fs_commit_txn(NULL, &rev, txn, pool));
+ SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(rev));
+
+ for (i = 0; i <= rev; ++i)
+ {
+ svn_pool_clear(iterpool);
+ SVN_ERR(fuzzing_1_byte_1_rev(repo_name, fs, i, modifier, baton,
+ iterpool));
+ }
+
+ svn_pool_destroy(iterpool);
+
+ return SVN_NO_ERROR;
+}
+
+/* Modifier function to be used with fuzzing_set_byte_test.
+ * We return the fixed char value given as *BATON. */
+static unsigned char
+set_byte(unsigned char c, void *baton)
+{
+ return *(const unsigned char *)baton;
+}
+
+/* Run the fuzzing test setting any byte in the repo to all values MIN to
+ * MAX-1. */
+static svn_error_t *
+fuzzing_set_byte_test(const svn_test_opts_t *opts,
+ int min,
+ int max,
+ apr_pool_t *pool)
+{
+ apr_pool_t *iterpool = svn_pool_create(pool);
+ unsigned i = 0;
+ for (i = min; i < max; ++i)
+ {
+ unsigned char c = i;
+ const char *repo_name;
+ svn_pool_clear(iterpool);
+
+ repo_name = apr_psprintf(iterpool, "test-repo-fuzzing_set_byte_%d_%d",
+ min, max);
+ SVN_ERR(fuzzing_1_byte_test(opts, repo_name, set_byte, &c, iterpool));
+ }
+
+ svn_pool_destroy(iterpool);
+ return SVN_NO_ERROR;
+}
+
+
+
+/*** Tests ***/
+
+/* ------------------------------------------------------------------------ */
+
+static unsigned char
+invert_byte(unsigned char c, void *baton)
+{
+ return ~c;
+}
+
+static svn_error_t *
+fuzzing_invert_byte_test(const svn_test_opts_t *opts,
+ apr_pool_t *pool)
+{
+ SVN_ERR(fuzzing_1_byte_test(opts, "test-repo-fuzzing_invert_byte",
+ invert_byte, NULL, pool));
+
+ return SVN_NO_ERROR;
+}
+
+/* ------------------------------------------------------------------------ */
+
+static unsigned char
+increment_byte(unsigned char c, void *baton)
+{
+ return c + 1;
+}
+
+static svn_error_t *
+fuzzing_increment_byte_test(const svn_test_opts_t *opts,
+ apr_pool_t *pool)
+{
+ SVN_ERR(fuzzing_1_byte_test(opts, "test-repo-fuzzing_increment_byte",
+ increment_byte, NULL, pool));
+
+ return SVN_NO_ERROR;
+}
+
+/* ------------------------------------------------------------------------ */
+
+static unsigned char
+decrement_byte(unsigned char c, void *baton)
+{
+ return c - 1;
+}
+
+static svn_error_t *
+fuzzing_decrement_byte_test(const svn_test_opts_t *opts,
+ apr_pool_t *pool)
+{
+ SVN_ERR(fuzzing_1_byte_test(opts, "test-repo-fuzzing_decrement_byte",
+ decrement_byte, NULL, pool));
+
+ return SVN_NO_ERROR;
+}
+
+/* ------------------------------------------------------------------------ */
+
+static unsigned char
+null_byte(unsigned char c, void *baton)
+{
+ return 0;
+}
+
+static svn_error_t *
+fuzzing_null_byte_test(const svn_test_opts_t *opts,
+ apr_pool_t *pool)
+{
+ SVN_ERR(fuzzing_1_byte_test(opts, "test-repo-fuzzing_null_byte",
+ null_byte, NULL, pool));
+
+ return SVN_NO_ERROR;
+}
+
+/* ------------------------------------------------------------------------ */
+
+/* Generator macro: define a test function covering byte values N to M-1 */
+#define FUZZING_SET_BYTE_TEST_N(N,M)\
+ static svn_error_t * \
+ fuzzing_set_byte_test_ ##N(const svn_test_opts_t *opts, \
+ apr_pool_t *pool) \
+ { \
+ return svn_error_trace(fuzzing_set_byte_test(opts, N, M, pool)); \
+ }
+
+/* Add the test function declared above to the test_funcs array. */
+#define TEST_FUZZING_SET_BYTE_TEST_N(N,M)\
+ SVN_TEST_OPTS_PASS(fuzzing_set_byte_test_ ##N, \
+ "set any byte to any value between " #N " and " #M)
+
+/* Declare tests that will cover all possible byte values. */
+FUZZING_SET_BYTE_TEST_N(0,16)
+FUZZING_SET_BYTE_TEST_N(16,32)
+FUZZING_SET_BYTE_TEST_N(32,48)
+FUZZING_SET_BYTE_TEST_N(48,64)
+FUZZING_SET_BYTE_TEST_N(64,80)
+FUZZING_SET_BYTE_TEST_N(80,96)
+FUZZING_SET_BYTE_TEST_N(96,112)
+FUZZING_SET_BYTE_TEST_N(112,128)
+FUZZING_SET_BYTE_TEST_N(128,144)
+FUZZING_SET_BYTE_TEST_N(144,160)
+FUZZING_SET_BYTE_TEST_N(160,176)
+FUZZING_SET_BYTE_TEST_N(176,192)
+FUZZING_SET_BYTE_TEST_N(192,208)
+FUZZING_SET_BYTE_TEST_N(208,224)
+FUZZING_SET_BYTE_TEST_N(224,240)
+FUZZING_SET_BYTE_TEST_N(240,256)
+
+
+/* The test table. */
+
+/* Allow for any number of tests to run in parallel. */
+static int max_threads = 0;
+
+static struct svn_test_descriptor_t test_funcs[] =
+ {
+ SVN_TEST_NULL,
+ SVN_TEST_OPTS_PASS(fuzzing_invert_byte_test,
+ "fuzzing: invert any byte"),
+ SVN_TEST_OPTS_PASS(fuzzing_increment_byte_test,
+ "fuzzing: increment any byte"),
+ SVN_TEST_OPTS_PASS(fuzzing_decrement_byte_test,
+ "fuzzing: decrement any byte"),
+ SVN_TEST_OPTS_PASS(fuzzing_null_byte_test,
+ "fuzzing: set any byte to 0"),
+
+ /* Register generated tests. */
+ TEST_FUZZING_SET_BYTE_TEST_N(0,16),
+ TEST_FUZZING_SET_BYTE_TEST_N(16,32),
+ TEST_FUZZING_SET_BYTE_TEST_N(32,48),
+ TEST_FUZZING_SET_BYTE_TEST_N(48,64),
+ TEST_FUZZING_SET_BYTE_TEST_N(64,80),
+ TEST_FUZZING_SET_BYTE_TEST_N(80,96),
+ TEST_FUZZING_SET_BYTE_TEST_N(96,112),
+ TEST_FUZZING_SET_BYTE_TEST_N(112,128),
+ TEST_FUZZING_SET_BYTE_TEST_N(128,144),
+ TEST_FUZZING_SET_BYTE_TEST_N(144,160),
+ TEST_FUZZING_SET_BYTE_TEST_N(160,176),
+ TEST_FUZZING_SET_BYTE_TEST_N(176,192),
+ TEST_FUZZING_SET_BYTE_TEST_N(192,208),
+ TEST_FUZZING_SET_BYTE_TEST_N(208,224),
+ TEST_FUZZING_SET_BYTE_TEST_N(224,240),
+ TEST_FUZZING_SET_BYTE_TEST_N(240,256),
+
+ SVN_TEST_NULL
+ };
+
+SVN_TEST_MAIN