-
-
Notifications
You must be signed in to change notification settings - Fork 30.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
crc32 function outputs wrong result for large data on the macOS arm64 platform #105967
Comments
Thanks for the report. The Python standard library zlib and binascii modules depend on the widely-used third-party zlib library (libz) to do much of their work. The misbehavior shown in your test case appears to be due to a bug of some sort in the native Apple Silicon (arm64) libz binary shipped by Apple with current releases of macOS. The incorrect results can be seen on Apple Silicon Macs running any current release of macOS that support arm64, that is, macOS 11 Big Sur, macOS 12 Monterey, and macOS 13 Ventura. The upcoming macOS 14 Sonoma, which is currently in restricted beta, appears likely to be released with an updated version of libz that does not have this problem. The Apple-supplied libz on macOS 11 through 13 reports its version as 1.2.11. However, a quick look at the zlib project files published on Apple's open-source page (https://opensource.apple.com/releases/) shows that the Apple version has been significantly patched from the original upstream 1.2.11 release (linked to from https://www.zlib.net). I was unable to reproduce the failure here on either Apple Silicon or Intel Macs when using a libz built from unmodified upstream zlib source for 1.2.11, 1.2.12, or 1.2.13 (the most recent release) nor with current libz 1.2.13 binaries from either Homebrew or MacPorts. So, the problem seems to be confined to the Apple-supplied libz on Apple Silicon Macs. By default, when building on macOS, cpython will dynamically link with a number of open-source libraries supplied with macOS, including libz. While there are advantages to this approach and has been encouraged by Apple in the past, it can be a disadvantage if the system-supplied version has a bug. A non-exhaustive survey of several ways of using cpython on macOS:
This is not the first time that problems have been found using the zlib and binascii modules with crc32 and very large data buffers, for example, #54485 and, more recently, #82437. (The Python 3.9.6 supplied with current releases of Apple's developer tools exhibits both the problem identified in this issue and the problem in #82437 which was fixed in a later release of 3.9.) So, assuming the above analysis is correct and that there is no problem within cpython itself, what should be done to mitigate this? Ideally, Apple would address the problem with an updated libz while all three affected macOS release families are still receiving updates. To that end, we should then ensure that an issue is open with Apple about this problem. Given the uncertainty of if and when such a fix would be available, distributors of cpython for macOS and users who build cpython from source will need to evaluate the risk to their users and applications. If necessary, builds can be changed to use a non-system libz. That would be fairly straight-forward for distributions like the python.org installers and Homebrew which already make use of third-party dynamic shared libraries. Stand-alone applications need to be careful when linking and shipping a new shared library, perhaps needing RPATH directives, or by forcing static linking with libz, something which macOS deliberately does not make easy to do. Comments? Other options? cc: @ronaldoussoren, @ambv, @gpshead. @Yhg1s |
We should definitely file and issue with Apple about this, ideally with a reproducer in C. A fix in the macOS 14 beta doesn't bode well for getting this fixed in older releases though. That said, most fixes I've seen for issues I filed about system libraries like this were fixed in new major releases anyway. Other than that I'm not sure. Do you know what kind of changes there are in the Apple for of libz and if those are relevant for our users (for example by enabling some kind of hardware offload)? If the changes aren't relevant we could start shipping our own copy of libz, the build tooling is set up for that although it would add yet another moving part to track and be concerned about when there's a release that doesn't fit into the cpython release schedule. Another option we could look into: Is it possible to tweak the our CRC32 bindings to call the libz function with small enough buffers to not trigger the issue? The disadvantage of that it that it would complicate our code base, but hopefully not too much. |
@ned-deily libz is used by a huge pile of software other than Python, what do other things use instead of the system libz? While we could ship our own zlib on macOS but it'd be a shame as the standard zlib is very boring and unoptimized; dynamically linking against the platform one means they're responsible for security and performance updates. |
ex: zip and unzip on macOS, what do those use? what about Java and Ruby? |
From a risk perspective we already build our own upstream zlib 1.2.13 for Windows binary releases so statically linking against a build of that on macOS isn't a huge burden, zlib security fixes trigger Python binary security releases anyways. #91349 (use zlib-chromium or zlib-ng) and #93819 (we need zlib in WASI) are tangentially related FWIW. |
re: Apple bug vs our bug, we should verify that by writing a tiny C reproducer using libz to confirm the issue. From a CPython bugfix perspective, we do still carry a copy of our own |
It will be eliminated by gh-32043 (see |
If there is wish to continue to link with platform libz, how about setting the loop limit to 1G instead 4G-1 (UINT32_MAX)? That would isolate the code from platform corner-cases and its unlikely to give any measurable slowdown. |
The following is a C program that tries to reproduce the issue based on the code in binascii.c: #include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "zlib.h"
int main(void)
{
unsigned char* buffer = malloc(4L * 1024 * 1024 * 1024);
if (buffer == NULL) {
perror("malloc");
return 2;
}
for (size_t k = 0; k < 4L * 1024 * 1024 * 1024; k++) {
buffer[k] = k & 0xff;
}
unsigned int crc_whole = 0;
unsigned int crc_chunked = 0;
// use two chunks for crc_whole because 4G is larger than UINT_MAX,
// 1 big chunk and 1 chunk for the rest
crc_whole = crc32(crc_whole, buffer, 4L * 1024 * 1024 * 1024 - 1);
crc_whole = crc32(crc_whole, buffer + 4L * 1024 * 1024 * 1024 - 1, 1);
for (unsigned int i = 0; i < 4; i++) {
crc_chunked = crc32(crc_chunked, buffer + (i * 1024 * 1024 * 1024), 1 * 1024 * 1024 * 1024);
}
printf("W: %#x\n", crc_whole);
printf("C: %#x\n", crc_chunked);
printf("W == C: %d\n", crc_whole == crc_chunked);
return !(crc_whole == crc_chunked);
} Build using For me this prints:
That is, both approaches get the same CRC. System version (M1 MacBook):
I get the same result in a VM running 10.15.5. |
My results are expected given @ned-deily's earlier remarks... Limiting chunk size in calls to crc32(3) to 1 GB is the easiest workaround on our end. |
…GiB. Without this, `zlib.crc32` and `binascii.crc32` could produce incorrect results on multi-gigabyte inputs depending on the macOS version's Apple supplied zlib implementation.
Can someone who can reproduce the original problem confirm if applying my #112615 change to limit the calls to 1gig solves it for them? |
…lls to 1gig (#112615) Work around a macOS bug, limit zlib crc32 calls to 1GiB. Without this, `zlib.crc32` and `binascii.crc32` could produce incorrect results on multi-gigabyte inputs depending on the macOS version's Apple supplied zlib implementation.
…API calls to 1gig (pythonGH-112615) Work around a macOS bug, limit zlib crc32 calls to 1GiB. Without this, `zlib.crc32` and `binascii.crc32` could produce incorrect results on multi-gigabyte inputs depending on the macOS version's Apple supplied zlib implementation. (cherry picked from commit 4eddb4c) Co-authored-by: Gregory P. Smith <greg@krypto.org>
…API calls to 1gig (pythonGH-112615) Work around a macOS bug, limit zlib crc32 calls to 1GiB. Without this, `zlib.crc32` and `binascii.crc32` could produce incorrect results on multi-gigabyte inputs depending on the macOS version's Apple supplied zlib implementation. (cherry picked from commit 4eddb4c) Co-authored-by: Gregory P. Smith <greg@krypto.org>
… API calls to 1gig (GH-112615) (#112725) gh-105967: Work around a macOS bug, limit zlib C library crc32 API calls to 1gig (GH-112615) Work around a macOS bug, limit zlib crc32 calls to 1GiB. Without this, `zlib.crc32` and `binascii.crc32` could produce incorrect results on multi-gigabyte inputs depending on the macOS version's Apple supplied zlib implementation. (cherry picked from commit 4eddb4c) Co-authored-by: Gregory P. Smith <greg@krypto.org>
… API calls to 1gig (GH-112615) (#112724) gh-105967: Work around a macOS bug, limit zlib C library crc32 API calls to 1gig (GH-112615) Work around a macOS bug, limit zlib crc32 calls to 1GiB. Without this, `zlib.crc32` and `binascii.crc32` could produce incorrect results on multi-gigabyte inputs depending on the macOS version's Apple supplied zlib implementation. (cherry picked from commit 4eddb4c) Co-authored-by: Gregory P. Smith <greg@krypto.org>
I've run some tests and your fix does seem to successfully workaround the Apple zlib bug on Apple Silicon Macs running macOS 12.6.1 Monterey and macOS 13.6.1 Ventura (I don't have access at the moment to macOS 11 Big Sur on an Apple Silicon Mac) and have checked that there is no regression on Intel Macs. Interestingly, it appears that Apple has fixed the problem in at least the most recent release of macOS 14 Sonoma: on macOS 14.1.2, the most recent release of macOS, the test runs correctly on Apple Silicon without the fix (and with). Thanks for the PR, @gpshead. What do you think about adding a test for this? It might show up again anywhere. |
I was pondering that. I think what we're missing is bigmem test coverage for these being run on the relevant macOS platforms? I haven't gone looking to see if existing tests would've explicitly caught this though (if not, we should add some, and get one relevant buildbot set with a high enough memory limit on its regrtest command line regardless) |
Both binascii and zlib have bigmem tests that should have caught this: cpython/Lib/test/test_binascii.py Lines 496 to 502 in 5aa317e
and Lines 105 to 110 in 5aa317e
Buildbots run tests with |
Is there anything left to do for this issue? |
I verified that both However, running with |
Thanks for confirming. It's basically us happening to not have the "right" older version OS runners that exhibit the platform bug in the text matrix. Existing tests would've worked if that style of test had been run there. Good to know, but there's not anything we can do about this code wise. |
It may be that we don't have a vulnerable system now in our test matrix but we did up until not many months ago. Shouldn't we be testing the bigger mem cases on most platforms? |
…API calls to 1gig (python#112615) Work around a macOS bug, limit zlib crc32 calls to 1GiB. Without this, `zlib.crc32` and `binascii.crc32` could produce incorrect results on multi-gigabyte inputs depending on the macOS version's Apple supplied zlib implementation.
…API calls to 1gig (python#112615) Work around a macOS bug, limit zlib crc32 calls to 1GiB. Without this, `zlib.crc32` and `binascii.crc32` could produce incorrect results on multi-gigabyte inputs depending on the macOS version's Apple supplied zlib implementation.
Bug report
The functions zlib.crc32 and binascii.crc32 share the problematic behavior. When computing the CRC for data >= 2GB macOS arm64 binaries result in different values than all other platforms such as macOS x64, Windows x64, Linux x64. Consequently, problems arise e.g. when using the zipfile module.
A clear and concise description of what the bug is.
Reproduction:
Output on macOS arm64:
Your environment
Linked PRs
The text was updated successfully, but these errors were encountered: