{
 "number": 35524,
 "repo": "bitcoin/bitcoin",
 "url": "https://github.com/bitcoin/bitcoin/pull/35524",
 "title": "validation: Avoid rewriting the genesis block during index recovery",
 "author": "winterrdog",
 "author_association": "CONTRIBUTOR",
 "created_at": "2026-06-13T10:12:20Z",
 "updated_at": "2026-10-26T20:57:49Z",
 "description_hash": "4b15715fc7e6b05a",
 "age_days": 141,
 "draft": true,
 "labels": [
  "Utils/log/libs",
  "Needs rebase"
 ],
 "milestone": null,
 "base": "master",
 "head_sha": "fd2d21053f014e76f44fb72ff081240cd3a2b519",
 "head_ref": "fix/posix-fallback-alloc",
 "head_repo": "winterrdog/bitcoin",
 "head_history": [
  {
   "t": "2026-06-13T19:04:36Z",
   "sha": "b07e08c6d889cbbb5fb9d6c2b4a8b48b0c74f2f0"
  },
  {
   "t": "2026-06-16T17:59:07Z",
   "sha": "65b44fcf0b664a4bd51beca5bd53010885b5fe2a"
  },
  {
   "t": "2026-09-21T17:42:10Z",
   "sha": "a1c412e93cc35b0b896af0325fe74f9f6d9b1603"
  },
  {
   "t": "2026-09-26T21:02:43Z",
   "sha": "0fcca3997605ed4e9af7db922418ded7390955cf"
  },
  {
   "t": "2026-09-26T21:16:14Z",
   "sha": "1a3d4ea56cdc9b10b484d95116988a0d396ce5c5"
  },
  {
   "t": "2026-09-26T21:22:09Z",
   "sha": "fd2d21053f014e76f44fb72ff081240cd3a2b519"
  }
 ],
 "additions": 40,
 "deletions": 19,
 "changed_files": 2,
 "commit_count": 2,
 "size_bucket": "S",
 "mergeable_state": "dirty",
 "bot": {
  "drahtbot": {
   "present": true,
   "reviews": {},
   "conflicts": []
  }
 },
 "acks_parsed": {},
 "acks_tally": {
  "ack": 0,
  "stale_ack": 0,
  "concept_ack": 0,
  "approach_ack": 0,
  "nack": 0,
  "concept_nack": 0,
  "approach_nack": 0
 },
 "reviews": {
  "approved": 0,
  "changes_requested": 0,
  "distinct_reviewers": [
   "sedited",
   "thomasbuilds"
  ]
 },
 "signals": {
  "needs_rebase": true,
  "ci_failed": false,
  "mergeable_state": "dirty",
  "last_author_activity": "2026-09-30T19:13:15Z",
  "last_reviewer_activity": "2026-09-19T15:01:53Z",
  "last_reviewer": "sedited",
  "author_silent_days": 32,
  "waiting_on_author_days": 0,
  "days_since_update": 6
 },
 "refs": {
  "mentioned": [
   1677,
   33128,
   33164
  ],
  "depends_on": [],
  "fixes": [
   33128
  ],
  "linked_issues": [
   {
    "number": 33128,
    "type": "issue",
    "state": "open",
    "merged": false,
    "merged_at": null,
    "title": "*BSD, OmniOS: `-reindex` is broken"
   }
  ],
  "references": [
   {
    "number": 33128,
    "type": "issue",
    "state": "open",
    "merged": false,
    "merged_at": null,
    "title": "*BSD, OmniOS: `-reindex` is broken"
   },
   {
    "number": 33164,
    "type": "pull",
    "state": "closed",
    "merged": false,
    "merged_at": null,
    "title": "Avoid file overwriting in fallback `AllocateFileRange` implementation"
   },
   {
    "number": 1677,
    "type": "pull",
    "state": "closed",
    "merged": true,
    "merged_at": "2012-10-20",
    "title": "Ultraprune: use a pruned-txout-set database for block validation"
   }
  ],
  "conflicts": []
 },
 "stack": {
  "shares_commits_with": [],
  "based_on": [],
  "base_for": []
 },
 "review_paths": [
  "src/util/fs_helpers.cpp"
 ],
 "body": "fixes #33128.\n\nthis picks up #33164.\n\n`Chainstate::LoadGenesisBlock()` could rewrite the genesis block even\nwhen `blk00000.dat` already contained valid block data. this becomes a\nproblem when the block index is missing or corrupted while the block\nfiles are still intact, such as during an explicit `-reindex` or an\nincomplete shutdown.\n\na call to `LoadGenesisBlock()` eventually reaches `AllocateFileRange()`,\nwhich falls back to writing zeroes to extend the file on platforms\nwithout a real `posix_fallocate()` syscall. when the index is missing,\nthis can overwrite existing block data beyond genesis. in #33128,\ndeleting `blocks/index` and running `-reindex` could therefore destroy\nthe remaining blocks and leave the node with only genesis, without\nreporting any error.\n\nthe primary fix is in `LoadGenesisBlock()`: check for an existing valid\ngenesis block at `{0, 0}` before writing it, and reuse it when\npresent.\n\n`AllocateFileRange()` also gets a small defensive change: its fallback\nno longer writes over a range that is already within the file. it only\nextends the file when necessary, protecting other callers that may\nencounter the same situation.",
 "commits": [
  {
   "sha": "ec80643e04503768d743e76de8c0fd573c23b2a3",
   "date": "2026-09-26T21:03:32Z",
   "message": "util: Fix data clobbering in `AllocateFileRange()`\n\nThe fallback variant of `AllocateFileRange` (introduced in #1677)\nunconditionally zero-filled the requested range, which could overwrite\nexisting block data in `blk*.dat` files during `-reindex` before the\nreindex scan ran. The fallback now only zero-fills the bytes beyond the\ncurrent end of the file.\n\nThe Windows implementation likewise now checks the current file size and\nonly extends the file when the requested range (`offset + length`)\nexceeds the existing end of the file, rather than extending it\nunconditionally."
  },
  {
   "sha": "fd2d21053f014e76f44fb72ff081240cd3a2b519",
   "date": "2026-09-26T21:21:51Z",
   "message": "validation: Avoid rewriting genesis block when it's already on disk\n\nFixes #33128\n\nWhen rebuilding the block index, the genesis block may already exist on\ndisk even if it appears to be corrupted or missing in the index.\nLoadGenesisBlock() rewriting the genesis block unconditionally in that\ncase can overwrite block data that follows it.\n\nOn platforms without a real preallocation syscall, that rewrite reaches\nAllocateFileRange's fallback (via `BlockManager::WriteBlock`),\nwhich has no way to know the file already contains data past this\npoint, and can zero it out. #33128 shows the effect: mine N blocks,\ndelete `blocks/index`, run `-reindex`. The log reports reindexing\nfinished, but the blocks after genesis are gone and the chain height\ncomes back as 0.\n\nNow, we check whether a valid genesis block is already present before\nwriting one, and reuse it when possible. This preserves existing block\ndata when recovering from a missing or corrupted block index."
  }
 ],
 "timeline": [
  {
   "t": "2026-06-13T10:15:19Z",
   "kind": "comment",
   "who": "winterrdog",
   "assoc": "CONTRIBUTOR",
   "text": "## test plan\n\n     bug reproduction (before the fix)\n\n(assuming you're at this PR's merge base i.e. ed7dd7cf4e1561a97edf72eba29a67b14e28c717)\n\n- on an affected system (e.g. OpenBSD or NetBSD), follow the steps in the issue's description: https://github.com/bitcoin/bitcoin/issues/33128#issue-3286010421\n- on systems that have `posix_fallocate()`, force the fallback path by applying this patch (profusely based on [maflcko's comment](https://github.com/bitcoin/bitcoin/issues/33128#issuecomment-3206135519)):\n    ```diff\n    diff --git a/src/util/fs_helpers.cpp b/src/util/fs_helpers.cpp\n    index be7f1ee5a2..ada3280dbc 100644\n    --- a/src/util/fs_helpers.cpp\n    +++ b/src/util/fs_helpers.cpp\n    @@ -200,7 +200,7 @@ void AllocateFileRange(FILE* file, unsigned int offset, unsigned int length)\n         }\n         ftruncate(fileno(file), static_cast<off_t>(offset) + length);\n     #else\n    -#if defined(HAVE_POSIX_FALLOCATE)\n    +#if 0\n         // Version using posix_fallocate\n         off_t nEndPos = (off_t)offset + length;\n         if (0 == posix_fallocate(fileno(file), 0, nEndPos)) return;\n    ```\n    then run:\n    ```sh\n    ./build/test/functional/test_runner.py feature_reindex_init.py --failfast\n    ```\n    which MUST fail with: `AssertionError: not(0 == 200)`.\n\n     fix verification (after applying the fix)\n\nafter reindexing, `getblockcount` should return the _same_ number of blocks that existed _before_ deleting the `index/` directory.\n\n---\ncc: @cedwies @hebasto",
   "id": "c:4698227652",
   "url": "https://github.com/bitcoin/bitcoin/pull/35524#issuecomment-4698227652",
   "hash": "ee68cf2e1c8251b9",
   "edited": "2026-09-30T18:14:07Z"
  },
  {
   "t": "2026-06-13T12:02:54Z",
   "kind": "review_comment",
   "who": "thomasbuilds",
   "assoc": "CONTRIBUTOR",
   "path": "src/util/fs_helpers.cpp",
   "commit": "88fd83ecf05a7435b673360a7f9f6f0cc2860a6e",
   "in_reply_to": null,
   "text": "wrong indentation",
   "id": "rc:3407926478",
   "url": "https://github.com/bitcoin/bitcoin/pull/35524#discussion_r3407926478",
   "hash": "4d554c2610f35882",
   "edited": null
  },
  {
   "t": "2026-06-13T19:04:36Z",
   "kind": "force_push",
   "who": "winterrdog",
   "commit": "b07e08c6d889cbbb5fb9d6c2b4a8b48b0c74f2f0",
   "id": "push:b07e08c6d889cbbb5fb9d6c2b4a8b48b0c74f2f0"
  },
  {
   "t": "2026-06-13T19:06:07Z",
   "kind": "review_comment",
   "who": "winterrdog",
   "assoc": "CONTRIBUTOR",
   "path": "src/util/fs_helpers.cpp",
   "commit": "88fd83ecf05a7435b673360a7f9f6f0cc2860a6e",
   "in_reply_to": 3407926478,
   "text": "thanks! fixed",
   "id": "rc:3408448664",
   "url": "https://github.com/bitcoin/bitcoin/pull/35524#discussion_r3408448664",
   "hash": "c9eac44e0f21e06d",
   "edited": null
  },
  {
   "t": "2026-06-14T07:37:49Z",
   "kind": "review_comment",
   "who": "thomasbuilds",
   "assoc": "CONTRIBUTOR",
   "path": "src/util/fs_helpers.cpp",
   "commit": "b07e08c6d889cbbb5fb9d6c2b4a8b48b0c74f2f0",
   "in_reply_to": null,
   "text": "nit: the Windows, macOS, and posix computations above all widen `offset` before adding `length`\n```suggestion\n    off_t end_pos{static_cast<off_t>(offset) + length};\n```\nwhat do you think?",
   "id": "rc:3409196404",
   "url": "https://github.com/bitcoin/bitcoin/pull/35524#discussion_r3409196404",
   "hash": "ff765a2a82f637f3",
   "edited": "2026-06-15T05:33:32Z"
  },
  {
   "t": "2026-06-16T17:59:07Z",
   "kind": "force_push",
   "who": "winterrdog",
   "commit": "65b44fcf0b664a4bd51beca5bd53010885b5fe2a",
   "id": "push:65b44fcf0b664a4bd51beca5bd53010885b5fe2a"
  },
  {
   "t": "2026-06-16T18:14:24Z",
   "kind": "review_comment",
   "who": "winterrdog",
   "assoc": "CONTRIBUTOR",
   "path": "src/util/fs_helpers.cpp",
   "commit": "b07e08c6d889cbbb5fb9d6c2b4a8b48b0c74f2f0",
   "in_reply_to": 3409196404,
   "text": "i think it's a good point for safety.\n\nsince `blk*.dat` files are capped at [`128` MiB](https://github.com/bitcoin/bitcoin/blob/a30ef6b91f73a6669e5aeedf460e0c9be2745a49/src/node/blockstorage.h#L126) (_the [allocation](https://github.com/bitcoin/bitcoin/blob/a30ef6b91f73a6669e5aeedf460e0c9be2745a49/src/node/blockstorage.cpp#L914) that later does the [pre-allocation](https://github.com/bitcoin/bitcoin/blob/a30ef6b91f73a6669e5aeedf460e0c9be2745a49/src/flatfile.cpp#L74) can only happen if the [assertion](https://github.com/bitcoin/bitcoin/blob/a30ef6b91f73a6669e5aeedf460e0c9be2745a49/src/node/blockstorage.cpp#L868) passed_), overflow is not an issue here, but explicitly casting `offset` makes the code more uniform to match the the other variants\n\napplied the change",
   "id": "rc:3423008427",
   "url": "https://github.com/bitcoin/bitcoin/pull/35524#discussion_r3423008427",
   "hash": "b039a19f00961dfa",
   "edited": "2026-06-16T19:08:24Z"
  },
  {
   "t": "2026-06-16T18:59:11Z",
   "kind": "review_comment",
   "who": "winterrdog",
   "assoc": "CONTRIBUTOR",
   "path": "src/util/fs_helpers.cpp",
   "commit": "65b44fcf0b664a4bd51beca5bd53010885b5fe2a",
   "in_reply_to": null,
   "text": "was thinking about making this an *append-only operation* in this PR, but before I go ahead I need to get @sedited's thoughts (_because of your [comment](https://github.com/bitcoin/bitcoin/pull/33228/changes/4a9f8c78f65d49cca08ee66241cad88b547c5eda#r2619311217) on this [commit](https://github.com/bitcoin/bitcoin/pull/33228/changes/4a9f8c78f65d49cca08ee66241cad88b547c5eda)_). what led me to think about it is that, when I was looking at the only [call site](https://github.com/bitcoin/bitcoin/blob/a30ef6b91f73a6669e5aeedf460e0c9be2745a49/src/flatfile.cpp#L59-L74), it suggests the pre-allocation is fundamentally an append-only operation (i.e. always extending from the current file end in chunk-aligned steps).\n\njust want to understand the intended contract here a bit better before changing anything. under what circumstances would `AllocateFileRange()` ever be called with an `offset` that is _not equal_ to the current end of file (i.e. a non-contiguous region / hole in the file) ?",
   "id": "rc:3423287110",
   "url": "https://github.com/bitcoin/bitcoin/pull/35524#discussion_r3423287110",
   "hash": "55f30cf95f40da5a",
   "edited": "2026-06-16T19:00:11Z"
  },
  {
   "t": "2026-09-19T15:01:53Z",
   "kind": "comment",
   "who": "sedited",
   "assoc": "MEMBER",
   "text": "I was a bit confused initially by the PR description. If I understand the higher level problem here correctly it is that if a node is restarted with a broken block index and we land in `LoadGenesisBlock`, it will write the genesis block over the existing one. On platforms without posix_fallocate this will clobber the rest of the block file with zeros, because the pre-allocation code, absent a block index providing it with the relevant block file cursors, thinks it needs to allocate the file first. Can you re-write the description to make it a bit more clear what is going on?\n\nI wonder if instead of trying to fix this salvaging scenario by doing a bunch of patches in our low-level file handling code, the easier fix might be to check if we can read the genesis block from disk in `LoadGenesisBlock` by calling `ReadBlock` with a `0` flat file position?",
   "id": "c:5742883004",
   "url": "https://github.com/bitcoin/bitcoin/pull/35524#issuecomment-5742883004",
   "hash": "aba09067105637ef",
   "edited": null
  },
  {
   "t": "2026-09-21T08:13:52Z",
   "kind": "comment",
   "who": "winterrdog",
   "assoc": "CONTRIBUTOR",
   "text": "[quoted text omitted]\n\nthat's right. consider it done\n\nthough.. before i rewrite the PR description, i _just_ wanted to quickly\nestablish consensus with you on the direction i would like to take so that we\nare on the same page. i am going to expound on this, just below\n\n[quoted text omitted]\ngood idea! thanks for the suggestion. much easier to prevent the clobbering much\nearlier in the `AllocateFileRange()`'s caller graph\n\nso what i plan on doing is to make the `LoadGenesisBlock()` fix\n(your suggestion) the primary change i.e. read genesis back from `{0,0}` before\nwriting, and skip `WriteBlock()` entirely if there is already a valid genesis\nblock there. that directly fixes the scenario we are concerned about.\n\n~~for `AllocateFileRange()`, i am going to drop the [cleverness](https://github.com/winterrdog/bitcoin/blob/65b44fcf0b664a4bd51beca5bd53010885b5fe2a/src/util/fs_helpers.cpp#L236-L241) i was originally\nplaying with around block-spaced single-byte writes and keep the change small.\nthe fallback will simply skip the write entirely if the target range is already\nwithin the current file size, otherwise fall back to the old bulk-write\nbehavior _unchanged_~~ => **EDIT:** oh, wait! i remember why i added the clever counting\n(the `remaining = end_pos - file_size`). i went with that because once we know\nthe current file size, the fallback only needs to write enough bytes to reach `end_pos`,\ninstead of the full `length`. the `AllocateFileRange`'s doc comment:\nhttps://github.com/bitcoin/bitcoin/blob/5ca3773414df36b52d6ea6c0a0ae0b1a1ad1f435/src/util/fs_helpers.cpp#L196-L199\nallows `offset` to be before the current EOF (most common case in practice), so\nusing `length` directly can extend the file on disk past the requested end. for\ninstance, with a `100,000`-byte file and `offset = 90,000`, `length = 50,000`,\nthe target is `140,000` bytes: `remaining` writes `40,000` bytes and reaches\nthe target exactly, while using `length` writes `50,000` bytes and extends the\nfile to `150,000` bytes\n\nalso, `FlatFileSeq::Allocate`'s doc comment:\nhttps://github.com/bitcoin/bitcoin/blob/f839ee1afb0ff02c58422dae6d2678e5e654b605/src/flatfile.h#L64-L73\nsays allocations are rounded up (ceil division) to the nearest chunk-size\nmultiple, meaning a block file's real on-disk size is routinely ahead of the\nlogical write position by design, with that preallocated space being used up\ngradually). so `offset < file_size` is a pretty common situation pragmatically,\nwhich is why i had the fallback case compute the exact gap instead of writing\nthe full `length` on top of whatever headroom that _already_ happens to exist\n\n---\nalso poked at the caller graph for `AllocateFileRange()`\n(via `FlatFileSeq::Allocate`) out of curiosity, to see if `LoadGenesisBlock()`\nwas the only thing exposed to this pattern. nothing conclusive, but flagging in\ncase it is useful:\n\n- [`WriteFilterToDisk`](https://github.com/bitcoin/bitcoin/blob/bb5296576e8f1a9fc11c19d9a25ba02ed4547e24/src/index/blockfilterindex.cpp#L220-L226) (block filter index) goes through the same allocator, and\n  a full index rebuild resets its write cursor from scratch. in theory,\n  a _stale-cursor-vs-intact-disk-data_ mismatch _could_ recreate the same bug\n  shape as genesis, but i have not fully confirmed whether that is actually\n  reachable in practice or just a narrow edge case\n- i also wondered about [`WriteBlockUndo`](https://github.com/bitcoin/bitcoin/blob/bb5296576e8f1a9fc11c19d9a25ba02ed4547e24/src/node/blockstorage.cpp#L991-L995)/`rev*.dat` via `ConnectBlock`, but it\n  looks like `-reindex` has [historically](https://github.com/bitcoin/bitcoin/pull/6221) deleted `rev*.dat` files upfront\n  specifically to keep block-file bookkeeping in sync with disk -- so this one\n  is probably not a real issue\n\nnot claiming either is a live bug, just noting that the same pattern can turn up\nelsewhere in the caller graph. that is why i am leaning towards keeping the\nbackstop (_at least_) rather than scoping the fix to genesis alone\n\nthoughts on the approach ?",
   "id": "c:5757418198",
   "url": "https://github.com/bitcoin/bitcoin/pull/35524#issuecomment-5757418198",
   "hash": "99f2c29cbb2f5a8b",
   "edited": "2026-09-21T21:02:32Z"
  },
  {
   "t": "2026-09-21T17:42:10Z",
   "kind": "force_push",
   "who": "winterrdog",
   "commit": "a1c412e93cc35b0b896af0325fe74f9f6d9b1603",
   "id": "push:a1c412e93cc35b0b896af0325fe74f9f6d9b1603"
  },
  {
   "t": "2026-09-21T17:43:42Z",
   "kind": "comment",
   "who": "winterrdog",
   "assoc": "CONTRIBUTOR",
   "text": "for now, i am marking this as draft so that i can work on the new implementation of the fix",
   "id": "c:5764911367",
   "url": "https://github.com/bitcoin/bitcoin/pull/35524#issuecomment-5764911367",
   "hash": "5402540f1b7e0757",
   "edited": "2026-09-21T20:41:43Z"
  },
  {
   "t": "2026-09-22T20:31:04Z",
   "kind": "review_comment",
   "who": "winterrdog",
   "assoc": "CONTRIBUTOR",
   "path": "src/util/fs_helpers.cpp",
   "commit": "65b44fcf0b664a4bd51beca5bd53010885b5fe2a",
   "in_reply_to": 3423287110,
   "text": "from https://github.com/bitcoin/bitcoin/pull/35524#issuecomment-5742883004:\n[quoted text omitted]\n\ndue to this reason, i am dropping this. we might be doing too much here",
   "id": "rc:4076176279",
   "url": "https://github.com/bitcoin/bitcoin/pull/35524#discussion_r4076176279",
   "hash": "f92b521e2e4dcdce",
   "edited": null
  },
  {
   "t": "2026-09-26T21:02:43Z",
   "kind": "force_push",
   "who": "winterrdog",
   "commit": "0fcca3997605ed4e9af7db922418ded7390955cf",
   "id": "push:0fcca3997605ed4e9af7db922418ded7390955cf"
  },
  {
   "t": "2026-09-26T21:16:14Z",
   "kind": "force_push",
   "who": "winterrdog",
   "commit": "1a3d4ea56cdc9b10b484d95116988a0d396ce5c5",
   "id": "push:1a3d4ea56cdc9b10b484d95116988a0d396ce5c5"
  },
  {
   "t": "2026-09-26T21:22:09Z",
   "kind": "force_push",
   "who": "winterrdog",
   "commit": "fd2d21053f014e76f44fb72ff081240cd3a2b519",
   "id": "push:fd2d21053f014e76f44fb72ff081240cd3a2b519"
  },
  {
   "t": "2026-09-26T21:23:25Z",
   "kind": "comment",
   "who": "winterrdog",
   "assoc": "CONTRIBUTOR",
   "text": "changes made:\n- rebased on latest `master` and pushed the new fix implementation\n- updated *both* the PR title and description to better capture what is being fixed in this PR as requested in https://github.com/bitcoin/bitcoin/pull/35524#issuecomment-5742883004\n\nalso, i verified that the fix works correctly on both NetBSD `11.0` and FreeBSD `15.0`",
   "id": "c:5850005755",
   "url": "https://github.com/bitcoin/bitcoin/pull/35524#issuecomment-5850005755",
   "hash": "615ea6be9afb9063",
   "edited": "2026-09-26T22:38:08Z"
  },
  {
   "t": "2026-09-30T19:13:15Z",
   "kind": "comment",
   "who": "winterrdog",
   "assoc": "CONTRIBUTOR",
   "text": "@sedited\n\nfrom: https://github.com/bitcoin/bitcoin/pull/35524#issuecomment-5742883004\n\n[quoted text omitted]\nuhmm! i tried this approach, but it did not fix the issue.\n\ni used the following patch to implement your suggestion, along with forcing the\nfallback path since i was testing on Linux:\n\n    diff\n\n```diff\ndiff --git a/src/util/fs_helpers.cpp b/src/util/fs_helpers.cpp\nindex 9723c145de..5dfb53c680 100644\n--- a/src/util/fs_helpers.cpp\n+++ b/src/util/fs_helpers.cpp\n@@ -224,7 +224,7 @@ void AllocateFileRange(FILE* file, unsigned int offset, unsigned int length)\n     }\n     ftruncate(fileno(file), static_cast<off_t>(offset) + length);\n #else\n-#if defined(HAVE_POSIX_FALLOCATE)\n+#if 0\n     // Version using posix_fallocate\n     off_t nEndPos = (off_t)offset + length;\n     if (0 == posix_fallocate(fileno(file), 0, nEndPos)) return;\ndiff --git a/src/validation.cpp b/src/validation.cpp\nindex c85a3af730..919fee915e 100644\n--- a/src/validation.cpp\n+++ b/src/validation.cpp\n@@ -4957,10 +4957,21 @@ bool ChainstateManager::LoadGenesisBlock()\n     }\n\n     try {\n-        FlatFilePos blockPos{m_blockman.WriteBlock(genesis_block, 0)};\n-        if (blockPos.IsNull()) {\n-            LogError(\"Writing genesis block to disk failed\");\n-            return false;\n+        FlatFilePos blockPos{0, 0};\n+        // The block index may be missing or corrupted (e.g. after an\n+        // incomplete shutdown or manual recovery) while block file 0\n+        // still contains a valid genesis block from a previous run.\n+        // Check for that first. Otherwise, WriteBlock() eventually\n+        // reaches AllocateFileRange(), whose fallback on platforms without\n+        // a real preallocation syscall cannot tell that the file already\n+        // contains data beyond this point and may zero it out.\n+        CBlock existing_block;\n+        if (!m_blockman.ReadBlock(existing_block, blockPos, genesis_block.GetHash())) {\n+            blockPos = m_blockman.WriteBlock(genesis_block, 0);\n+            if (blockPos.IsNull()) {\n+                LogError(\"Writing genesis block to disk failed\");\n+                return false;\n+            }\n         }\n         CBlockIndex* pindex{m_blockman.AddToBlockIndex(genesis_block, m_best_header)};\n         ReceivedBlockTransactions(genesis_block, pindex, blockPos);\n```\n\ni then ran `./build/test/functional/test_runner.py feature_reindex_init`, which\nstill failed with `getblockcount()` returning `0` instead of the expected\n`200`:\n\n    output\n\n```text\nTemporary test directory at /tmp/test_runner_\u20bf_\ud83c\udfc3_20260930_215123\nRemaining jobs: [feature_reindex_init.py]\n1/1 - feature_reindex_init.py failed (exit code 1), Duration: 5 s\n\nstdout:\n2026-09-30T18:51:23.738462Z TestFramework (INFO): PRNG seed is: 3969550340272191845\n2026-09-30T18:51:23.789421Z TestFramework (INFO): Initializing test directory /tmp/test_runner_\u20bf_\ud83c\udfc3_20260930_215123/feature_reindex_init_0\n2026-09-30T18:51:27.014326Z TestFramework (INFO): Removing the block index leads to init error\n2026-09-30T18:51:27.784859Z TestFramework (INFO): Allowing the reindex should work fine\n2026-09-30T18:51:29.057559Z TestFramework (ERROR): Unexpected exception:\nTraceback (most recent call last):\n  File \"/home/madman/Documents/btc/btc-core/my-btc-fork/test/functional/test_framework/test_framework.py\", line 145, in main\n    self.run_test()\n  File \"/home/madman/Documents/btc/btc-core/my-btc-fork/build/test/functional/feature_reindex_init.py\", line 29, in run_test\n    assert_equal(node.getblockcount(), 200)\n  File \"/home/madman/Documents/btc/btc-core/my-btc-fork/test/functional/test_framework/util.py\", line 94, in assert_equal\n    raise AssertionError(\"not(%s)\" % \" == \".join(str(arg) for arg in (thing1, thing2) + args))\nAssertionError: not(0 == 200)\n2026-09-30T18:51:29.112141Z TestFramework (INFO): Not stopping nodes as test failed. The dangling processes will be cleaned up later.\n2026-09-30T18:51:29.112592Z TestFramework (WARNING): Not cleaning up dir /tmp/test_runner_\u20bf_\ud83c\udfc3_20260930_215123/feature_reindex_init_0\n2026-09-30T18:51:29.112854Z TestFramework (ERROR): Test failed. Test logging available at /tmp/test_runner_\u20bf_\ud83c\udfc3_20260930_215123/feature_reindex_init_0/test_framework.log\n\n...\n\nTEST                    | STATUS    | DURATION\n\nfeature_reindex_init.py | \u2716 Failed  | 5 s\n\nALL                     | \u2716 Failed  | 5 s (accumulated)\nRuntime: 5 s\n```\n\ntested both fixes independently and it turns out the `ReadBlock()` check in\n`ChainstateManager::LoadGenesisBlock()` alone does not fix the reported bug;\nthe test still fails _without_ the `AllocateFileRange` guard unlike the one _with_ the\n`AllocateFileRange` guard",
   "id": "c:5917957172",
   "url": "https://github.com/bitcoin/bitcoin/pull/35524#issuecomment-5917957172",
   "hash": "de66371ee5de1e3e",
   "edited": "2026-09-30T19:44:16Z"
  }
 ],
 "labels_log": [
  {
   "t": "2026-06-13T10:12:24Z",
   "action": "labeled",
   "label": "Utils/log/libs",
   "who": "DrahtBot"
  },
  {
   "t": "2026-09-26T21:22:52Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-09-26T22:26:16Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-10-26T20:57:48Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  }
 ],
 "state_log": [
  {
   "t": "2026-09-21T17:42:32Z",
   "kind": "convert_to_draft",
   "who": "winterrdog"
  },
  {
   "t": "2026-09-26T20:37:11Z",
   "kind": "renamed",
   "who": "winterrdog",
   "from": "util: Prevent file overwriting in fallback `AllocateFileRange` implementation",
   "to": "validation: Avoid rewriting the genesis block during index recovery"
  }
 ],
 "text_chars": 16361,
 "text_tokens_estimate": 4090,
 "changed_paths": [
  "src/util/fs_helpers.cpp",
  "src/validation.cpp"
 ],
 "files": [
  {
   "path": "src/util/fs_helpers.cpp",
   "add": 25,
   "del": 15
  },
  {
   "path": "src/validation.cpp",
   "add": 15,
   "del": 4
  }
 ],
 "test_lines": 0,
 "git": {
  "head": "fd2d21053f014e76f44fb72ff081240cd3a2b519",
  "head_matches_backup": true,
  "base": "ed7dd7cf4e1561a97edf72eba29a67b14e28c717",
  "patch_id": "76b57f3764e49b3c",
  "commits": [
   {
    "sha": "ec80643e04",
    "subject": "util: Fix data clobbering in `AllocateFileRange()`",
    "files": 1,
    "add": 25,
    "del": 15
   },
   {
    "sha": "fd2d21053f",
    "subject": "validation: Avoid rewriting genesis block when it's already on disk",
    "files": 1,
    "add": 15,
    "del": 4
   }
  ],
  "patch_truncated": false
 },
 "patch_id": "76b57f3764e49b3c",
 "input_hash": "628ef839f0f868b7",
 "extracted_at": "2026-11-02T01:09:49+00:00"
}