/root/bitcoin/src/wallet/test/fuzz/wallet_bdb_parser.cpp
Line | Count | Source (jump to first uncovered line) |
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 <bitcoin-build-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 | | // There is an inconsistency in BDB on Windows. |
22 | | // See: https://github.com/bitcoin/bitcoin/pull/26606#issuecomment-2322763212 |
23 | | #undef USE_BDB_NON_MSVC |
24 | | #if defined(USE_BDB) && !defined(_MSC_VER) |
25 | | #define USE_BDB_NON_MSVC |
26 | | #endif |
27 | | |
28 | | using wallet::DatabaseOptions; |
29 | | using wallet::DatabaseStatus; |
30 | | |
31 | | namespace { |
32 | | TestingSetup* g_setup; |
33 | | } // namespace |
34 | | |
35 | | void initialize_wallet_bdb_parser() |
36 | 0 | { |
37 | 0 | static auto testing_setup = MakeNoLogFileContext<TestingSetup>(); |
38 | 0 | g_setup = testing_setup.get(); |
39 | 0 | } |
40 | | |
41 | | FUZZ_TARGET(wallet_bdb_parser, .init = initialize_wallet_bdb_parser) |
42 | 0 | { |
43 | 0 | const auto wallet_path = g_setup->m_args.GetDataDirNet() / "fuzzed_wallet.dat"; |
44 | |
|
45 | 0 | { |
46 | 0 | AutoFile outfile{fsbridge::fopen(wallet_path, "wb")}; |
47 | 0 | outfile << Span{buffer}; |
48 | 0 | } |
49 | |
|
50 | 0 | const DatabaseOptions options{}; |
51 | 0 | DatabaseStatus status; |
52 | 0 | bilingual_str error; |
53 | |
|
54 | 0 | fs::path bdb_ro_dumpfile{g_setup->m_args.GetDataDirNet() / "fuzzed_dumpfile_bdb_ro.dump"}; |
55 | 0 | if (fs::exists(bdb_ro_dumpfile)) { // Writing into an existing dump file will throw an exception |
56 | 0 | remove(bdb_ro_dumpfile); |
57 | 0 | } |
58 | 0 | g_setup->m_args.ForceSetArg("-dumpfile", fs::PathToString(bdb_ro_dumpfile)); |
59 | |
|
60 | | #ifdef USE_BDB_NON_MSVC |
61 | | bool bdb_ro_err = false; |
62 | | bool bdb_ro_strict_err = false; |
63 | | #endif |
64 | 0 | auto db{MakeBerkeleyRODatabase(wallet_path, options, status, error)}; |
65 | 0 | if (db) { |
66 | 0 | assert(DumpWallet(g_setup->m_args, *db, error)); |
67 | 0 | } else { |
68 | | #ifdef USE_BDB_NON_MSVC |
69 | | bdb_ro_err = true; |
70 | | #endif |
71 | 0 | if (error.original.starts_with("AutoFile::ignore: end of file") || |
72 | 0 | error.original.starts_with("AutoFile::read: end of file") || |
73 | 0 | error.original.starts_with("AutoFile::seek: ") || |
74 | 0 | error.original == "Not a BDB file" || |
75 | 0 | error.original == "Unexpected page type, should be 9 (BTree Metadata)" || |
76 | 0 | error.original == "Unexpected database flags, should only be 0x20 (subdatabases)" || |
77 | 0 | error.original == "Unexpected outer database root page type" || |
78 | 0 | error.original == "Unexpected number of entries in outer database root page" || |
79 | 0 | error.original == "Subdatabase page number has unexpected length" || |
80 | 0 | error.original == "Unknown record type in records page" || |
81 | 0 | error.original == "Unknown record type in internal page" || |
82 | 0 | error.original == "Unexpected page size" || |
83 | 0 | error.original == "Unexpected page type" || |
84 | 0 | error.original == "Page number mismatch" || |
85 | 0 | error.original == "Bad btree level" || |
86 | 0 | error.original == "Bad page size" || |
87 | 0 | error.original == "Meta page number mismatch" || |
88 | 0 | error.original == "Data record position not in page" || |
89 | 0 | error.original == "Internal record position not in page" || |
90 | 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" || |
91 | 0 | error.original == "Records page has odd number of records" || |
92 | 0 | error.original == "Bad overflow record page type") { |
93 | | // Do nothing |
94 | 0 | } else if (error.original == "Subdatabase last page is greater than database last page" || |
95 | 0 | error.original == "Page number is greater than database last page" || |
96 | 0 | error.original == "Last page number could not fit in file" || |
97 | 0 | error.original == "Subdatabase has an unexpected name" || |
98 | 0 | error.original == "Unsupported BDB data file version number" || |
99 | 0 | error.original == "BDB builtin encryption is not supported") { |
100 | | #ifdef USE_BDB_NON_MSVC |
101 | | bdb_ro_strict_err = true; |
102 | | #endif |
103 | 0 | } else { |
104 | 0 | throw std::runtime_error(error.original); |
105 | 0 | } |
106 | 0 | } |
107 | |
|
108 | | #ifdef USE_BDB_NON_MSVC |
109 | | // Try opening with BDB |
110 | | fs::path bdb_dumpfile{g_setup->m_args.GetDataDirNet() / "fuzzed_dumpfile_bdb.dump"}; |
111 | | if (fs::exists(bdb_dumpfile)) { // Writing into an existing dump file will throw an exception |
112 | | remove(bdb_dumpfile); |
113 | | } |
114 | | g_setup->m_args.ForceSetArg("-dumpfile", fs::PathToString(bdb_dumpfile)); |
115 | | |
116 | | try { |
117 | | auto db{MakeBerkeleyDatabase(wallet_path, options, status, error)}; |
118 | | if (bdb_ro_err && !db) { |
119 | | return; |
120 | | } |
121 | | assert(db); |
122 | | if (bdb_ro_strict_err) { |
123 | | // BerkeleyRO will be stricter than BDB. Ignore when those specific errors are hit. |
124 | | return; |
125 | | } |
126 | | assert(!bdb_ro_err); |
127 | | assert(DumpWallet(g_setup->m_args, *db, error)); |
128 | | } catch (const std::runtime_error& e) { |
129 | | if (bdb_ro_err) return; |
130 | | throw e; |
131 | | } |
132 | | |
133 | | // Make sure the dumpfiles match |
134 | | if (fs::exists(bdb_ro_dumpfile) && fs::exists(bdb_dumpfile)) { |
135 | | std::ifstream bdb_ro_dump(bdb_ro_dumpfile, std::ios_base::binary | std::ios_base::in); |
136 | | std::ifstream bdb_dump(bdb_dumpfile, std::ios_base::binary | std::ios_base::in); |
137 | | assert(std::equal( |
138 | | std::istreambuf_iterator<char>(bdb_ro_dump.rdbuf()), |
139 | | std::istreambuf_iterator<char>(), |
140 | | std::istreambuf_iterator<char>(bdb_dump.rdbuf()))); |
141 | | } |
142 | | #endif |
143 | 0 | } |