Repository navigation
Conversation
load() assigned result->reader = readNormalIncomplete after KThreadMake(). When the background thread loaded a small reference before that assignment ran, it set reader = readNormal and async = NULL, and the late assignment put back readNormalIncomplete. The next RefSeq_getBases() took the fast path (async == NULL) into readNormalIncomplete(), which dereferenced the NULL async and crashed.
…ering runLoadThread() signals that the bases and the final reader are ready by setting async = NULL, and RefSeq_getBases() takes its lock-free fast path when it reads async == NULL. Both were plain volatile accesses, so on a weakly ordered CPU (e.g. arm64) a reader could see async == NULL but a stale reader or stale bases. Use a release store and acquire loads.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Thank you for maintaining ncbi-vdb. This fixes the segfault described in #280, where a small external reference finishes background loading before
load()returns.Problem
load()inlibs/axf/refseq.cstarts the background loading thread and only then setsresult->reader = readNormalIncomplete. For a small reference, the thread can finish first: it setsreader = readNormalandasync = NULL, and thenload()putsreadNormalIncompleteback. The nextRefSeq_getBases()seesasync == NULLand takes the fast path intoreadNormalIncomplete(), which dereferences the NULLasync(async->rr.first, offset 0x18). This crashes fastq-dump 3.4.1 on ERR14844919. Details and stacks are in the issue.Changes
Two commits, so the first can be taken on its own:
readNormalIncompleteassignment ahead ofKThreadMake().pthread_create()orders it before anything the thread does, so the thread's finalreaderalways wins. This commit fixes the crash.runLoadThread()clearsasyncwith a release store, andRefSeq_getBases()reads it with acquire loads. A reader that seesasync == NULLis then guaranteed to also see the finalreaderand the loaded bases. Plainvolatiledoes not guarantee that on arm64. The change uses__atomic_*under__GNUC__(gcc and clang), following the__GNUC__/_MSC_VERsplit inlibs/klib/hashfile.c. Other compilers keep thevolatileaccess; on MSVC x64 that already has acquire/release semantics (/volatile:ms, the default). The fast path stays lock-free. On x86-64, acquire loads and release stores compile to plainmovs; on arm64 they becomeldar/stlr.Testing
On macOS 26.6.1 arm64, Apple clang,
./configure --with-debug && make all:libs/axf.Test_Axf_RestoreReadpasses. It is the only test that exercisesrefseq.c, and it reads NC_000001.11 remotely.pthread_createshim that waits for the new thread to finish (up to 1 s) before returning, and a harness that callsRestoreReadGetSequence()on the 205 RefSeq-format references of ERR14844919:engineering:assert(async != NULL)inpositionToRowon the first reference, 5 of 5 runs.No regression test is included. Hitting the window reliably needs a hook such as the
pthread_createshim above, which doesn't fit the existing test framework. The shim and harness are in the issue, and I'm happy to turn them into a test if you can suggest a place for one.Not tested on Linux, Windows or x86-64. Commit 1 is platform-independent. On Windows, commit 2 relies on MSVC's default
/volatile:msfor x64.I dedicate these changes to the public domain, consistent with the project's Public Domain Notice. Thanks for considering them; I'm glad to rework the change in whatever way you prefer.