summaryrefslogtreecommitdiff
path: root/t/unit-tests
AgeCommit message (Collapse)AuthorFilesLines
6 daysMerge branch 'kn/reftable-optimize-reloading'Junio C Hamano1-39/+60
The reftable code has been optimized to avoid an unnecessary stat/reload of the stack when an addition already holds the list_file lock, reducing the number of newfstatat syscalls from linear to constant when writing refs. * kn/reftable-optimize-reloading: reftable/stack: avoid reloading the stack when already locked reftable/stack: move list lock to `struct reftable_stack` reftable/stack: rename reftable_stack_new_addition() reftable/stack: remove `REFTABLE_STACK_NEW_ADDITION_RELOAD`
13 daysMerge branch 'kh/trailers-no-urls'Junio C Hamano1-0/+52
The trailer parsing machinery has been updated to avoid mistaking lines that begin with a URL (e.g., 'https://...') as trailer lines. This prevents intended textual URLs from being mangled or mistakenly treated as metadata keys. * kh/trailers-no-urls: trailers: stop recognizing URLs as trailers
13 daysMerge branch 'ps/odb-generic-corrupt-objects'Junio C Hamano1-2/+3
The object database (odb) API has been refactored to distinguish between missing objects and corrupt ones by returning more descriptive error statuses. Both the packed and loose backends now faithfully propagate error details using a generic strbuf error mechanism, removing backend-specific leakage from central lookup paths. * ps/odb-generic-corrupt-objects: odb: handle `OBJECT_INFO_DIE_IF_CORRUPT` generically odb/source: allow `read_object_info()` to bubble up error messages odb/source: let callers discern missing and corrupt objects odb/source: introduce error status when reading objects odb/source-packed: flag known-bad objects as corrupt and not missing
2026-08-24Merge branch 'js/coverity-unchecked-returns-fix'Junio C Hamano1-2/+4
A handful of code paths have been corrected to check return values from functions like curl_easy_duphandle(), deflateInit(), lseek(), dup(), and strbuf_getline_lf(), resolving several Coverity warnings about unchecked returns. * js/coverity-unchecked-returns-fix: bisect: handle dup() failure when redirecting stdout bisect: check get_terms return at all call sites bisect: check strbuf_getline_lf return when reading terms transport-helper: warn when export-marks file cannot be finalized transport-helper: check dup() return in get_exporter compat/pread: check initial lseek for errors last-modified: handle repo_parse_commit() failures reftable tests: check reftable_table_init_ref_iterator() return reftable/block: check deflateInit() return value reftable: handle block-writer initialization errors config: propagate launch_editor() failure in show_editor() http: die on curl_easy_duphandle failure in get_active_slot
2026-08-24reftable/stack: move list lock to `struct reftable_stack`Karthik Nayak1-0/+28
The struct `reftable_addition` is used to modify a given stack, as such, it also includes a `struct reftable_flock` used to obtain the lock to the list file. While the scope of the field lies within this struct, it doesn't allow for optimizations to be made on `struct reftable_stack` itself. Move the field to `struct reftable_stack`, allowing us to make a simple optimization around avoiding a stack reload when we have already obtained a lock. While this is currently possible in the write path, the write path also contains multiple branches to reads which only work on top of `struct reftable_stack`, and we would miss the optimization in such paths. Since the lock is now shared across all additions on the same stack, a second `reftable_addition` that fails to acquire the already held lock would still call `reftable_addition_close()`, which will release the `stack->list_lock` which is still held by the first addition. To avoid this, add a new bit field `locked` to `reftable_addition` that tracks whether a particular addition is the one holding the lock, and only release it in that case. Add a unit test to validate this behavior. While here, remove an unused header file from 'reftable/stack.h'. Signed-off-by: Karthik Nayak <karthik.188@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-08-24reftable/stack: rename reftable_stack_new_addition()Karthik Nayak1-5/+5
Rename the function `reftable_stack_new_addition()` to `reftable_stack_addition_new()` to be more inline with our naming scheme. Signed-off-by: Karthik Nayak <karthik.188@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-08-24reftable/stack: remove `REFTABLE_STACK_NEW_ADDITION_RELOAD`Karthik Nayak1-38/+31
In 80e7342ea8 (reftable/stack: allow locking of outdated stacks, 2024-09-24), the `REFTABLE_STACK_NEW_ADDITION_RELOAD` was introduced so that callers of `reftable_stack_init_addition()` can also reload the stack if there was a concurrent update made before the lock was obtained. Then 16684b6fae (refs/reftable: always reload stacks when creating lock, 2025-08-12) updated all of the remaining call-sites to propagate this flag to ensure that we always reload the stack whenever there was a concurrent update. As all calls to `reftable_stack_init_addition()` inevitably propagate the flag, it is safe to remove the flag and its associated code and make the reloading of the stack the default flow. This makes it easier to follow the flow and simplifies the logic. The only exceptions are: 1. Unit tests, where we explicitly do not propagate the flag. These tests are now modified with the new status quo. 2. `reftable_stack_clean()`, which was propagating 0 to `reftable_stack_new_addition()` but was then manually reloading the stack after. Here the new flow will achieve the same, while also allowing us to remove the manual reload. This also makes two checks for 'REFTABLE_OUTDATED_ERROR' redundant, so remove them also. Signed-off-by: Karthik Nayak <karthik.188@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-08-23Merge branch 'ps/odb-streams'Junio C Hamano1-17/+20
The 'struct odb_read_stream' and 'struct odb_write_stream' structures have been consolidated into a single unified 'struct odb_stream' structure, simplifying object database streaming APIs and enabling streaming of arbitrary object types. * ps/odb-streams: odb/streaming: unify function names to create new streams odb/streaming: rename `struct input_zstream_data` odb/streaming: rename `struct read_object_fd_data` odb/streaming: consolidate read and write streams odb/streaming: rename `struct odb_read_stream` odb/streaming: support streaming arbitrary object types odb/streaming: drop `is_finished` field odb/streaming: track write stream size in the structure
2026-08-21trailers: stop recognizing URLs as trailersKristoffer Haugsbakk1-0/+52
An HTTPS URL starts with an alphanumeric scheme followed by a colon. That means that they will be recognized as trailers in a trailer block. That turns out to be a problem in practice. Let’s stop recognizing these as trailers by failing the trailer parsing when we: 1. find the separator; 2. the separator and the next two characters form `://`; and 3. we haven’t parsed any whitespace yet. The simplest example of how this can be a problem is for people who do not use trailers but may leave URLs at the end of the commit message. Now, while these authors might not use trailers themselves, other authors may have used trailers and this metadata confusion can become a problem once someone tries to extract that metadata (and non-metadata). Let’s now look at some examples in the Linux Kernel[1] to see how this is a problem in practice. There are commits which contain intended non-trailer lines which start with URLs. These are comments. Example with just the trailers:[2] Signed-off-by: Shuai Xue <xueshuai@linux.alibaba.com> [bhelgaas: squash fixes: https://lore.kernel.org/r/20260108013956.14351-2-bagasdotme@gmail.com https://lore.kernel.org/r/20260108013956.14351-3-bagasdotme@gmail.com] Signed-off-by: Bjorn Helgaas <bhelgaas@google.com> Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com> Link: https://patch.msgid.link/20251210132907.58799-4-xueshuai@linux.alibaba.com Those `[]` pairs delimit the “squash fixes” comment. Now, any of these two commands: git log --format='%(trailers:only)' -1 <commit> git log -1 --format=%B <commit> | git interpret-trailers --only-trailers Will both wrongly (according to the surmised user intent) include these two URL lines as trailers and also mangle the URLs, e.g.: https: //lore.kernel.org/r/20260108013956.14351-2-bagasdotme@gmail.com Because the `--only-trailers` mode (or `only` for the git-log(1) format) normalizes the output to a colon and a space. Another example is linewrapping mistakes; a `Link` trailer with a URL where the URL ended up on the next line, presumably because the user’s editor linewrapped the “too long” line. Example with just the trailers:[3] Link: https://patch.msgid.link/20260216-work-xattr-socket-v1-4-c2efa4f74cb7@kernel.org Link: https://lore.kernel.org/3cnmtqmakpbb2uwhenrj7kdqu3uefykiykjllgfbtpkiwhaa4s@sghkevv7jned [1] Acked-by: Darrick J. Wong <djwong@kernel.org> Reviewed-by: Jan Kara <jack@suse.cz> Signed-off-by: Christian Brauner <brauner@kernel.org> Now, this intended trailer is already ruined, but interpreting the URL as a standalone trailer only compounds the mistake. Yet another example is the trailer machinery normalizing the trailer block before application, resulting in a `https` trailer key in the commit message itself. Example with just the trailers:[4] https: //sashiko.dev/#/patchset/20260429114208.941011-1-holger.brunck%40hitachienergy.com Fixes: c19b6d246a35 ("drivers/net: support hdlc function for QE-UCC") Signed-off-by: Holger Brunck <holger.brunck@hitachienergy.com> Link: https://patch.msgid.link/20260507155332.3452319-1-holger.brunck@hitachienergy.com Signed-off-by: Jakub Kicinski <kuba@kernel.org> We have a helpful `Link` that points to the original patch.[5] Following it we can see that that `https` trailer was indeed a URL originally (again just the trailer block here): https://sashiko.dev/#/patchset/20260429114208.941011-1-holger.brunck%40hitachienergy.com Fixes: c19b6d246a35 ("drivers/net: support hdlc function for QE-UCC") Signed-off-by: Holger Brunck <holger.brunck@hitachienergy.com> So how did it end up as a `https` trailer? My theory is that the trailer block was normalized on patch application, causing a URL comment to be wrongly normalized and cemented in the commit message as a trailer.[6] † 1: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/ † 2: commit 8236fc613d44e59f6736d6c3e9efffaf26ab7f00 † 3: commit 5bd97f5c5f241a5610c4412d1b93995a26241f81 † 4: commit 496c0c4c53bbe1bad97e82cd12103df61a6e459d † 5: https://patch.msgid.link/20260507155332.3452319-1-holger.brunck@hitachienergy.com † 6: There are only four commits in the Linux Kernel of this kind, and three of them have the same recurring person in the signoff chain. *** Note that this check has some benign false positives. A trailer key can start with a digit, but a URL scheme can not start with a digit. That means that a line that starts with `1://` will be rejected even though it cannot be a URL. I don’t think this will reject any real trailers, so I think the implementation simplicity is worth it. And these false positives are just for a limited start fragment check; a mere heuristic, not a URL parser. Helped-by: Jeff King <peff@peff.net> Acked-by: Jeff King <peff@peff.net> Signed-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-08-20Merge branch 'ps/odb-make-creation-pluggable'Junio C Hamano1-1/+1
The creation of the on-disk data structures for the object database has been made pluggable, allowing future backends to customize their setup. As part of this, the initialization of the object database has been deferred, and the loading of the loose-object map has been detangled from repository initialization. * ps/odb-make-creation-pluggable: odb: make creation of on-disk structures pluggable odb/source: introduce function to map source type to name setup: defer object database creation setup: handle ODB-related environment variables in `odb_new()` setup: detangle loading of loose object maps loose: load loose object map for the correct source
2026-08-19odb/source: allow `read_object_info()` to bubble up error messagesPatrick Steinhardt1-2/+2
When reading an object fails even though it exists, the sources know best what exactly went wrong and where the corrupt object is located. This information is lost though when bubbling up the error to the object database layer, which forces that layer to reconstruct it after the fact. This is exactly what `do_oid_object_info_extended()` does via `has_packed_and_bad()`, but that function only really knows to handle the "files" backend by reaching into its internals. Introduce a new `errmsg` parameter for the `read_object_info()` callback that sources are expected to populate with a human-readable message in case reading the object has failed. Adapt the packed and loose sources to populate the buffer with the messages that we ultimately want to surface to the user. For now, all callers are adapted to pass a `NULL` pointer. We will add a user of this new infrastructure in a subsequent commit. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-08-19odb/source: let callers discern missing and corrupt objectsPatrick Steinhardt1-1/+2
As explained in the preceding commits, reading objects can either fail because the object truly does not exist or because it exists, but its data is corrupt. Some callers do care about this distinction, but there is no way to tell these two cases apart right now. Introduce a new `ODB_READ_NOT_FOUND` value that ought to be returned by the backends in case the object truly does not exist and adapt backends to use it. Note that we don't yet return this error from `odb_read_object_info()` itself. This will be fixed in a subsequent commit. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-08-12reftable tests: check reftable_table_init_ref_iterator() returnJohannes Schindelin1-2/+4
test_reftable_table__seek_once() and test_reftable_table__reseek() both call reftable_table_init_ref_iterator() without checking its return value. This function returns an int error code (0 on success, negative on failure). Every other reftable function call in these same tests checks the return via cl_assert_equal_i() or cl_assert(), making this omission inconsistent. If the iterator initialization ever fails (e.g., due to a memory allocation failure in the reftable internals), the test would proceed to seek and read with an uninitialized iterator, producing misleading test results or crashes rather than a clear assertion failure. Check the return value via cl_assert_equal_i(ret, 0), consistent with the surrounding code. Pointed out by Coverity. Assisted-by: Claude Opus 4.6 Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-08-06setup: handle ODB-related environment variables in `odb_new()`Patrick Steinhardt1-1/+1
When initializing a repository's object database we have to respect the GIT_OBJECT_DIRECTORY and GIT_ALTERNATE_OBJECT_DIRECTORIES environment variables, which can be set by the user to override the default location of where we write objects to and read objects from. This is handled in `apply_repository_format()`, which is fine. But in a subsequent commit we'll have to defer constructing the object database to a later point in some cases, and that will require a second site where we call `odb_new()`. And of course, that second site would have to handle those environment variables, as well. It would be somewhat awkward to duplicate the logic though. But there's a better alternative: instead of handling this logic in "setup.c", we can easily handle environment variables in `odb_new()` itself. This ensures that object database creation is neatly self-contained, and we don't have to duplicate any of the logic. Another benefit is that in a future patch series we plan to move handling of alternates into the backends themselves [1], and that will require us to also handle those environment variables in the "files" backend itself. So moving the logic into the ODB level already gets us one step closer to that goal. Refactor the logic accordingly. [1]: https://lore.kernel.org/git/amLgMqkqxR8mKIbT@pks.im/ Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-08-05odb/streaming: consolidate read and write streamsPatrick Steinhardt1-3/+3
The `struct odb_read_stream` and `struct odb_write_stream` both provide the same functionality: they allow a caller to read object data from an arbitrary source. Historically, the only difference was that the read stream was used to read data out of the object database, whereas the write stream was used to write data into the object database, but the interfaces were mostly the same. Over the preceding commits we have refactored the write stream to have almost exactly the same interface as the read stream. With these refactorings we can now easily merge those two streams into a single interface that's used for both use cases. While most of the changes are mechanical, there are two sites that need special mention: - "builtin/unpack-objects.c" creates a write stream from compressed object data. - "odb/streaming.c" creates a write stream from a file descriptor. Adapting these sites to yield the new stream type requires a couple more changes. Most importantly, instead of embedding the pointer to the data in `struct odb_write_stream`, we now allocate a structure that wraps the new `struct odb_stream` base. Other than that though, the changes are rather straight forward. Some of the structures and functions are now somewhat misnamed. These will be fixed in subsequent commits. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-08-05odb/streaming: rename `struct odb_read_stream`Patrick Steinhardt1-6/+6
Rename `struct odb_read_stream` to just `struct odb_stream`. This prepares for unification of the two different types of streams, as these provide the same functionality with the preceding refactorings. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-08-05odb/streaming: support streaming arbitrary object typesPatrick Steinhardt1-2/+5
The object database supports the ability to write object streams into it. This functionality is used when we encounter a blob that is larger than "core.bigFileThreshold" so that we don't have to soak large files into memory. As we only ever write large files, the infrastructure doesn't support specifying any other object type than "blob". This limitation is quite artificial though: there is no reason why we shouldn't support writing arbitrary large objects with a stream. While it's very unlikely that we encounter a huge object other than a blob, users are known to be creative and sometimes like to inflict pain on themselves by creating commits or trees that are huge. Extend the infrastructure to support streaming arbitrary object types. For now we don't use this functionality anywhere, but it brings us a bit closer to unify `struct odb_read_stream` and `struct odb_write_stream`. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-08-05odb/streaming: drop `is_finished` fieldPatrick Steinhardt1-2/+3
The `is_finished` field is used to track whether a write stream is done writing all of its data. Tracking this field as part of the stream itself shouldn't be required though: callers will already know when the stream is done when the stream's read function returns zero bytes, same as when reading from a file descriptor. There is one exception where it gets a bit more complicated: when consuming data in "builtin/unpack-objects.c" it may happen that we don't yield any new bytes after reading from the pipe. This is addressed by looping until we have produced at least a single byte of output. Drop the field from `struct odb_write_stream`. Again, same as in the preceding commit, this brings the structure a bit closer to its sibling `struct odb_read_stream`. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-08-05odb/streaming: track write stream size in the structurePatrick Steinhardt1-6/+5
When passing around a `struct odb_write_stream` we typically also have to pass the number of bytes that the stream will yield. This is required because the object header itself contains that size, and consequently we cannot write the header without that information. Move this information into the stream itself so that it becomes self- describing. In addition to that, this also brings the `struct odb_write_stream` a bit closer to the `struct odb_read_stream` so that we can eventually merge both stream types. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-07-30Merge branch 'ps/odb-move-loose-object-writing'Junio C Hamano1-27/+26
The logic to write loose objects has been refactored and moved from 'object-file.c' to the loose backend source file 'odb/source-loose.c', making the loose backend more self-contained. This is achieved by first refactoring force_object_loose() to use generic ODB write interfaces instead of loose-backend internals. * ps/odb-move-loose-object-writing: object-file: move logic to write loose objects object-file: move `force_object_loose()` object-file: force objects loose via generic interface object-file: fix memory leak in `force_object_loose()` odb: support setting mtime when writing objects odb: lift object existence check out of the "loose" backend odb: compute object hash in `odb_write_object_ext()` t/u-odb-inmemory: implement wrapper for writing objects odb: compute compat object ID in `odb_write_object_ext()`
2026-07-19Merge branch 'ps/reftable-hardening'Junio C Hamano4-19/+297
The 'reftable' code has been hardened against corrupted tables by fixing out-of-bounds writes, out-of-bounds reads, and abort calls during parsing. * ps/reftable-hardening: reftable/table: fix OOB read on truncated table reftable/table: fix NULL pointer access when seeking to bogus offsets reftable/block: fix OOB read with bogus restart offset reftable/block: fix use of uninitialized memory when binsearch fails reftable/block: fix OOB read with bogus restart count reftable/block: fix OOB read with bogus block size reftable/block: fix OOB write with bogus inflated log size t/unit-tests: introduce test helper to write reftable blocks reftable/record: don't abort when decoding invalid ref value type reftable/basics: fix OOB read on binary search of empty range oss-fuzz: add fuzzer for parsing reftables meson: support building fuzzers with libFuzzer
2026-07-18odb: support setting mtime when writing objectsPatrick Steinhardt1-3/+3
The function `force_object_loose()` is used to loosen packed objects before repacking. It passes the pack's mtime along so that the newly written loose object inherits the same timestamp. This matters for object pruning, which uses the mtime to determine whether an object is old enough to be pruned. In a subsequent commit, `force_object_loose()` will be converted to use the generic `odb_source_write_object()` interface instead of calling `write_loose_object()` directly. But the generic interface doesn't yet support setting a specific mtime, which makes it impossible to implement the logic as of now. Prepare for the change by introducing a new `mtime` parameter to this function that we plumb through the stack. If set, the backends are instructed to set the object's mtime accordingly. If unset, the backends are expected to use the current time instead. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-07-18odb: compute object hash in `odb_write_object_ext()`Patrick Steinhardt1-0/+1
Same as in a preceding commit, compute the object hash in `odb_write_object_ext()` so that we can unify this logic. Besides unification, this change also allows us to lift the object existence check out of the "loose" backend into the generic layer, which will happen in the next commit. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-07-18t/u-odb-inmemory: implement wrapper for writing objectsPatrick Steinhardt1-25/+23
In the next commit we're about to change how objects are being written into the object database source. Prepare for this refactoring by introducing a wrapper function into our unit tests so that we don't have to adjust all callsites. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-07-16Merge branch 'jk/git-hash-cleanups'Junio C Hamano1-1/+1
The 'git_hash_*()' wrappers have been updated to be used consistently across the codebase instead of direct calls to members of 'struct git_hash_algo', and 'git_hash_discard()' has been made idempotent to simplify cleanups. * jk/git-hash-cleanups: hash: check ctx->active flag in all wrapper functions http: use idempotent git_hash_discard() csum-file: use idempotent git_hash_discard() hash: make git_hash_discard() idempotent hash: document function pointers and wrappers hash: convert remaining direct function calls hash: use git_hash_init() consistently
2026-07-13Merge branch 'kk/prio-queue-get-put-fusion'Junio C Hamano1-3/+3
The lazy priority queue optimization pattern (deferring actual removal in 'prio_queue_get()' to allow get+put fusion) has been folded directly into 'prio_queue' itself, speeding up commit traversal workflows and simplifying callers. * kk/prio-queue-get-put-fusion: prio-queue: fold lazy_queue into prio_queue for automatic get+put fusion prio-queue: rename .nr to .nr_ and add accessor helpers
2026-07-07hash: use git_hash_init() consistentlyJeff King1-1/+1
We'd like to add more logic to git_hash_init(), but many callers skip it and call algop->init_fn() directly. Let's make sure we're consistently using the wrapper by adding a coccinelle rule. Besides the coccinelle file itself, this is a purely mechanical conversion based on the patch it generates. There should be no bare init_fn() calls left (except for the one in the wrapper). Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-07-06Merge branch 'ps/refs-onbranch-fixes'Junio C Hamano6-124/+130
Reference backend configuration has been updated to load lazily to avoid recursive calls during repository initialization when 'onbranch' configuration conditions are evaluated. This has also fixed a memory leak and allowed the unused `chdir_notify_reparent()` machinery to be dropped. * ps/refs-onbranch-fixes: refs: protect against chicken-and-egg recursion refs/reftable: lazy-load configuration to fix chicken-and-egg reftable: split up write options refs/files: lazy-load configuration to fix chicken-and-egg refs: move parsing of "core.logAllRefUpdates" back into ref stores repository: free main reference database chdir-notify: drop unused `chdir_notify_reparent()` refs: unregister reference stores from "chdir_notify" setup: don't apply "GIT_REFERENCE_BACKEND" without a repository setup: stop applying repository format twice setup: inline `check_and_apply_repository_format()`
2026-07-03reftable/table: fix OOB read on truncated tablePatrick Steinhardt1-0/+28
When opening a table we compute the size of its data section by subtracting the footer size from the file size. We do not verify that the file is actually large enough to contain both the header and the footer though. With a truncated table the subtraction can thus underflow, causing us to read the footer out of bounds: SUMMARY: AddressSanitizer: heap-buffer-overflow (/home/pks/Development/git/build/t/unit-tests+0x2479a4) in __asan_memcpy Shadow bytes around the buggy address: 0x7ccff6e0de80: fa fa fa fa fa fa fa fa fd fd fd fd fd fd fd fd 0x7ccff6e0df00: fd fd fd fd fd fd fd fd fd fa fa fa fa fa fa fa 0x7ccff6e0df80: fa fa fd fd fd fd fd fd fd fd fd fd fd fd fd fd 0x7ccff6e0e000: fd fd fd fd fa fa fa fa fa fa fa fa fd fd fd fd 0x7ccff6e0e080: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fa fa =>0x7ccff6e0e100: fa fa fa fa fa[fa]00 00 00 00 00 00 00 00 00 00 0x7ccff6e0e180: 00 00 00 00 00 00 00 04 fa fa fa fa fa fa fa fa 0x7ccff6e0e200: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x7ccff6e0e280: 00 00 fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x7ccff6e0e300: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x7ccff6e0e380: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa Shadow byte legend (one shadow byte represents 8 application bytes): Addressable: 00 Partially addressable: 01 02 03 04 05 06 07 Heap left redzone: fa Freed heap region: fd Stack left redzone: f1 Stack mid redzone: f2 Stack right redzone: f3 Stack after return: f5 Stack use after scope: f8 Global redzone: f9 Global init order: f6 Poisoned by user: f7 Container overflow: fc Array cookie: ac Intra object redzone: bb ASan internal: fe Left alloca redzone: ca Right alloca redzone: cb ==1500371==ABORTING Verify that the file is large enough to contain both the header and the footer before computing the table size. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-07-03reftable/table: fix NULL pointer access when seeking to bogus offsetsPatrick Steinhardt1-0/+63
When seeking an iterator to an arbitrary offset we may return a positive value in case the offset points beyond the block. This makes sense when iterating through multiple blocks of the same section, as the positive value indicates to us that we're at the end of the table. But when the offset originates from a section or index offset it is supposed to point at a valid block, so an out-of-bounds value means that the table is corrupt. Treating it as a normal end-of-iteration causes us to silently report an empty section instead of surfacing the corruption, and we are left with a partially-initialized block. This may later on cause a NULL pointer exception: ==1486841==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x55555598e02c bp 0x7fffffff4eb0 sp 0x7fffffff4e70 T0) ==1486841==The signal is caused by a READ memory access. ==1486841==Hint: address points to the zero page. #0 0x55555598e02c in reftable_block_type ./git/build/../reftable/block.c:392:9 #1 0x55555598ee6e in block_iter_seek_key ./git/build/../reftable/block.c:536:35 #2 0x5555559ae553 in table_iter_seek_linear ./git/build/../reftable/table.c:344:8 #3 0x5555559adbff in table_iter_seek ./git/build/../reftable/table.c:450:9 #4 0x5555559ada9c in table_iter_seek_void ./git/build/../reftable/table.c:460:9 #5 0x555555992872 in reftable_iterator_seek_log_at ./git/build/../reftable/iter.c:281:9 #6 0x555555992953 in reftable_iterator_seek_log ./git/build/../reftable/iter.c:287:9 #7 0x55555583aa78 in test_reftable_table__seek_invalid_log_offset ./git/build/../t/unit-tests/u-reftable-table.c:257:20 #8 0x5555557f684e in clar_run_test ./git/build/../t/unit-tests/clar/clar.c:335:3 #9 0x5555557f2e69 in clar_run_suite ./git/build/../t/unit-tests/clar/clar.c:431:3 #10 0x5555557f2882 in clar_test_run ./git/build/../t/unit-tests/clar/clar.c:636:4 #11 0x5555557f375f in clar_test ./git/build/../t/unit-tests/clar/clar.c:687:11 #12 0x5555557fa49d in cmd_main ./git/build/../t/unit-tests/unit-test.c:62:8 #13 0x55555584cffa in main ./git/build/../common-main.c:9:11 #14 0x7ffff7a2b284 in __libc_start_call_main (/nix/store/57iz36553175g3178pvxjij8z5rcsd4n-glibc-2.42-61/lib/libc.so.6+0x2b284) (BuildId: 8ae0b698f2d4e727f569f64bb166e08ae30bd077) #15 0x7ffff7a2b337 in __libc_start_main@GLIBC_2.2.5 (/nix/store/57iz36553175g3178pvxjij8z5rcsd4n-glibc-2.42-61/lib/libc.so.6+0x2b337) (BuildId: 8ae0b698f2d4e727f569f64bb166e08ae30bd077) #16 0x555555694c24 in _start (./git/build/t/unit-tests+0x140c24) ==1486841==Register values: rax = 0x0000000000000000 rbx = 0x00007fffffff4ec0 rcx = 0x0000000000000000 rdx = 0x00007cfff6e2bd58 rdi = 0x00007cfff6e2bd58 rsi = 0x00007bfff5da1020 rbp = 0x00007fffffff4eb0 rsp = 0x00007fffffff4e70 r8 = 0x0000000000000000 r9 = 0x0000000000000002 r10 = 0x0000000000000000 r11 = 0x0000000000000017 r12 = 0x00007fffffff5908 r13 = 0x0000000000000001 r14 = 0x00007ffff7ffd000 r15 = 0x0000555556056e90 AddressSanitizer can not provide additional info. SUMMARY: AddressSanitizer: SEGV ./git/build/../reftable/block.c:392:9 in reftable_block_type ==1486841==ABORTING Fix this by returning a proper error in `table_iter_seek_to()` when the offset ranges beyond the block. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-07-03reftable/block: fix OOB read with bogus restart offsetPatrick Steinhardt1-0/+39
Restart points encode records in a given block that do not use prefix compression and that can thus immediately be seeked to. These offsets are encoded in the restart table, where each offset needs to point at one of the records of the block. We do not verify this though, so a bogus restart offset may cause an out-of-bounds read: ==1472280==ERROR: AddressSanitizer: SEGV on unknown address 0x7d8ff7de5f7f (pc 0x55555599502b bp 0x7fffffff4df0 sp 0x7fffffff4d40 T0) ==1472280==The signal is caused by a READ memory access. #0 0x55555599502b in get_var_int ./git/build/../reftable/record.c:30:6 #1 0x555555995c2a in reftable_decode_keylen ./git/build/../reftable/record.c:177:6 #2 0x55555598e85c in restart_needle_less ./git/build/../reftable/block.c:455:6 #3 0x55555598895f in binsearch ./git/build/../reftable/basics.c:175:9 #4 0x55555598e189 in block_iter_seek_key ./git/build/../reftable/block.c:543:6 #5 0x555555814aee in test_reftable_block__corrupt_restart_offset ./git/build/../t/unit-tests/u-reftable-block.c:636:20 #6 0x5555557f684e in clar_run_test ./git/build/../t/unit-tests/clar/clar.c:335:3 #7 0x5555557f2e69 in clar_run_suite ./git/build/../t/unit-tests/clar/clar.c:431:3 #8 0x5555557f2882 in clar_test_run ./git/build/../t/unit-tests/clar/clar.c:636:4 #9 0x5555557f375f in clar_test ./git/build/../t/unit-tests/clar/clar.c:687:11 #10 0x5555557fa49d in cmd_main ./git/build/../t/unit-tests/unit-test.c:62:8 #11 0x55555584c25a in main ./git/build/../common-main.c:9:11 #12 0x7ffff7a2b284 in __libc_start_call_main (/nix/store/57iz36553175g3178pvxjij8z5rcsd4n-glibc-2.42-61/lib/libc.so.6+0x2b284) (BuildId: 8ae0b698f2d4e727f569f64bb166e08ae30bd077) #13 0x7ffff7a2b337 in __libc_start_main@GLIBC_2.2.5 (/nix/store/57iz36553175g3178pvxjij8z5rcsd4n-glibc-2.42-61/lib/libc.so.6+0x2b337) (BuildId: 8ae0b698f2d4e727f569f64bb166e08ae30bd077) #14 0x555555694c24 in _start (./git/build/t/unit-tests+0x140c24) ==1472280==Register values: rax = 0x00007d8ff7de5f7f rbx = 0x00007fffffff4e00 rcx = 0x00007d8ff7de5f80 rdx = 0x00007bfff5b6af60 rdi = 0x00007bfff5b6af40 rsi = 0x00007bfff592dfa0 rbp = 0x00007fffffff4df0 rsp = 0x00007fffffff4d40 r8 = 0x00000000ff00002b r9 = 0x00007d8ff7de5f7f r10 = 0x00000f7ffeb25bf0 r11 = 0xf3f30000f1f1f1f1 r12 = 0x00007fffffff58f8 r13 = 0x0000000000000001 r14 = 0x00007ffff7ffd000 r15 = 0x0000555556055fd0 AddressSanitizer can not provide additional info. SUMMARY: AddressSanitizer: SEGV ./git/build/../reftable/record.c:30:6 in get_var_int Guard against such restart offsets and signal an error to the caller via `args.error`. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-07-03reftable/block: fix OOB read with bogus restart countPatrick Steinhardt1-0/+33
The restart count is stored in the last two bytes of a block. We use it without verification to compute the offset of the restart table. With a bogus restart count that is large enough this computation underflows, and the subsequent reads via the restart table access out-of-bounds memory: ==129439==ERROR: AddressSanitizer: SEGV on unknown address 0x7d90f6dcd0ad (pc 0x55555598ce89 bp 0x7fffffff4ed0 sp 0x7fffffff4e80 T0) ==129439==The signal is caused by a READ memory access. #0 0x55555598ce89 in reftable_get_be24 ./git/build/../reftable/basics.h:125:9 #1 0x55555598eabf in block_restart_offset ./git/build/../reftable/block.c:407:9 #2 0x55555598e5d5 in restart_needle_less ./git/build/../reftable/block.c:431:17 #3 0x5555559887e2 in binsearch ./git/build/../reftable/basics.c:165:13 #4 0x55555598dfec in block_iter_seek_key ./git/build/../reftable/block.c:529:6 #5 0x555555814517 in test_reftable_block__corrupt_restart_count ./git/build/../t/unit-tests/u-reftable-block.c:593:15 #6 0x5555557f684e in clar_run_test ./git/build/../t/unit-tests/clar/clar.c:335:3 #7 0x5555557f2e69 in clar_run_suite ./git/build/../t/unit-tests/clar/clar.c:431:3 #8 0x5555557f2882 in clar_test_run ./git/build/../t/unit-tests/clar/clar.c:636:4 #9 0x5555557f375f in clar_test ./git/build/../t/unit-tests/clar/clar.c:687:11 #10 0x5555557fa49d in cmd_main ./git/build/../t/unit-tests/unit-test.c:62:8 #11 0x55555584c12a in main ./git/build/../common-main.c:9:11 #12 0x7ffff7a2b284 in __libc_start_call_main (/nix/store/57iz36553175g3178pvxjij8z5rcsd4n-glibc-2.42-61/lib/libc.so.6+0x2b284) (BuildId: 8ae0b698f2d4e727f569f64bb166e08ae30bd077) #13 0x7ffff7a2b337 in __libc_start_main@GLIBC_2.2.5 (/nix/store/57iz36553175g3178pvxjij8z5rcsd4n-glibc-2.42-61/lib/libc.so.6+0x2b337) (BuildId: 8ae0b698f2d4e727f569f64bb166e08ae30bd077) #14 0x555555694c24 in _start (./git/build/t/unit-tests+0x140c24) ==129439==Register values: rax = 0x00007d90f6dcd0ad rbx = 0x00007fffffff4f20 rcx = 0xf2f2f2f8f2f2f2f8 rdx = 0x0000000000000000 rdi = 0x00007d90f6dcd0ad rsi = 0x0000000000007fff rbp = 0x00007fffffff4ed0 rsp = 0x00007fffffff4e80 r8 = 0x0000000000000000 r9 = 0x0000000000000000 r10 = 0x0000000000000000 r11 = 0x0000000000000017 r12 = 0x00007fffffff58e8 r13 = 0x0000000000000001 r14 = 0x00007ffff7ffd000 r15 = 0x00005555560550b0 AddressSanitizer can not provide additional info. SUMMARY: AddressSanitizer: SEGV ./git/build/../reftable/basics.h:125:9 in reftable_get_be24 Verify that the restart table actually fits into the block. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-07-03reftable/block: fix OOB read with bogus block sizePatrick Steinhardt1-0/+38
The block size is read from the block header, which is untrusted data. We use it without verification to access the restart count at the end of the block as well as to compute the restart table offset. With a bogus block size that exceeds the data we have actually read this can lead to an out-of-bounds read: ==2274138==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x7c3ff6de2e3f at pc 0x55555598c6ea bp 0x7fffffff4ee0 sp 0x7fffffff4ed8 READ of size 1 at 0x7c3ff6de2e3f thread T0 #0 0x55555598c6e9 in reftable_get_be16 /home/pks/Development/git/build/../reftable/basics.h:119:20 #1 0x55555598c252 in reftable_block_init /home/pks/Development/git/build/../reftable/block.c:343:18 #2 0x555555813c70 in test_reftable_block__corrupt_block_size /home/pks/Development/git/build/../t/unit-tests/u-reftable-block.c:531:20 #3 0x5555557f684e in clar_run_test /home/pks/Development/git/build/../t/unit-tests/clar/clar.c:335:3 #4 0x5555557f2e69 in clar_run_suite /home/pks/Development/git/build/../t/unit-tests/clar/clar.c:431:3 #5 0x5555557f2882 in clar_test_run /home/pks/Development/git/build/../t/unit-tests/clar/clar.c:636:4 #6 0x5555557f375f in clar_test /home/pks/Development/git/build/../t/unit-tests/clar/clar.c:687:11 #7 0x5555557fa49d in cmd_main /home/pks/Development/git/build/../t/unit-tests/unit-test.c:62:8 #8 0x55555584b8aa in main /home/pks/Development/git/build/../common-main.c:9:11 #9 0x7ffff7a2b284 in __libc_start_call_main (/nix/store/8kvxvr3pmsypxiypq4g8zy13glnfr7nx-glibc-2.42-67/lib/libc.so.6+0x2b284) (BuildId: 5a702452a01df1d7d50ce0663acec7be3c71fd4d) #10 0x7ffff7a2b337 in __libc_start_main@GLIBC_2.2.5 (/nix/store/8kvxvr3pmsypxiypq4g8zy13glnfr7nx-glibc-2.42-67/lib/libc.so.6+0x2b337) (BuildId: 5a702452a01df1d7d50ce0663acec7be3c71fd4d) #11 0x555555694c24 in _start (/home/pks/Development/git/build/t/unit-tests+0x140c24) 0x7c3ff6de2e3f is located 0 bytes after 47-byte region [0x7c3ff6de2e10,0x7c3ff6de2e3f) allocated by thread T0 here: #0 0x55555579e95b in malloc (/home/pks/Development/git/build/t/unit-tests+0x24a95b) #1 0x5555559871c2 in reftable_malloc /home/pks/Development/git/build/../reftable/basics.c:24:9 #2 0x5555559872e8 in reftable_calloc /home/pks/Development/git/build/../reftable/basics.c:54:6 #3 0x55555598f0d3 in reftable_buf_read_data /home/pks/Development/git/build/../reftable/blocksource.c:67:2 #4 0x55555598ea7e in block_source_read_data /home/pks/Development/git/build/../reftable/blocksource.c:41:19 #5 0x55555598c555 in read_block /home/pks/Development/git/build/../reftable/block.c:224:9 #6 0x55555598b69e in reftable_block_init /home/pks/Development/git/build/../reftable/block.c:258:9 #7 0x555555813c70 in test_reftable_block__corrupt_block_size /home/pks/Development/git/build/../t/unit-tests/u-reftable-block.c:531:20 #8 0x5555557f684e in clar_run_test /home/pks/Development/git/build/../t/unit-tests/clar/clar.c:335:3 #9 0x5555557f2e69 in clar_run_suite /home/pks/Development/git/build/../t/unit-tests/clar/clar.c:431:3 #10 0x5555557f2882 in clar_test_run /home/pks/Development/git/build/../t/unit-tests/clar/clar.c:636:4 #11 0x5555557f375f in clar_test /home/pks/Development/git/build/../t/unit-tests/clar/clar.c:687:11 #12 0x5555557fa49d in cmd_main /home/pks/Development/git/build/../t/unit-tests/unit-test.c:62:8 #13 0x55555584b8aa in main /home/pks/Development/git/build/../common-main.c:9:11 #14 0x7ffff7a2b284 in __libc_start_call_main (/nix/store/8kvxvr3pmsypxiypq4g8zy13glnfr7nx-glibc-2.42-67/lib/libc.so.6+0x2b284) (BuildId: 5a702452a01df1d7d50ce0663acec7be3c71fd4d) #15 0x7ffff7a2b337 in __libc_start_main@GLIBC_2.2.5 (/nix/store/8kvxvr3pmsypxiypq4g8zy13glnfr7nx-glibc-2.42-67/lib/libc.so.6+0x2b337) (BuildId: 5a702452a01df1d7d50ce0663acec7be3c71fd4d) #16 0x555555694c24 in _start (/home/pks/Development/git/build/t/unit-tests+0x140c24) SUMMARY: AddressSanitizer: heap-buffer-overflow /home/pks/Development/git/build/../reftable/basics.h:119:20 in reftable_get_be16 Shadow bytes around the buggy address: 0x7c3ff6de2b80: fa fa fd fd fd fd fd fa fa fa fd fd fd fd fd fa 0x7c3ff6de2c00: fa fa fd fd fd fd fd fa fa fa fd fd fd fd fd fa 0x7c3ff6de2c80: fa fa fd fd fd fd fd fd fa fa fd fd fd fd fd fa 0x7c3ff6de2d00: fa fa fd fd fd fd fd fd fa fa fd fd fd fd fd fa 0x7c3ff6de2d80: fa fa 00 00 00 00 00 00 fa fa fd fd fd fd fd fd =>0x7c3ff6de2e00: fa fa 00 00 00 00 00[07]fa fa fa fa fa fa fa fa 0x7c3ff6de2e80: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x7c3ff6de2f00: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x7c3ff6de2f80: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x7c3ff6de3000: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x7c3ff6de3080: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa Shadow byte legend (one shadow byte represents 8 application bytes): Addressable: 00 Partially addressable: 01 02 03 04 05 06 07 Heap left redzone: fa Freed heap region: fd Stack left redzone: f1 Stack mid redzone: f2 Stack right redzone: f3 Stack after return: f5 Stack use after scope: f8 Global redzone: f9 Global init order: f6 Poisoned by user: f7 Container overflow: fc Array cookie: ac Intra object redzone: bb ASan internal: fe Left alloca redzone: ca Right alloca redzone: cb Verify that the claimed block size fits into the block data before using it. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-07-03reftable/block: fix OOB write with bogus inflated log sizePatrick Steinhardt1-0/+32
The "log" reftable block stores reflog information. This information is compressed using zlib. The inflated size is stored in the block header so that callers can easily learn ahead of time how large of a buffer they have to allocate to inflate the data in a single pass. So to reconstruct the full inflated block we: - Copy over the header as-is, as it's not deflated. - Append the inflated data to the buffer. The inflated block size stored in the header also includes the length of the header itself. So to figure out the bytes that should be inflated by zlib we need to subtract the header size, which is trusted data, from the block size, which is untrusted data derived from the block header. While we do verify that we were able to inflate all data as expected, we don't verify ahead of time that the encoded block length is larger than the header length. This can lead to an underflow, which makes zlib assume that it can write more data into the target buffer than we have allocated. The result is an out-of-bounds write: ==1422297==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x7c1ff6de5231 at pc 0x55555579a628 bp 0x7fffffff4f10 sp 0x7fffffff46d0 WRITE of size 4 at 0x7c1ff6de5231 thread T0 #0 0x55555579a627 in __asan_memcpy (./build/t/unit-tests+0x246627) #1 0x55555598b093 in reftable_block_init ./build/../reftable/block.c:277:3 #2 0x555555813701 in test_reftable_block__corrupt_log_block_size ./build/../t/unit-tests/u-reftable-block.c:495:20 #3 0x5555557f684e in clar_run_test ./build/../t/unit-tests/clar/clar.c:335:3 #4 0x5555557f2e69 in clar_run_suite ./build/../t/unit-tests/clar/clar.c:431:3 #5 0x5555557f2882 in clar_test_run ./build/../t/unit-tests/clar/clar.c:636:4 #6 0x5555557f375f in clar_test ./build/../t/unit-tests/clar/clar.c:687:11 #7 0x5555557fa49d in cmd_main ./build/../t/unit-tests/unit-test.c:62:8 #8 0x55555584af4a in main ./build/../common-main.c:9:11 #9 0x7ffff7a2b284 in __libc_start_call_main (/nix/store/57iz36553175g3178pvxjij8z5rcsd4n-glibc-2.42-61/lib/libc.so.6+0x2b284) (BuildId: 8ae0b698f2d4e727f569f64bb166e08ae30bd077) #10 0x7ffff7a2b337 in __libc_start_main@GLIBC_2.2.5 (/nix/store/57iz36553175g3178pvxjij8z5rcsd4n-glibc-2.42-61/lib/libc.so.6+0x2b337) (BuildId: 8ae0b698f2d4e727f569f64bb166e08ae30bd077) #11 0x555555694c24 in _start (./build/t/unit-tests+0x140c24) 0x7c1ff6de5231 is located 0 bytes after 1-byte region [0x7c1ff6de5230,0x7c1ff6de5231) allocated by thread T0 here: #0 0x55555579db1b in realloc.part.0 asan_malloc_linux.cpp.o #1 0x5555559868d7 in reftable_realloc ./build/../reftable/basics.c:36:9 #2 0x55555598a98f in reftable_alloc_grow ./build/../reftable/basics.h:229:10 #3 0x55555598ae58 in reftable_block_init ./build/../reftable/block.c:269:3 #4 0x555555813701 in test_reftable_block__corrupt_log_block_size ./build/../t/unit-tests/u-reftable-block.c:495:20 #5 0x5555557f684e in clar_run_test ./build/../t/unit-tests/clar/clar.c:335:3 #6 0x5555557f2e69 in clar_run_suite ./build/../t/unit-tests/clar/clar.c:431:3 #7 0x5555557f2882 in clar_test_run ./build/../t/unit-tests/clar/clar.c:636:4 #8 0x5555557f375f in clar_test ./build/../t/unit-tests/clar/clar.c:687:11 #9 0x5555557fa49d in cmd_main ./build/../t/unit-tests/unit-test.c:62:8 #10 0x55555584af4a in main ./build/../common-main.c:9:11 #11 0x7ffff7a2b284 in __libc_start_call_main (/nix/store/57iz36553175g3178pvxjij8z5rcsd4n-glibc-2.42-61/lib/libc.so.6+0x2b284) (BuildId: 8ae0b698f2d4e727f569f64bb166e08ae30bd077) #12 0x7ffff7a2b337 in __libc_start_main@GLIBC_2.2.5 (/nix/store/57iz36553175g3178pvxjij8z5rcsd4n-glibc-2.42-61/lib/libc.so.6+0x2b337) (BuildId: 8ae0b698f2d4e727f569f64bb166e08ae30bd077) #13 0x555555694c24 in _start (./build/t/unit-tests+0x140c24) SUMMARY: AddressSanitizer: heap-buffer-overflow (./build/t/unit-tests+0x246627) in __asan_memcpy Shadow bytes around the buggy address: 0x7c1ff6de4f80: fa fa fd fd fa fa fd fd fa fa fd fd fa fa fd fd 0x7c1ff6de5000: fa fa fd fd fa fa fd fd fa fa fd fd fa fa fd fd 0x7c1ff6de5080: fa fa fd fd fa fa fd fd fa fa fd fd fa fa fd fd 0x7c1ff6de5100: fa fa fd fd fa fa fd fd fa fa fd fd fa fa fd fd 0x7c1ff6de5180: fa fa fd fd fa fa fd fd fa fa fd fa fa fa fd fd =>0x7c1ff6de5200: fa fa 04 fa fa fa[01]fa fa fa fa fa fa fa fa fa 0x7c1ff6de5280: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x7c1ff6de5300: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x7c1ff6de5380: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x7c1ff6de5400: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x7c1ff6de5480: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa Shadow byte legend (one shadow byte represents 8 application bytes): Addressable: 00 Partially addressable: 01 02 03 04 05 06 07 Heap left redzone: fa Freed heap region: fd Stack left redzone: f1 Stack mid redzone: f2 Stack right redzone: f3 Stack after return: f5 Stack use after scope: f8 Global redzone: f9 Global init order: f6 Poisoned by user: f7 Container overflow: fc Array cookie: ac Intra object redzone: bb ASan internal: fe Left alloca redzone: ca Right alloca redzone: cb Fix the bug by adding a sanity check and add a unit test. Reported-by: oxsignal <awo@kakao.com> Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-07-03t/unit-tests: introduce test helper to write reftable blocksPatrick Steinhardt1-19/+28
Introduce a new test helper that allows us to write reftable blocks. This helper will be used by subsequent commits. Suggested-by: Christian Couder <christian.couder@gmail.com> Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-07-03reftable/record: don't abort when decoding invalid ref value typePatrick Steinhardt1-0/+24
When decoding a ref record we read its value type from the block. In case the type itself is invalid we call `abort()`. This is rather heavy-handed though: the data we're reading is untrusted, so we should treat the issue as a normal and not as a programming error. Fix this by handling the error gracefully. Note that this also requires us to set the value type later, as otherwise we might store an invalid type in the record. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-07-03reftable/basics: fix OOB read on binary search of empty rangePatrick Steinhardt1-0/+11
`binsearch()` performs a binary search over a range of `sz` elements by repeatedly calling the comparison function with indices into that range. When the range is empty though, there is no valid index to call the comparison function with. We still end up executing the comparison function though with an index of 0, which of course will cause an out-of-bounds read. Return early when the range is empty. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-06-26reftable: split up write optionsPatrick Steinhardt6-124/+130
When initializing the reftable stack the caller may optionally pass some write options. These write options mix up two different concerns though: - Of course, they allow the caller to configure how new reftables are being written. - But they also allow the caller to configure the stack itself, like its hash ID and the `on_reload` callback. This is somewhat awkward, as it doesn't easily give the caller the flexibility to for example write multiple reftables with different options. Furthermore, this requires us to eagerly parse relevant configuration when initializing the reftable backend. Refactor the code by splitting out those options that configure the stack itself. Creating a new stack will thus only require this limited set of options, whereas the caller is expected to pass write options to all functions that end up writing tables. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-06-21Merge branch 'js/objects-larger-than-4gb-on-windows-more'Junio C Hamano1-1/+1
* js/objects-larger-than-4gb-on-windows-more: odb: use size_t for object_info.sizep and the size APIs packfile,delta: drop the `cast_size_t_to_ulong()` wrappers pack-objects: use size_t for in-core object sizes packfile: widen unpack_entry()'s size out-parameter to size_t pack-objects(check_pack_inflate()): use size_t instead of unsigned long patch-delta: use size_t for sizes compat/msvc: use _chsize_s for ftruncate
2026-06-15odb: use size_t for object_info.sizep and the size APIsJohannes Schindelin1-1/+1
When `js/objects-larger-than-4gb-on-windows` widened the streaming, index-pack and unpack-objects code paths, in the interest of keeping the patches somewhat reasonably-sized, it left the public ODB API still typed in `unsigned long`. In particular `struct object_info::sizep` and the four wrappers built on top of it (`odb_read_object`, `odb_read_object_peeled`, `odb_read_object_info`, `odb_pretend_object`) still return the unpacked size through `unsigned long *`, so on Windows `cat-file -s` and the `git add` / `git status` paths for a >4 GiB blob silently cap at 4 GiB. Widen the field and the four wrappers. The previous commits already widened the `unpack_entry()` cascade and pack-objects' in-core size accessors, so most of the cascade arrives here with no further work: the temporary shims in `packed_object_info_with_index_pos()` and in `unpack_entry()`'s delta-base recovery path go away, the two `SET_SIZE(entry, cast_size_t_to_ulong(canonical_size))` calls in `check_object()` and the matching one in `drop_reused_delta()` collapse to plain `SET_SIZE`, and `oe_get_size_slow()`'s tail `cast_size_t_to_ulong()` is gone too. What remains narrow are the boundaries this series does not intend to touch: the diff, blame, textconv and fast-import machinery. Even so, this patch is unfortunately quite large. Assisted-by: Opus 4.7 Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-06-15Merge branch 'ak/typofixes'Junio C Hamano1-1/+1
Typofixes. * ak/typofixes: doc: fix typos via codespell
2026-06-09prio-queue: fold lazy_queue into prio_queue for automatic get+put fusionKristofer Karlsson1-3/+3
Defer the actual removal in prio_queue_get() until the next operation. If that next operation is a prio_queue_put(), the removal and insertion are fused into a single replace — writing the new element at the root and sifting it down — which avoids a full remove-rebalance-insert cycle. This matches the dominant usage pattern in git's commit traversal: get a commit, then put its parents. The first parent insertion after each get is now a replace operation automatically. This generalizes the lazy_queue pattern from builtin/describe.c (introduced in 08bb69d70f) into prio_queue itself. Three callers independently implemented the same get+put fusion: - builtin/describe.c had a full lazy_queue wrapper - commit.c:pop_most_recent_commit() used peek+replace - builtin/show-branch.c:join_revs() used peek+replace All three now collapse to plain _get() and _put(), with the data structure handling the fusion internally. This simplifies callers and means every prio_queue user gets the optimization for free without needing to implement it manually. Remove prio_queue_replace() since no external callers remain. Benchmarked on a 1.8M-commit monorepo (30 interleaved runs, paired t-test, Xeon @ 2.20GHz): Code paths that previously did eager get+put (new optimization): Command base patched change p merge-base --all A A~1000 3828ms 3725ms -2.69% 0.0001 rev-list --count A~1000..A 3055ms 2986ms -2.27% 0.0601 log --oneline A~1000..A 3408ms 3350ms -1.71% 0.0482 Code paths that already had manual get+put fusion (expect neutral — the optimization moves into prio_queue but the number of heap operations stays the same): Command base patched change p show-branch A A~1000 9156ms 9127ms -0.32% 0.3470 describe (4751 revs, 81K repo) 1983ms 1963ms -1.02% <0.001 No regressions in any scenario. Suggested-by: René Scharfe <l.s.r@web.de> Signed-off-by: Kristofer Karlsson <krka@spotify.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-06-08doc: fix typos via codespellAndrew Kreimer1-1/+1
There are some typos in the documentation, comments, etc. Fix them via codespell, and then adjust the "dump" files used by the subversion tests to match the updated contents. Signed-off-by: Andrew Kreimer <algonell@gmail.com> [dscho noticed and fixed the problems in svn test] Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> [jc did final assembling of the three patches] Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-27Merge branch 'ps/odb-in-memory'Junio C Hamano2-3/+336
Add a new odb "in-memory" source that is meant to only hold tentative objects (like the virtual blob object that represents the working tree file used by "git blame"). * ps/odb-in-memory: t/unit-tests: add tests for the in-memory object source odb: generic in-memory source odb/source-inmemory: stub out remaining functions odb/source-inmemory: implement `freshen_object()` callback odb/source-inmemory: implement `count_objects()` callback odb/source-inmemory: implement `find_abbrev_len()` callback odb/source-inmemory: implement `for_each_object()` callback odb/source-inmemory: convert to use oidtree oidtree: add ability to store data cbtree: allow using arbitrary wrapper structures for nodes odb/source-inmemory: implement `write_object_stream()` callback odb/source-inmemory: implement `write_object()` callback odb/source-inmemory: implement `read_object_stream()` callback odb/source-inmemory: implement `read_object_info()` callback odb: fix unnecessary call to `find_cached_object()` odb/source-inmemory: implement `free()` callback odb: introduce "in-memory" source
2026-05-15t/unit-tests: add tests for the in-memory object sourcePatrick Steinhardt1-0/+313
While the in-memory object source is a full-fledged source, our code base only exercises parts of its functionality because we only use it in git-blame(1). Implement unit tests to verify that the yet-unused functionality of the backend works as expected. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-15oidtree: add ability to store dataPatrick Steinhardt1-3/+23
The oidtree data structure is currently only used to store object IDs, without any associated data. So consequently, it can only really be used to track which object IDs exist, and we can use the tree structure to efficiently operate on OID prefixes. But there are valid use cases where we want to both: - Store object IDs in a sorted order. - Associated arbitrary data with them. Refactor the oidtree interface so that it allows us to store arbitrary payloads within the respective nodes. This will be used in the next commit. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-06urlmatch: define url_parse functionMatheus Afonso Martins Moreira1-0/+45
Define url_parse, a general parsing function that supports all Git URLs including scp style URLs such as hostname:~user/repo. It is adapted from the algorithm in connect.c's parse_connect_url and reuses the shared enum url_scheme and url_get_scheme function that previous commits made available in url.h. The new parser and the connect path agree on scheme classification. url_parse has the same interface as url_normalize and uses the same data structures. Both functions accept the same URL forms with one deliberate exception. Bare local paths such as "/abs/path", "./rel" or "repo" are accepted by parse_connect_url as URL_SCHEME_LOCAL, but rejected by url_parse because url_normalize requires a URL with a scheme://host form. A consumer that wants to handle both URLs and local paths needs to dispatch on url_is_local_not_ssh before calling url_parse, just as the connect path does internally. The duplication with parse_connect_url is intentional. The two functions have different contracts: - parse_connect_url Calls die() on an unknown scheme and returns NUL-terminated host/path strings for the connect path - url_parse Returns NULL on failure while populating out_info->err, and exposes components as offset/length pairs into the normalized URL buffer, matching url_normalize. Reconciling both is possible, but not in the scope of the current patch set. Signed-off-by: Matheus Afonso Martins Moreira <matheus@matheusmoreira.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-04-06Merge branch 'ps/odb-generic-object-name-handling'Junio C Hamano1-9/+9
Object name handling (disambiguation and abbreviation) has been refactored to be backend-generic, moving logic into the respective object database backends. * ps/odb-generic-object-name-handling: odb: introduce generic `odb_find_abbrev_len()` object-file: move logic to compute packed abbreviation length object-name: move logic to compute loose abbreviation length object-name: simplify computing common prefixes object-name: abbreviate loose object names without `disambiguate_state` object-name: merge `update_candidates()` and `match_prefix()` object-name: backend-generic `get_short_oid()` object-name: backend-generic `repo_collect_ambiguous()` object-name: extract function to parse object ID prefixes object-name: move logic to iterate through packed prefixed objects object-name: move logic to iterate through loose prefixed objects odb: introduce `struct odb_for_each_object_options` oidtree: extend iteration to allow for arbitrary return codes oidtree: modernize the code a bit object-file: fix sparse 'plain integer as NULL pointer' error
2026-03-24Merge branch 'ps/clar-wo-path-max'Junio C Hamano2-15/+68
Clar (unit testing framework) update from the upstream. * ps/clar-wo-path-max: clar: update to fix compilation on platforms without PATH_MAX
2026-03-20oidtree: extend iteration to allow for arbitrary return codesPatrick Steinhardt1-2/+2
The interface `cb_each()` iterates through a crit-bit tree and calls a specific callback function for each of the contained items. The callback function is expected to return either: - `CB_CONTINUE` in case iteration shall continue. - `CB_BREAK` to abort iteration. This is needlessly restrictive though, as callers may want to return arbitrary values and have them be bubbled up to the `cb_each()` call site. In fact, this is a rather common pattern we have: whenever such a callback function returns a non-zero error code, we abort iteration and bubble up the code as-is. Refactor both the crit-bit tree and oidtree subsystems to behave accordingly. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>