这是indexloc提供的服务,不要输入任何密码
Skip to content

Conversation

@KungFuJesus
Copy link
Contributor

@KungFuJesus KungFuJesus commented Nov 17, 2023

Evidently we weren't hitting this code path very often in our tests?
If we reach here, there are some circumstances where we never ran any
of the bytes through the checksum. We can easily check for this with a
comparison if the initial checksum bytes.

This should fix issue #1600, and possibly #1565 as well.

@codecov
Copy link

codecov bot commented Nov 17, 2023

Codecov Report

All modified and coverable lines are covered by tests ✅

Comparison is base (0b3bb1d) 69.90% compared to head (b0607c3) 83.27%.

Additional details and impacted files
@@             Coverage Diff              @@
##           develop    #1602       +/-   ##
============================================
+ Coverage    69.90%   83.27%   +13.37%     
============================================
  Files          116      133       +17     
  Lines         9479    10880     +1401     
  Branches      2341     2809      +468     
============================================
+ Hits          6626     9060     +2434     
+ Misses        1777     1117      -660     
+ Partials      1076      703      -373     
Flag Coverage Δ
macos_clang 42.97% <ø> (?)
macos_gcc 74.66% <80.00%> (?)
ubuntu_clang 82.27% <100.00%> (?)
ubuntu_clang_debug 81.90% <100.00%> (?)
ubuntu_clang_inflate_allow_invalid_dist 81.93% <100.00%> (?)
ubuntu_clang_inflate_strict 82.26% <100.00%> (?)
ubuntu_clang_mmap 82.60% <100.00%> (?)
ubuntu_clang_pigz 13.92% <0.00%> (-0.01%) ⬇️
ubuntu_clang_pigz_no_optim 11.51% <0.00%> (-0.01%) ⬇️
ubuntu_clang_pigz_no_threads 13.74% <0.00%> (-0.01%) ⬇️
ubuntu_clang_reduced_mem 82.67% <100.00%> (?)
ubuntu_clang_toolchain_riscv ∅ <ø> (?)
ubuntu_gcc 75.23% <80.00%> (?)
ubuntu_gcc_aarch64 77.41% <80.00%> (?)
ubuntu_gcc_aarch64_compat_no_opt 75.65% <80.00%> (?)
ubuntu_gcc_aarch64_no_acle 76.17% <80.00%> (?)
ubuntu_gcc_aarch64_no_neon 76.17% <80.00%> (?)
ubuntu_gcc_armhf 77.18% <80.00%> (?)
ubuntu_gcc_armhf_compat_no_opt 75.62% <80.00%> (?)
ubuntu_gcc_armhf_no_acle 77.13% <80.00%> (?)
ubuntu_gcc_armhf_no_neon 77.25% <80.00%> (?)
ubuntu_gcc_armsf 74.62% <80.00%> (?)
ubuntu_gcc_armsf_compat_no_opt 74.13% <80.00%> (?)
ubuntu_gcc_benchmark 73.43% <80.00%> (?)
ubuntu_gcc_compat_no_opt 76.86% <80.00%> (?)
ubuntu_gcc_compat_sprefix 73.75% <80.00%> (?)
ubuntu_gcc_m32 73.41% <80.00%> (?)
ubuntu_gcc_mingw_i686 73.68% <80.00%> (?)
ubuntu_gcc_mingw_x86_64 73.69% <80.00%> (?)
ubuntu_gcc_mips 74.99% <80.00%> (?)
ubuntu_gcc_mips64 75.00% <80.00%> (?)
ubuntu_gcc_no_avx2 74.34% <80.00%> (?)
ubuntu_gcc_no_ctz 74.67% <80.00%> (?)
ubuntu_gcc_no_ctzll 74.66% <80.00%> (?)
ubuntu_gcc_no_pclmulqdq 74.22% <80.00%> (?)
ubuntu_gcc_no_sse2 74.49% <80.00%> (?)
ubuntu_gcc_no_sse42 74.17% <80.00%> (?)
ubuntu_gcc_o1 74.12% <80.00%> (?)
ubuntu_gcc_osb ∅ <ø> (?)
ubuntu_gcc_pigz 38.10% <0.00%> (-0.06%) ⬇️
ubuntu_gcc_pigz_aarch64 39.15% <0.00%> (-0.04%) ⬇️
ubuntu_gcc_ppc 73.94% <80.00%> (?)
ubuntu_gcc_ppc64 74.38% <80.00%> (?)
ubuntu_gcc_ppc64_power9 74.55% <80.00%> (?)
ubuntu_gcc_ppc64le 74.45% <80.00%> (?)
ubuntu_gcc_ppc64le_novsx 74.77% <80.00%> (?)
ubuntu_gcc_ppc64le_power9 74.33% <80.00%> (?)
ubuntu_gcc_ppc_no_power8 74.65% <80.00%> (?)
ubuntu_gcc_s390x 74.82% <80.00%> (?)
ubuntu_gcc_s390x_dfltcc 71.92% <40.00%> (-0.02%) ⬇️
ubuntu_gcc_s390x_dfltcc_compat 73.99% <40.00%> (-0.02%) ⬇️
ubuntu_gcc_s390x_no_crc32 74.61% <80.00%> (?)
ubuntu_gcc_sparc64 74.81% <80.00%> (?)
ubuntu_gcc_sprefix 73.42% <80.00%> (?)
win64_gcc 74.04% <80.00%> (?)
win64_gcc_compat_no_opt 74.73% <80.00%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@KungFuJesus
Copy link
Contributor Author

OK, I think this will work. It's maybe a bit hackier than I'd like but I'm pretty sure correctness isn't an issue. This should catch the corner case we were seeing. The main difference is I think there were unconditional checksums being applied in earlier versions in a lot of places. I don't see a clean delineation where we clearly aren't applying the checksum but should be, though.

@KungFuJesus
Copy link
Contributor Author

Hmm, I have half a mind to write the buffer being fed through that library's test suite and put it into ours.

@KungFuJesus
Copy link
Contributor Author

KungFuJesus commented Nov 18, 2023

Not sure what's up with the clang reduced memory failure.

Ahh, it does look like it reduces the window size to 8k, so perhaps this is making us hit a different corner case. It also has dawned on me that the checksum will always be the initial CRC32 value until the final fold so that bit might need to be reworked.

@KungFuJesus
Copy link
Contributor Author

I reproduced the reduced mem failure, I'll look into it tonight.

@KungFuJesus KungFuJesus changed the title Fix an issue with regard to finishing out the window Draft: Fix an issue with regard to finishing out the window Nov 18, 2023
@KungFuJesus KungFuJesus changed the title Draft: Fix an issue with regard to finishing out the window Fix an issue with regard to finishing out the window Nov 18, 2023
@KungFuJesus
Copy link
Contributor Author

Ok I think I got it. Still a little hackier than I'd like (I'd be much more thrilled about catching the set of conditions that paints us in this corner) but barring better suggestions or proof that this is not catching all of the corner cases, I say it's ready to merge.

@KungFuJesus KungFuJesus force-pushed the fix_1600 branch 3 times, most recently from 400285a to ee8207c Compare November 19, 2023 02:32
Copy link
Member

@Dead2 Dead2 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@Dead2
Copy link
Member

Dead2 commented Nov 21, 2023

Wish we did have a CI test for this, so we could make sure that this is not a problem for all the other alternative code-paths as well. I don't think any other arch based their code on crc32_fold_pclmulqdq etc and inherited the problem, did they?

@KungFuJesus
Copy link
Contributor Author

The problem isn't specific to crcs, it's specific to inflate. There's a potential encoding through the compression state machine that arrives at the final check instruction without the current window getting run through the checksum routines.

This seems to catch those corner cases, as when it happens the checksum is the initial unaltered checksum, be that Adler or crc.

As far as something that catches this, I think integrating libgit2's tests into our CI wouldn't be a bad approach.

Copy link
Collaborator

@mtl1979 mtl1979 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM... We can add CI test in separate pull request.

@nmoinvaz
Copy link
Member

nmoinvaz commented Nov 22, 2023

I am testing using minideflate and comparing to libgit2. The put variable sent to inf_chksum is different.

image

minideflate using a next position in write buffer for each call to inflate(), where as libgit2 starts from scratch from the buffer, so put is evaluated correctly.

Here is my test data:
000000A2CD78EBA0-inflate.gz

minideflate -d -k -s 4 -r 25 -w 15 -t 8192 000000A2CD78EBA0-inflate.gz

And change minideflate:

            err = PREFIX(inflate)(&d_stream, flush);
            if (err == Z_BUF_ERROR && read_buf_size != BUFSIZE) {
                err = Z_OK;
            }

So essentially, it only works because in libgit2 they are passing the same buffer pointer of the previous inflate() to the the next call to inflate(). I still don't think we should commit this until we add a unit test.

@nmoinvaz
Copy link
Member

nmoinvaz commented Nov 22, 2023

This also seems to work although it can probably be better some how:

  inf_leave:
    RESTORE();
    if (INFLATE_NEED_UPDATEWINDOW(strm) &&
            (state->wsize || (out != strm->avail_out && state->mode < BAD &&
                 (state->mode < CHECK || flush != Z_FINISH)))) {
        /* update sliding window with respective checksum if not in "raw" mode */
        if (updatewindow(strm, strm->next_out, out - strm->avail_out, state->wrap & 4)) {
            state->mode = MEM;
            return Z_MEM_ERROR;
        }
-   }
+   } else if (!state->wsize && flush == Z_FINISH) {
+       inf_chksum(strm, put - (out - strm->avail_out), (out - strm->avail_out));
+   }

Vars: state->wsize = 0, flush = Z_FINISH, state->mode = CHECK

In this use case Z_FINISH is being used which according to the docs:
image

Normally, inflate might already have a window if it had been previously called with another flush type. But in libgit2 case it is very short length and calling it with Z_FINISH, a window doesn't get created or used.

@KungFuJesus
Copy link
Contributor Author

KungFuJesus commented Nov 22, 2023

This also seems to work although it can probably be better some how:

  inf_leave:
    RESTORE();
    if (INFLATE_NEED_UPDATEWINDOW(strm) &&
            (state->wsize || (out != strm->avail_out && state->mode < BAD &&
                 (state->mode < CHECK || flush != Z_FINISH)))) {
        /* update sliding window with respective checksum if not in "raw" mode */
        if (updatewindow(strm, strm->next_out, out - strm->avail_out, state->wrap & 4)) {
            state->mode = MEM;
            return Z_MEM_ERROR;
        }
-   }
+   } else if (!state->wsize && flush == Z_FINISH) {
+       inf_chksum(strm, put - (out - strm->avail_out), (out - strm->avail_out));
+   }

Vars: state->wsize = 0, flush = Z_FINISH, state->mode = CHECK

In this use case Z_FINISH is being used which according to the docs: image

Normally, inflate might already have a window if it had been previously called with another flush type. But in libgit2 case it is very short length and calling it with Z_FINISH, a window doesn't get created or used.

That solution is a lot cleaner assuming it captures every instance of this. I was hoping to find a solution more like that but couldn't bring myself to find the right spot and conditions to inject that extra forced checksum. So the right conditions are specifically if the user has specified Z_FINISH, bypassing the window creation entirely and effectively it's moved to the end stage "check" state

@KungFuJesus
Copy link
Contributor Author

KungFuJesus commented Nov 22, 2023

I like @nmoinvaz's solution much better, way less of a hack, way fewer lines, fewer comparisons. @nmoinvaz can you file a PR with your minideflate changes and that simple reproducer as a test case?

@nmoinvaz
Copy link
Member

nmoinvaz commented Nov 22, 2023

Yes, I can create a PR with my minideflate use case. I looked putting my suggested logic in CHECK mode instead, but CHECK mode bails early in NEEDSBITS with Z_BUF_ERROR. So I think at the bottom of the function like I originally suggested is probably the best place for the logic.

Copy link
Collaborator

@mtl1979 mtl1979 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs further testing...

@KungFuJesus KungFuJesus force-pushed the fix_1600 branch 2 times, most recently from 23ecfbd to d05324d Compare November 23, 2023 03:31
@KungFuJesus KungFuJesus force-pushed the fix_1600 branch 2 times, most recently from 17494ae to c6e8c11 Compare November 23, 2023 03:52
@nmoinvaz
Copy link
Member

LGTM, now we just need to rebase after #1603 is merged.

@Dead2
Copy link
Member

Dead2 commented Nov 23, 2023

Rebase please 👍

Copy link
Member

@Dead2 Dead2 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this elegant solution 👍

@KungFuJesus
Copy link
Contributor Author

Guessing we can probably ignore that failure to upload issue?

if inflate is invoked with Z_FINISH, and it deems a window was not
necessary, there's a corner case where we never checksum the bytes.
Detect this by checking the window size against zero and the value
of the flush parameter.

This should fix issue zlib-ng#1600, and possibly zlib-ng#1565 as well.
@KungFuJesus
Copy link
Contributor Author

Rerolling the dice anyway for that green check mark.

@Dead2
Copy link
Member

Dead2 commented Nov 23, 2023

Looking good, all tests passing. I will probably roll out a hotfix release with this within a day or two, unless something crops up.

Copy link
Collaborator

@mtl1979 mtl1979 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@Dead2 Dead2 merged commit 90b6c36 into zlib-ng:develop Nov 24, 2023
@Dead2 Dead2 mentioned this pull request Nov 26, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants