Branch data Line data Source code
1 : : // Copyright (c) 2023-present The Bitcoin Core developers
2 : : // Distributed under the MIT software license, see the accompanying
3 : : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4 : :
5 : : #include <config/bitcoin-config.h> // IWYU pragma: keep
6 : : #include <test/fuzz/FuzzedDataProvider.h>
7 : : #include <test/fuzz/fuzz.h>
8 : : #include <test/fuzz/util.h>
9 : : #include <test/util/setup_common.h>
10 : : #include <util/fs.h>
11 : : #include <util/time.h>
12 : : #include <util/translation.h>
13 : : #include <wallet/bdb.h>
14 : : #include <wallet/db.h>
15 : : #include <wallet/dump.h>
16 : : #include <wallet/migrate.h>
17 : :
18 : : #include <fstream>
19 : : #include <iostream>
20 : :
21 : : using wallet::DatabaseOptions;
22 : : using wallet::DatabaseStatus;
23 : :
24 : : namespace {
25 : : TestingSetup* g_setup;
26 : : } // namespace
27 : :
28 : 0 : void initialize_wallet_bdb_parser()
29 : : {
30 [ # # # # ]: 0 : static auto testing_setup = MakeNoLogFileContext<TestingSetup>();
31 : 0 : g_setup = testing_setup.get();
32 [ # # ]: 0 : }
33 : :
34 [ # # ]: 0 : FUZZ_TARGET(wallet_bdb_parser, .init = initialize_wallet_bdb_parser)
35 : : {
36 [ # # ]: 0 : const auto wallet_path = g_setup->m_args.GetDataDirNet() / "fuzzed_wallet.dat";
37 : :
38 : 0 : {
39 [ # # ]: 0 : AutoFile outfile{fsbridge::fopen(wallet_path, "wb")};
40 [ # # ]: 0 : outfile << Span{buffer};
41 : 0 : }
42 : :
43 [ # # ]: 0 : const DatabaseOptions options{};
44 : 0 : DatabaseStatus status;
45 [ # # ]: 0 : bilingual_str error;
46 : :
47 [ # # # # ]: 0 : fs::path bdb_ro_dumpfile{g_setup->m_args.GetDataDirNet() / "fuzzed_dumpfile_bdb_ro.dump"};
48 [ # # # # ]: 0 : if (fs::exists(bdb_ro_dumpfile)) { // Writing into an existing dump file will throw an exception
49 [ # # ]: 0 : remove(bdb_ro_dumpfile);
50 : : }
51 [ # # # # : 0 : g_setup->m_args.ForceSetArg("-dumpfile", fs::PathToString(bdb_ro_dumpfile));
# # ]
52 : :
53 : : #ifdef USE_BDB
54 : 0 : bool bdb_ro_err = false;
55 : 0 : bool bdb_ro_strict_err = false;
56 : : #endif
57 [ # # ]: 0 : auto db{MakeBerkeleyRODatabase(wallet_path, options, status, error)};
58 [ # # ]: 0 : if (db) {
59 [ # # # # ]: 0 : assert(DumpWallet(g_setup->m_args, *db, error));
60 : : } else {
61 : : #ifdef USE_BDB
62 : 0 : bdb_ro_err = true;
63 : : #endif
64 : 0 : if (error.original.starts_with("AutoFile::ignore: end of file") ||
65 [ # # ]: 0 : error.original.starts_with("AutoFile::read: end of file") ||
66 [ # # ]: 0 : error.original.starts_with("AutoFile::seek: ") ||
67 [ # # ]: 0 : error.original == "Not a BDB file" ||
68 [ # # ]: 0 : error.original == "Unexpected page type, should be 9 (BTree Metadata)" ||
69 [ # # ]: 0 : error.original == "Unexpected database flags, should only be 0x20 (subdatabases)" ||
70 [ # # ]: 0 : error.original == "Unexpected outer database root page type" ||
71 [ # # ]: 0 : error.original == "Unexpected number of entries in outer database root page" ||
72 [ # # ]: 0 : error.original == "Subdatabase page number has unexpected length" ||
73 [ # # ]: 0 : error.original == "Unknown record type in records page" ||
74 [ # # ]: 0 : error.original == "Unknown record type in internal page" ||
75 [ # # ]: 0 : error.original == "Unexpected page size" ||
76 [ # # ]: 0 : error.original == "Unexpected page type" ||
77 [ # # ]: 0 : error.original == "Page number mismatch" ||
78 [ # # ]: 0 : error.original == "Bad btree level" ||
79 [ # # ]: 0 : error.original == "Bad page size" ||
80 [ # # ]: 0 : error.original == "Meta page number mismatch" ||
81 [ # # ]: 0 : error.original == "Data record position not in page" ||
82 [ # # ]: 0 : error.original == "Internal record position not in page" ||
83 [ # # ]: 0 : error.original == "LSNs are not reset, this database is not completely flushed. Please reopen then close the database with a version that has BDB support" ||
84 [ # # # # ]: 0 : error.original == "Records page has odd number of records" ||
85 [ # # ]: 0 : error.original == "Bad overflow record page type") {
86 : : // Do nothing
87 : 0 : } else if (error.original == "Subdatabase last page is greater than database last page" ||
88 [ # # ]: 0 : error.original == "Page number is greater than database last page" ||
89 [ # # ]: 0 : error.original == "Last page number could not fit in file" ||
90 [ # # ]: 0 : error.original == "Subdatabase has an unexpected name" ||
91 [ # # # # ]: 0 : error.original == "Unsupported BDB data file version number" ||
92 [ # # ]: 0 : error.original == "BDB builtin encryption is not supported") {
93 : : #ifdef USE_BDB
94 : : bdb_ro_strict_err = true;
95 : : #endif
96 : : } else {
97 [ # # ]: 0 : throw std::runtime_error(error.original);
98 : : }
99 : : }
100 : :
101 : : #ifdef USE_BDB
102 : : // Try opening with BDB
103 [ # # # # ]: 0 : fs::path bdb_dumpfile{g_setup->m_args.GetDataDirNet() / "fuzzed_dumpfile_bdb.dump"};
104 [ # # # # ]: 0 : if (fs::exists(bdb_dumpfile)) { // Writing into an existing dump file will throw an exception
105 [ # # ]: 0 : remove(bdb_dumpfile);
106 : : }
107 [ # # # # : 0 : g_setup->m_args.ForceSetArg("-dumpfile", fs::PathToString(bdb_dumpfile));
# # ]
108 : :
109 : 0 : try {
110 [ # # ]: 0 : auto db{MakeBerkeleyDatabase(wallet_path, options, status, error)};
111 [ # # # # ]: 0 : if (bdb_ro_err && !db) {
112 : : return;
113 : : }
114 [ # # ]: 0 : assert(db);
115 [ # # ]: 0 : if (bdb_ro_strict_err) {
116 : : // BerkeleyRO will be stricter than BDB. Ignore when those specific errors are hit.
117 : : return;
118 : : }
119 [ # # ]: 0 : assert(!bdb_ro_err);
120 [ # # # # ]: 0 : assert(DumpWallet(g_setup->m_args, *db, error));
121 [ # # ]: 0 : } catch (const std::runtime_error& e) {
122 [ - - ]: 0 : if (bdb_ro_err) return;
123 : 0 : throw e;
124 : 0 : }
125 : :
126 : : // Make sure the dumpfiles match
127 [ # # # # : 0 : if (fs::exists(bdb_ro_dumpfile) && fs::exists(bdb_dumpfile)) {
# # ]
128 [ # # ]: 0 : std::ifstream bdb_ro_dump(bdb_ro_dumpfile, std::ios_base::binary | std::ios_base::in);
129 [ # # ]: 0 : std::ifstream bdb_dump(bdb_dumpfile, std::ios_base::binary | std::ios_base::in);
130 [ # # # # ]: 0 : assert(std::equal(
131 : : std::istreambuf_iterator<char>(bdb_ro_dump.rdbuf()),
132 : : std::istreambuf_iterator<char>(),
133 : : std::istreambuf_iterator<char>(bdb_dump.rdbuf())));
134 : 0 : }
135 : : #endif
136 : 0 : }
|