LCOV - code coverage report
Current view: top level - src/wallet/test/fuzz - wallet_bdb_parser.cpp (source / functions) Coverage Total Hit
Test: fuzz_coverage.info Lines: 96.0 % 50 48
Test Date: 2024-12-04 04:00:22 Functions: 100.0 % 3 3
Branches: 69.6 % 102 71

             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 <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                 :           1 : void initialize_wallet_bdb_parser()
      36                 :             : {
      37   [ +  -  +  - ]:           2 :     static auto testing_setup = MakeNoLogFileContext<TestingSetup>();
      38                 :           1 :     g_setup = testing_setup.get();
      39         [ +  - ]:           2 : }
      40                 :             : 
      41         [ +  - ]:         500 : FUZZ_TARGET(wallet_bdb_parser, .init = initialize_wallet_bdb_parser)
      42                 :             : {
      43         [ +  - ]:         176 :     const auto wallet_path = g_setup->m_args.GetDataDirNet() / "fuzzed_wallet.dat";
      44                 :             : 
      45                 :          88 :     {
      46   [ +  -  +  - ]:          88 :         AutoFile outfile{fsbridge::fopen(wallet_path, "wb")};
      47         [ +  - ]:         176 :         outfile << Span{buffer};
      48                 :           0 :     }
      49                 :             : 
      50         [ +  - ]:          88 :     const DatabaseOptions options{};
      51                 :          88 :     DatabaseStatus status;
      52         [ +  - ]:          88 :     bilingual_str error;
      53                 :             : 
      54   [ +  -  +  - ]:         176 :     fs::path bdb_ro_dumpfile{g_setup->m_args.GetDataDirNet() / "fuzzed_dumpfile_bdb_ro.dump"};
      55   [ +  -  +  + ]:          88 :     if (fs::exists(bdb_ro_dumpfile)) { // Writing into an existing dump file will throw an exception
      56         [ +  - ]:           7 :         remove(bdb_ro_dumpfile);
      57                 :             :     }
      58   [ +  -  +  -  :         264 :     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         [ +  - ]:          88 :     auto db{MakeBerkeleyRODatabase(wallet_path, options, status, error)};
      65         [ +  + ]:          88 :     if (db) {
      66   [ +  -  -  + ]:           7 :         assert(DumpWallet(g_setup->m_args, *db, error));
      67                 :             :     } else {
      68                 :             : #ifdef USE_BDB_NON_MSVC
      69                 :             :         bdb_ro_err = true;
      70                 :             : #endif
      71                 :          81 :         if (error.original.starts_with("AutoFile::ignore: end of file") ||
      72         [ +  + ]:          79 :             error.original.starts_with("AutoFile::read: end of file") ||
      73         [ +  - ]:          59 :             error.original.starts_with("AutoFile::seek: ") ||
      74         [ +  + ]:          59 :             error.original == "Not a BDB file" ||
      75         [ +  + ]:          57 :             error.original == "Unexpected page type, should be 9 (BTree Metadata)" ||
      76         [ +  + ]:          55 :             error.original == "Unexpected database flags, should only be 0x20 (subdatabases)" ||
      77         [ +  + ]:          53 :             error.original == "Unexpected outer database root page type" ||
      78         [ +  + ]:          52 :             error.original == "Unexpected number of entries in outer database root page" ||
      79         [ +  + ]:          51 :             error.original == "Subdatabase page number has unexpected length" ||
      80         [ +  + ]:          47 :             error.original == "Unknown record type in records page" ||
      81         [ +  - ]:          45 :             error.original == "Unknown record type in internal page" ||
      82         [ +  - ]:          45 :             error.original == "Unexpected page size" ||
      83         [ +  - ]:          45 :             error.original == "Unexpected page type" ||
      84         [ +  + ]:          45 :             error.original == "Page number mismatch" ||
      85         [ +  + ]:          42 :             error.original == "Bad btree level" ||
      86         [ +  + ]:          40 :             error.original == "Bad page size" ||
      87         [ +  + ]:          34 :             error.original == "Meta page number mismatch" ||
      88         [ +  + ]:          32 :             error.original == "Data record position not in page" ||
      89         [ +  - ]:          31 :             error.original == "Internal record position not in page" ||
      90         [ +  + ]:          31 :             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   [ +  +  +  - ]:         104 :             error.original == "Records page has odd number of records" ||
      92         [ +  - ]:          23 :             error.original == "Bad overflow record page type") {
      93                 :             :             // Do nothing
      94                 :          46 :         } else if (error.original == "Subdatabase last page is greater than database last page" ||
      95         [ +  + ]:          23 :                    error.original == "Page number is greater than database last page" ||
      96         [ +  + ]:          21 :                    error.original == "Last page number could not fit in file" ||
      97         [ +  + ]:          19 :                    error.original == "Subdatabase has an unexpected name" ||
      98   [ +  -  +  + ]:          26 :                    error.original == "Unsupported BDB data file version number" ||
      99         [ -  + ]:           1 :                    error.original == "BDB builtin encryption is not supported") {
     100                 :             : #ifdef USE_BDB_NON_MSVC
     101                 :             :             bdb_ro_strict_err = true;
     102                 :             : #endif
     103                 :             :         } else {
     104         [ #  # ]:           0 :             throw std::runtime_error(error.original);
     105                 :             :         }
     106                 :             :     }
     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                 :         352 : }
        

Generated by: LCOV version 2.0-1