Skip to content
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

libcrypt.so.1 being phased out? #305

Open
dvarrazzo opened this issue May 3, 2019 · 22 comments
Open

libcrypt.so.1 being phased out? #305

dvarrazzo opened this issue May 3, 2019 · 22 comments

Comments

@dvarrazzo
Copy link

Hello,

Just received psycopg/psycopg2#912, according to which libcrypt.so.1 was deprecated on Fedora 30. This sounds like manylinux wheel packages (both 1 and 2010) are incompatible with the platform.

Is there anything I can, or should, do? Would including libcrypt (maybe 2) in the wheel package be a good idea?

@njsmith
Copy link
Member

njsmith commented May 4, 2019

In case anyone else is thrown by the name: this isn't libcrypto, which comes from openssl, but rather libcrypt, which is part of glibc. This appears to be one of the rare cases where glibc is intentionally choosing to break backwards compatibility.

Practically speaking, if some systems have libcrypt.so.1 and some have libcrypt.so.2, then manylinux wheels can't assume that either exists. I guess that means that they should be dropped from auditwheel's library whitelist, and that they should be vendored into any wheels that need them. Normally I'd hesitate to vendor part of glibc and ship it to a system that might be using a different glibc, but hopefully the libcrypt functionality is sufficiently self-contained that it will work out okay...?

@zackw might be able to advise us further.

@zackw
Copy link

zackw commented May 4, 2019

It seems Fedora has gone further with this transition than I thought any Linux distribution would seriously consider doing, in the relatively short time it's been since the new "libxcrypt" became a thing. There are actually three versions of the library in play:

A. libcrypt.so.1 built from GNU libc's source code. Hasn't changed much in years and, in particular, hasn't been keeping up with advances in password hashing. About two years ago, me and a couple other glibc developers decided the glibc development process was too conservative and slow for this library, and we began a process of splitting it out.

B. libcrypt.so.1 built from besser82/libxcrypt. This has support for newer password hashing algorithms (bcrypt, scrypt, etc), and maintains compatibility with binaries built against glibc's libcrypt, but binaries built against its headers will not be compatible with glibc's libcrypt, and you cannot compile programs that use certain obsolete functions against it (bigcrypt, fcrypt, encrypt, setkey -- nobody should be using these anymore, since they all inherently involve the use of single DES, but it's still a consideration). Fedora seems to have switched to this library in version 28.

C. libcrypt.so.2 built from besser82/libxcrypt (configure option --disable-obsolete-api). This drops binary backward compatibility with both A and B. Fedora seems to have switched to this in version 30.

I expected Linux distributions to converge on B, not C, but I guess "any program using these functions is necessarily using a block cipher with an unacceptably short key" was enough of a reason for Fedora to break them.

I agree with @njsmith that, as a practical matter, it's no longer appropriate for libcrypt.so.1 to be on the manylinux whitelist. I'm not sure how safe it is to vendor A (glibc's libcrypt); the copy on my computer does refer to some internal-use-only symbols from libc.so.6. Vendoring B or C, however, should be safe; libxcrypt uses only public interfaces, and it shouldn't matter to any program if they get
crypt@GLIBC_2.2.5 from B instead of A.

What I'd recommend, therefore, is that the manylinux1 and manylinux2010 base images be modified to supply option C (libcrypt.so.2) as the libcrypt that will be used to build extension modules and vendored into wheels. Specifically, build libxcrypt with --disable-obsolete-api --enable-hashes=all, install it in /usr/local, and then delete the crypt.h, libcrypt.a, and libcrypt.so (but not libcrypt.so.1) installed by glibc in /usr. This will break any extension modules using the obsolete functions, but if that's okay for Fedora I think it should be okay for PyPA as well. Using --enable-hashes=all guarantees backward compatibility with all historical hashed passwords.

If there's a problem building libxcrypt in a CentOS 5 environment, please file a bug report on besser82/libxcrypt and I'll see it gets fixed.

@takluyver
Copy link
Member

takluyver commented Jul 18, 2019

So if I'm understanding this correctly, the steps to be taken are:

@lkollar
Copy link
Contributor

lkollar commented Jul 18, 2019

Shouldn't we update the PEPs as well? auditwheel will start reporting previously compliant wheels as non-compliant and given that libcrypt.so.1 is still on the list of allowed libraries in the PEPs, this can create confusion.

@takluyver
Copy link
Member

Yup, updating the PEPs is done now: python/peps#1124.

@zackw
Copy link

zackw commented Jul 19, 2019

@takluyver When adding libcrypt.so.2to the manylinux docker images, make sure to verify that it gets used for wheels that call the C function crypt, rather than the libcrypt.so.1 that shipped with CentOS. I am 95% sure that the changes I described above

build libxcrypt with --disable-obsolete-api --enable-hashes=all, install it in /usr/local, and then delete the crypt.h, libcrypt.a, and libcrypt.so (but not libcrypt.so.1) installed by glibc in /usr

will accomplish this, but it still needs testing.

@takluyver
Copy link
Member

To check that, I assume one can run ldd on a relevant binary and check which libcrypt it's linked against?

It would also be good if someone could provide a sample extension module which calls crypt, because it would probably take me a long time to piece together an example.

Finally: anyone reading this, please feel free to work on the manylinux images. I'm about to be offline for a week, and even when I am here, I'm not that hot on anything that involves writing or compiling C code. I wrote the checklist above as what needs doing, not saying that I'm going to do it. 🙂

@encukou
Copy link

encukou commented Jul 19, 2019

I learned about this issue in EuroPython. After some discussion, we're updating Fedora's pip to Recommend libcrypt.so.1. (“Recommend” means a soft dependency that's installed by default.)
This should hide the issue for regular users.
Of course, removing it from the manylinux standards is still the way to go.

When testing on Fedora, please make sure you don't have libcrypt.so.1 (use sudo dnf remove libxcrypt-compat).

@takluyver
Copy link
Member

(While I'm not doing systematic testing, I did note somewhere that I haven't come across any wheels which fail to load. So just to confirm: I don't have libcrypt.so.1; removing libxcrypt-compat said "Nothing to do.")

@lkollar
Copy link
Contributor

lkollar commented Jul 19, 2019

I submitted PRs for the manylinux images. GCC 8.2 failed to build libxcrypt on the manylinux2010 image so I will have to look into that.

@zackw
Copy link

zackw commented Jul 19, 2019

I'm writing a trivial extension module to test this change with. Would it be it useful for me to put a sdist of this module on PyPI, or will it be enough to have a public git repo? I don't know Fedora well enough to do the actual testing myself.

@takluyver
Copy link
Member

Thanks @lkollar . I've updated the checklist above to point to those PRs.

@zackw - we can probably work with it any form, but if it's easy to put an sdist on PyPI, please do. It may be useful to have a tarball with a stable hash - I'm told that the automatic tarballs from Github tags can change slightly.

@zackw
Copy link

zackw commented Jul 19, 2019

@takluyver OK, it's uploaded: https://pypi.org/project/pyphash/ and/or https://github.com/zackw/pyphash

@zackw
Copy link

zackw commented Jul 19, 2019

After all of the steps listed above are taken, we should look through PyPI for binary wheels that use libcrypt.so.1 and poke their maintainers to rebuild and re-upload them. (As far as I know, there's no more automatic way to make this happen.)

@takluyver
Copy link
Member

Something similar was suggested on pypi/warehouse#5420. The Warehouse maintainers might be able to say if it's practical to do something like that, and how to find all packages where the latest release has a manylinux wheel.

@dvarrazzo
Copy link
Author

I am about to release a new psycopg version. I assume the issue is not fixed, right?

@encukou
Copy link

encukou commented Oct 20, 2019

I see that all PRs linked from the "steps to be taken" comment are merged. If I understand this correctly, it means new wheels should bundle libcrypt.so.2.

Since this happened before the Python 3.8 release, it seems to me that no real-world wheels for Python 3.8 will need libcrypt.so.1. A distro will only need to provide it for Python 3.7 and below.

@dvarrazzo
Copy link
Author

dvarrazzo commented Oct 20, 2019

As far as I can see, packages created yesterday with the latest version of manylinux don't include libcrypt:

$ unzip -l ../psycopg2_binary-2.8.4-cp38-cp38-manylinux1_x86_64.whl | grep crypt
  3217584  2019-10-20 00:57   psycopg2/.libs/libcrypto-3a9cf061.so.1.1.1d
   167808  2019-10-20 00:57   psycopg2/.libs/libk5crypto-622ef25b.so.3.1

Packages were created using the quay.io/pypa/manylinux1_x86_64 docker image.

Has the procedure to build packages changed?

@codonell
Copy link

@zackw @njsmith Do you think is it possible, given that we're building libcrypt.so.2 (from libxcrypt) for the manylinux* containers, that we build libcrypt.so.1 (compat version again from libxcrypt) and use that specifically for bundling (hiding it otherwise because of the struct size differences) when the system libcrypt.so.1 is required? (@fweimer and I just chatted about this on IRC) The libxcrypt version of libcrypt.so.1 has no GLIBC_PRIVATE dependencies and is a drop-in replacement for the system libcrypt.so.1. This would allow developers to continue to use parts of OS that require libcrypt.so.1. That would solve some problems I've seen @dralley having (just talked with him today about this impact).

@zackw
Copy link

zackw commented Feb 24, 2020

@codonell Yes, I think that is a sane configuration. You'd have libcrypt.a and libcrypt.so matching libcrypt.so.2, but include libcrypt.so.1 on the file system for anything that might expect that via DT_NEEDED, and it shouldn't cause problems if auditwheel decides to bundle it.

As libxcrypt upstream I'm actually working on a restructure where we always build both, with all the real code in libcrypt.so.2 and libcrypt.so.1 being a thin wrapper with the compatibility symbols.

@mayeut
Copy link
Member

mayeut commented May 8, 2020

@zackw,

I was looking into including libcrypt.so.1 from libxcrypt in addition to libcrypt.so.2 in the manylinux images (and development dependencies for the latter). It seems libcrypt.so.1 also exports all symbols from libcrypt.so.2 and, not knowing the internals of libxcrypt, I find that a bit dangerous that a wheel could actually embed both (libcrypt.so.1 through system library dependency + libcrypt.so.2 as direct dependency using dev version) not knowing exactly which symbols would be mapped in linking phase.
Are there any recommendation while waiting for the "always build both" approach you mentioned ?

I came up with some ugly patching of libcrypt.map.in file in order to reduce the set of exposed function to the one actually exported by the system library but I'm wondering if this is the way to go.

Hugly patch:

$ sed -r -i 's/XCRYPT_([0-9.])+/-/g;s/(%chain OW_CRYPT_1.0).*/\1/g' lib/libcrypt.map.in
$ ./autogen.sh
$ ./configure --disable-xcrypt-compat-files --enable-obsolete-api=glibc --enable-hashes=all --disable-werror
$ make DESTDIR=$(pwd)/so.1 install
$ nm -g -D --with-symbol-versions --defined-only ./so.1/usr/local/lib/libcrypt.so.1 | awk '{ print $3 }' | sort
crypt@GLIBC_2.2.5
crypt_r@GLIBC_2.2.5
encrypt@GLIBC_2.2.5
encrypt_r@GLIBC_2.2.5
fcrypt@GLIBC_2.2.5
GLIBC_2.2.5@@GLIBC_2.2.5
setkey@GLIBC_2.2.5
setkey_r@GLIBC_2.2.5

@mayeut
Copy link
Member

mayeut commented May 9, 2020

The newly merged PR should allow to graft libcrypt.so.1.
I'm keeping this issue opened as a reminder that libxcrypt will (soon ?) provide both shared object with 1 build which is ultimately what should be in the images.

mwoehlke-kitware added a commit to RobotLocomotion/pip-drake that referenced this issue Sep 2, 2021
Patch out use of python-dbg, which is not available for other Python
versions. This should be sufficient that Drake can build. However, due
to some nasty business with libcrypt, which for some reason is not used
by Python 3.6, but is required by Python 3.7, we are not yet able to
produce a PyPI-usable wheel.

See also pypa/manylinux#305 for an explanation
of why this is broken.
mwoehlke-kitware added a commit to RobotLocomotion/pip-drake that referenced this issue Sep 8, 2021
Patch out use of python-dbg, which is not available for other Python
versions. This should be sufficient that Drake can build. However, due
to some nasty business with libcrypt, which for some reason is not used
by Python 3.6, but is required by Python 3.7, we are not yet able to
produce a PyPI-usable wheel.

See also pypa/manylinux#305 for an explanation
of why this is broken.
mwoehlke-kitware added a commit to RobotLocomotion/pip-drake that referenced this issue Sep 10, 2021
Replace both the standard and debugging Python interpreters with the
standard interpreter from our virtualenv. This is correct for the
former, and for the latter, should suffice to make the build system
happy, while not ever actually being used during a wheel build (so it
won't matter that it isn't a debug interpreter).

This should be sufficient that Drake can build. However, due to some
nasty business with libcrypt, which for some reason is not used by
Python 3.6, but is required by Python 3.7, we are not yet able to
produce a PyPI-usable wheel.

See also pypa/manylinux#305 for an explanation
of why this is broken.

fixup
mwoehlke-kitware added a commit to RobotLocomotion/pip-drake that referenced this issue Sep 15, 2021
Replace both the standard and debugging Python interpreters with the
standard interpreter from our virtualenv. This is correct for the
former, and for the latter, should suffice to make the build system
happy, while not ever actually being used during a wheel build (so it
won't matter that it isn't a debug interpreter).

This should be sufficient that Drake can build. However, due to some
nasty business with libcrypt, which for some reason is not used by
Python 3.6, but is required by Python 3.7, we are not yet able to
produce a PyPI-usable wheel.

See also pypa/manylinux#305 for an explanation
of why this is broken.

fixup
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants