gitoxide icon indicating copy to clipboard operation
gitoxide copied to clipboard

gix cat: Encountered unsupported command code: 0

Open ElvishJerricco opened this issue 1 month ago β€’ 6 comments

Current behavior 😯

I encountered this after updating jj, but I bisected it to jj's update from gix 0.73.0 to 0.74.1, and bisected further to https://github.com/GitoxideLabs/gitoxide/pull/2155

In a large repo I have, reading a certain object always fails.

$ gix cat 70b81af0814b0620db4af251b3f0e9530457104b
Error: An error occurred while obtaining an object from the packed object store

Caused by:
    Encountered unsupported command code: 0

Stack backtrace:
   0: anyhow::error::<impl core::convert::From<E> for anyhow::Error>::from
   1: <core::result::Result<T,F> as core::ops::try_trait::FromResidual<core::result::Result<core::convert::Infallible,E>>>::from_residual
   2: gitoxide_core::repository::cat::display_object
   3: gitoxide_core::repository::cat::function::cat
   4: gitoxide::plumbing::main::main::{{closure}}
   5: gitoxide::shared::pretty::prepare_and_run
   6: gitoxide::plumbing::main::main
   7: gix::main
   8: core::ops::function::FnOnce::call_once
   9: std::sys::backtrace::__rust_begin_short_backtrace
  10: std::rt::lang_start::{{closure}}
  11: std::rt::lang_start_internal
  12: std::rt::lang_start
  13: main
  14: __libc_start_call_main
  15: __libc_start_main_alias_1
  16: _start

Expected behavior πŸ€”

The object should be readable, as it is with gix 0.73.0.

Git behavior

git cat-file -p 70b81af0814b0620db4af251b3f0e9530457104b in the same repo outputs the contents of a file in the repo.

Steps to reproduce πŸ•Ή

Unfortunately, I can only narrow this down to a 5.1GiB pack file, and I don't know how this problem was created. It seems like the pack file is valid, given that both git and earlier versions of gix read it fine. Copying the .pack file to a new repo and regenerating the .idx and .rev with git-index-pack preserves the problem, so the problem is in the .pack. Is there a way I can narrow down the pack file to just the problematic object?

ElvishJerricco avatar Nov 23 '25 06:11 ElvishJerricco

Thanks for reporting and the initial investigation, and sorry for the hassle.

I understand this issue isn't present before #2155 was merged, and that it's unrelated to the pack index which was also regenerated. Thus, let me CC @folkertdev.

Besides that, I am pretty sure this is at least related to #2044, which was left open as well, with this comment being quite indicative of what seems to happen.

So it really seems to be something about zlib-rs and/or how these pack entries have been created. It could also be related to how gitoxide handles its buffers, but from all I could tell it just creates buffers of the required size and tells it to inflate everything into it in one go.

Byron avatar Nov 23 '25 07:11 Byron

Hmm, could this be some sort of overflow bug, given the large inputs?

Otherwise I'd need some sort of specific sequence of inputs to zlib to say anything useful.

folkertdev avatar Nov 25 '25 10:11 folkertdev

If you could help me figure out how to extract the problematic part of the pack file I'd be happy to share that.

ElvishJerricco avatar Nov 25 '25 10:11 ElvishJerricco

Assuming

  • the problem here would be in decompression
  • gixoxide only makes one Decompress and reuses it

I'd just need the sequence of inputs to decompress here

https://github.com/GitoxideLabs/gitoxide/blob/df393f37bb03955eff3ad52c27440393737fb334/gix-features/src/zlib/mod.rs#L47

Something like this patch

https://github.com/GitoxideLabs/gitoxide/compare/main...folkertdev:gitoxide:store-zlib-decompression

Hopefully that creates a smaller file, and we can replay that using zlib-rs and zlib-ng and see if there are any differences. If the file is still too large, maybe only keep data after the most recent reset and we can see if that is sufficient.

folkertdev avatar Nov 25 '25 11:11 folkertdev

Reading over the other issue again, you could also try and see if disabling the max-performance feature helps at all.

folkertdev avatar Nov 25 '25 11:11 folkertdev

Thanks so much for chiming in @folkertdev!

As a note, I think disabling max-performance shouldn't make a difference (anymore) as by now we always use zlib-rs, and we always use a hardened SHA1 implementation. There is no alternative options anymore. In any case, it's probably easy enough to try it anyway.

Just one more question: in #2044 we already did an investigation and it turned out to be an x86 specific issue that doesn't happen on AARM64 for instance. @ElvishJerricco do you happen to be on x86? Because if it's AARM or ARM, we'd have a first encounter.

Byron avatar Nov 26 '25 05:11 Byron

Just another data point. We received this bug report: https://github.com/jj-vcs/jj/issues/8350

The cloned "firefox" repository has large pack files:

% ll .git/objects/pack
total 4.7G
-r--r--r-- 1 yuya yuya 409M 2025-12-20 13:03 pack-5a5df80c4c69363dd6caa84400c16e35457f0b45.idx
-r--r--r-- 1 yuya yuya 4.3G 2025-12-20 13:03 pack-5a5df80c4c69363dd6caa84400c16e35457f0b45.pack
-r--r--r-- 1 yuya yuya  48M 2025-12-20 13:03 pack-5a5df80c4c69363dd6caa84400c16e35457f0b45.rev

and the current gix cannot read some objects (whereas gix 0.73.0 can):

% gix cat e3e80c740c34c84c75710f36e7a0dc97c3bdede2 | xxd
00000000: 7400 0000 0000 0000 0000 0000 0000 0000  t...............
00000010: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000020: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000030: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000040: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000050: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000060: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000070: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000080: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000090: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000000a0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000000b0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000000c0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000000d0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000000e0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000000f0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000100: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000110: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000120: 0000 0000 00                             .....

In jj git init's workload, it seems that some misplaced or uninitialized data was loaded:

[lib/src/repo.rs:1643:37] (commit.id(), commit.store_commit()) = (
    CommitId(
        "1ca754d72fdadc1cea6358b852440296d594f9c1",
    ),
    Commit {
        parents: [
            CommitId(
                "e3e80c740c34c84c75710f36e7a0dc97c3bdede2",
            ),
        ],
        predecessors: [],
        root_tree: Resolved(
            TreeId(
                "cbcda8d64976771cbea0a1afd86d2fca2ed4d905",
            ),
        ),
        conflict_labels: Resolved(
            "",
        ),
        change_id: ChangeId(
            "839f29ab6940224a1d1ac657383b5bf4",
        ),
        description: "Bug 596905 - Unconventional date format in the new about window (about box). r=gavin a=blocking2.0",
        author: Signature {
            name: "Margaret Leibovic",
            email: "[email protected]",
            timestamp: Timestamp {
                timestamp: MillisSinceEpoch(
                    1285090298000,
                ),
                tz_offset: -420,
            },
        },
        committer: Signature {
            name: "Margaret Leibovic",
            email: "[email protected]",
            timestamp: Timestamp {
                timestamp: MillisSinceEpoch(
                    1285090298000,
                ),
                tz_offset: -420,
            },
        },
        secure_sig: None,
    },
)
[lib/src/repo.rs:1643:37] (commit.id(), commit.store_commit()) = (
    CommitId(
        "e3e80c740c34c84c75710f36e7a0dc97c3bdede2",
    ),
    Commit {
        parents: [
            CommitId(
                "e3e80c740c34c84c75710f36e7a0dc97c3bdede2",
            ),
        ],
        predecessors: [],
        root_tree: Resolved(
            TreeId(
                "cbcda8d64976771cbea0a1afd86d2fca2ed4d905",
            ),
        ),
        conflict_labels: Resolved(
            "",
        ),
        change_id: ChangeId(
            "47b7bdc3e93b05e76cf08eae32132c30",
        ),
        description: "Bug 596905 - Unconventional date format in the new ",
        author: Signature {
            name: "Margaret Leibovic",
            email: "[email protected]",
            timestamp: Timestamp {
                timestamp: MillisSinceEpoch(
                    1285090298000,
                ),
                tz_offset: -420,
            },
        },
        committer: Signature {
            name: "Margaret Leibovic",
            email: "[email protected]",
            timestamp: Timestamp {
                timestamp: MillisSinceEpoch(
                    1285090298000,
                ),
                tz_offset: -420,
            },
        },
        secure_sig: None,
    },
)

The commit object 1ca754d72fdadc1cea6358b852440296d594f9c1 is good, but it seems e3e80c740c34c84c75710f36e7a0dc97c3bdede2 copy the previous data?

yuja avatar Dec 20 '25 05:12 yuja

The problem is that decompress() truncates input length to c_uint. If the input slice slightly exceeds u32 range, there would be very few data to consume. Therefore, decompress() ends with Status::Ok (meaning more input may be needed.)

https://github.com/trifectatechfoundation/zlib-rs/blob/v0.5.4/zlib-rs/src/stable.rs#L119

This hack fixed the problem at my end.

diff --git a/gix-features/src/zlib/mod.rs b/gix-features/src/zlib/mod.rs
index 448b8f1aaa..c32665722c 100644
--- a/gix-features/src/zlib/mod.rs
+++ b/gix-features/src/zlib/mod.rs
@@ -44,6 +44,7 @@
             FlushDecompress::Finish => zlib_rs::InflateFlush::Finish,
         };
 
+        let input = input.get(..std::ffi::c_uint::MAX as usize).unwrap_or(input);
         let status = self.0.decompress(input, output, inflate_flush)?;
         match status {
             zlib_rs::Status::Ok => Ok(Status::Ok),

yuja avatar Dec 20 '25 09:12 yuja

@yuja May I ask another datapoint? Since you seem to be able to reproduce the issue, which once again I am not on macOS/AARM64, it would be really valuable if you could try it again with the latest main of gitoxide. It uses zlib-rs directly, without C interop.

Maybe that changes things. If it doesn't, it's clear what needs to be done, and we have to bring back zlib-ng which presumably was the backend JJ used before. If not, miniz_oxide is also an option, and even a more natural (albeit slower) one. I'd love to sort this out this year, and simply bringing back the backend option seems like the way to go until this is properly fixed.

My 'firefox' results for posterity

❯ gix clone https://github.com/mozilla-firefox/firefox.git
 10:18:20 indexing done 12.5M objects in 134.22s (92.8K objects/s)
 10:18:20 decompressing done 10.1GB in 134.22s (75.4MB/s)
 10:18:53     Resolving done 12.5M objects in 32.48s (383.3K objects/s)
 10:18:53      Decoding done 271.9GB in 32.48s (8.4GB/s)
 10:18:54 writing index file done 427.9MB in 0.90s (473.5MB/s)
 10:18:54  create index file done 12.5M objects in 168.50s (73.9K objects/s)
 10:18:54          read pack done 4.5GB in 169.55s (26.7MB/s)
 10:19:23           checkout done 398.4K files in 27.95s (14.3K files/s)
 10:19:23            writing done 3.2GB in 27.95s (116.3MB/s)
[..]
> cd firefox
firefox (ξ‚  main) via πŸ…Ά v9.2.1 via β˜• via  via 🐍
❯ gix cat e3e80c740c34c84c75710f36e7a0dc97c3bdede2
tree 76b3c0e4cdd474c9e2eec913f183a0966ec572dc
parent a88a29bf8eb19169aed6ddb020407bcdbd326fb2
author Oleg Romashin <[email protected]> 1285089018 -0700
committer Oleg Romashin <[email protected]> 1285089018 -0700

Bug 598250 - De-Initialize basic osso_context for Maemo5 Qt port. r=dougt a=npodb%
firefox (ξ‚  main) via πŸ…Ά v9.2.1 via β˜• via  via 🐍
❯ gix cat e3e80c740c34c84c75710f36e7a0dc97c3bdede2 | hexyl
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚00000000β”‚ 74 72 65 65 20 37 36 62 β”Š 33 63 30 65 34 63 64 64 β”‚tree 76bβ”Š3c0e4cddβ”‚
β”‚00000010β”‚ 34 37 34 63 39 65 32 65 β”Š 65 63 39 31 33 66 31 38 β”‚474c9e2eβ”Šec913f18β”‚
β”‚00000020β”‚ 33 61 30 39 36 36 65 63 β”Š 35 37 32 64 63 0a 70 61 β”‚3a0966ecβ”Š572dc_paβ”‚
β”‚00000030β”‚ 72 65 6e 74 20 61 38 38 β”Š 61 32 39 62 66 38 65 62 β”‚rent a88β”Ša29bf8ebβ”‚
β”‚00000040β”‚ 31 39 31 36 39 61 65 64 β”Š 36 64 64 62 30 32 30 34 β”‚19169aedβ”Š6ddb0204β”‚
β”‚00000050β”‚ 30 37 62 63 64 62 64 33 β”Š 32 36 66 62 32 0a 61 75 β”‚07bcdbd3β”Š26fb2_auβ”‚
β”‚00000060β”‚ 74 68 6f 72 20 4f 6c 65 β”Š 67 20 52 6f 6d 61 73 68 β”‚thor Oleβ”Šg Romashβ”‚
β”‚00000070β”‚ 69 6e 20 3c 72 6f 6d 61 β”Š 78 61 40 67 6d 61 69 6c β”‚in <romaβ”Šxa@gmailβ”‚
β”‚00000080β”‚ 2e 63 6f 6d 3e 20 31 32 β”Š 38 35 30 38 39 30 31 38 β”‚.com> 12β”Š85089018β”‚
β”‚00000090β”‚ 20 2d 30 37 30 30 0a 63 β”Š 6f 6d 6d 69 74 74 65 72 β”‚ -0700_cβ”Šommitterβ”‚
β”‚000000a0β”‚ 20 4f 6c 65 67 20 52 6f β”Š 6d 61 73 68 69 6e 20 3c β”‚ Oleg Roβ”Šmashin <β”‚
β”‚000000b0β”‚ 72 6f 6d 61 78 61 40 67 β”Š 6d 61 69 6c 2e 63 6f 6d β”‚romaxa@gβ”Šmail.comβ”‚
β”‚000000c0β”‚ 3e 20 31 32 38 35 30 38 β”Š 39 30 31 38 20 2d 30 37 β”‚> 128508β”Š9018 -07β”‚
β”‚000000d0β”‚ 30 30 0a 0a 42 75 67 20 β”Š 35 39 38 32 35 30 20 2d β”‚00__Bug β”Š598250 -β”‚
β”‚000000e0β”‚ 20 44 65 2d 49 6e 69 74 β”Š 69 61 6c 69 7a 65 20 62 β”‚ De-Initβ”Šialize bβ”‚
β”‚000000f0β”‚ 61 73 69 63 20 6f 73 73 β”Š 6f 5f 63 6f 6e 74 65 78 β”‚asic ossβ”Šo_contexβ”‚
β”‚00000100β”‚ 74 20 66 6f 72 20 4d 61 β”Š 65 6d 6f 35 20 51 74 20 β”‚t for Maβ”Šemo5 Qt β”‚
β”‚00000110β”‚ 70 6f 72 74 2e 20 72 3d β”Š 64 6f 75 67 74 20 61 3d β”‚port. r=β”Šdougt a=β”‚
β”‚00000120β”‚ 6e 70 6f 64 62          β”Š                         β”‚npodb   β”Š        β”‚
└────────┴─────────────────────────┴─────────────────────────┴────────┴────────

Byron avatar Dec 20 '25 09:12 Byron

That was racy :D, @yuja. And it looks like you already used the latest version.

@folkertdev The problem seems to be that gitoxide can totally pass in inputs that are larger than u32, they are uncapped as it's unclear how many bytes of the stream will be consumed. It looks like this is something that could classify as bug in zlib-rs, what do you think?

Now I am optimistic that there won't be a need to bring back zlib-ng πŸŽ‰, exciting!

Byron avatar Dec 20 '25 09:12 Byron

That does seem very plausible, good investigative work! It might be a couple of days before I'll be able to look into this further.

folkertdev avatar Dec 20 '25 10:12 folkertdev

I think https://github.com/trifectatechfoundation/zlib-rs/pull/449 should fix it, could someone check that?

folkertdev avatar Dec 20 '25 12:12 folkertdev

I think trifectatechfoundation/zlib-rs#449 should fix it, could someone check that?

It fixed the problem of jj, thanks.

yuja avatar Dec 20 '25 13:12 yuja