[MacPorts] #63415: p5.28-net-ssleay failed to compile under Monterey (21A5304g)

MacPorts noreply at macports.org
Thu Oct 28 17:08:24 UTC 2021


#63415: p5.28-net-ssleay failed to compile under Monterey (21A5304g)
----------------------------+--------------------------
  Reporter:  ilTofa         |      Owner:  markmentovai
      Type:  defect         |     Status:  reopened
  Priority:  Normal         |  Milestone:
 Component:  ports          |    Version:  2.7.1
Resolution:                 |   Keywords:  monterey
      Port:  p5-net-ssleay  |
----------------------------+--------------------------

Comment (by markmentovai):

 This is getting off into the weeds a little bit, but I can’t think of a
 better place to discuss.

 [comment:36 @breiter comment:36]:

  If you try to `dlopen` a /path/to/libcrypto.dylib that doesn't exist on
 disk `dlopen` will not simply return ENOENT but will instead `dlopen`
 libcrypto.dylib from the shared cache -- if it exists. Any code that tries
 to find a different version of a library that Apple includes in the shared
 cache by probing a set of paths until there is no ENOENT will open the
 Apple shared dylib version instead of the one it was looking for unless
 the first path it probed was correct. What happened here is the build
 script tried to `dlopen` /opt/local/libcrypto.dylib and if it had gotten
 ENOENT would have correctly loaded /opt/local/lib/libcrypto.dylib. Instead
 `dlopen` /opt/local/libcrypto.dylib which is set up to `abort()` unless it
 is opened by Apple's `/usr/bin/python2` (and maybe some other things) but
 print out a warning:
 {{{
 WARNING: Executing a script that is loading libcrypto in an unsafe way.
 This will fail in a future version of macOS. Set the
 LIBRESSL_REDIRECT_STUB_ABORT=1 in the environment to force this into an
 error.
 }}}
  /usr/bin/python3 just aborts. Presumably there is some cohort of
 important stuff that relies upon /usr/bin/python to load
 /usr/lib/libcrypto.dylib that Apple doesn't want to totally break, yet.

 This is essentially correct. `dlopen` doesn’t promise to expose `errno` in
 any particular way, but `dlopen`, upon encountering `ENOENT` internally,
 ''should'' fail and return `NULL` (and `dlerror` would indicate the
 problem as “image not found” (macOS 11) or, now, “no such file”). Instead,
 we’re seeing this new fallback behavior in macOS 12 that causes it to
 search for the library based on the path’s leaf name. You can see the
 search path by inspecting what `dlerror` has to say when you try to
 `dlopen` a library that doesn’t exist ''and'' for which the fallback
 search fails (so, ''not'' `libcrypto.dylib`):

 {{{
 mark at sweet16 zsh% ~/t_dlopen /enoent/libenoent.dylib
 t_dlopen: dlopen: dlopen(/enoent/libenoent.dylib, 0x0005): tried:
 '/enoent/libenoent.dylib' (no such file), '/usr/local/lib/libenoent.dylib'
 (no such file), '/usr/lib/libenoent.dylib' (no such file)
 }}}

 You can also, perhaps more directly, set `DYLD_PRINT_SEARCHING=1`, which
 indicates that this is called “default fallback”:

 {{{
 mark at sweet16 zsh% DYLD_PRINT_SEARCHING=1 ~/t_dlopen
 /enoent/libenoent.dylib
 dyld[59641]: find path "/usr/lib/libSystem.B.dylib"
 dyld[59641]:   possible path(original path): "/usr/lib/libSystem.B.dylib"
 dyld[59641]:   found: dylib-from-cache: (0x00AA)
 "/usr/lib/libSystem.B.dylib"
 dyld[59641]: find path "/enoent/libenoent.dylib"
 dyld[59641]:   possible path(original path): "/enoent/libenoent.dylib"
 dyld[59641]:   possible path(default fallback):
 "/usr/local/lib/libenoent.dylib"
 dyld[59641]:   possible path(default fallback): "/usr/lib/libenoent.dylib"
 dyld[59641]:   not found: "/enoent/libenoent.dylib"
 t_dlopen: dlopen: dlopen(/enoent/libenoent.dylib, 0x0005): tried:
 '/enoent/libenoent.dylib' (no such file), '/usr/local/lib/libenoent.dylib'
 (no such file), '/usr/lib/libenoent.dylib' (no such file)
 }}}

 The “soft” warning discussing `LIBRESSL_REDIRECT_STUB_ABORT` comes in when
 the main executable is something explicitly allow-listed by the poisoned
 `libcrypto.dylib` stub. The list is:

 {{{
 /usr/bin/perl
 /usr/bin/python
 /usr/bin/ruby
 /usr/bin/tcl
 /usr/bin/tk
 /System/Library/Frameworks/Python.framework
 /System/Library/Frameworks/Ruby.framework
 /System/Library/Frameworks/Tcl.framework
 /usr/local/bin/astris
 }}}

 If your main executable, as identified by `NSGetExecutablePath`, begins
 with any of those paths, then you get the warning message but no
 `abort()`, unless you set the environment variable as the message
 describes. This is a substring search, so you might assume that
 `/usr/bin/python` ought to catch both Apple-shipped `python2` and
 `python3`. `python2`, however, actually runs out of
 `/System/Library/Frameworks/Python.framework/Versions/2.7/bin/python2.7`,
 and you can see how that comes into play in the allow-list. `python3` is
 unbundled from the OS, and what you see at `/usr/bin/python3` is actually
 a stub to delegate to `python3` embedded within the active developer tools
 installation in accordance with `xcode-select` rules (so, respecting
 `DEVELOPER_DIR`, etc. This ultimately winds up at a path like
 `/Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/3.8/Resources/Python.app/Contents/MacOS/Python`
 which, as you can see, is ''not'' in the allow-list, so Apple’s
 `libcrypto.dylib` is poison even for that interpreter.

 The shared cache issue is ''somewhat'' irrelevant here. Internally, dyld
 is attempting a fallback by searching /usr/local/lib and /usr/lib for the
 leaf name of the module you’re attempting to load. It just so happens that
 `libcrypto.dylib` can be serviced via the cache and not the filesystem.
 You can even “beat” the poisoned `libcrypto.dylib` in /usr/lib (virtually,
 via the shared cache) by sticking your own `libcrypto.dylib` in
 /usr/local/lib and have it do whatever you want:

 {{{
 mark at sweet16 zsh% rm -f /usr/local/lib/libcrypto.dylib
 mark at sweet16 zsh% ./t_dlopen /opt/local/libcrypto.dylib
 WARNING: /Users/mark/t_dlopen is loading libcrypto in an unsafe way
 zsh: abort      ./t_dlopen /opt/local/libcrypto.dylib
 mark at sweet16 zsh% clang -Wall -Werror -x c -dynamiclib -o
 /usr/local/lib/libcrypto.dylib - << __EOF__
 #include <stdio.h>

 __attribute__((constructor))
 static void Initialize() {
   fprintf(stderr, "I may not be the module you intended.\n");
 }
 __EOF__
 mark at sweet16 zsh% ./t_dlopen /opt/local/libcrypto.dylib
 I may not be the module you intended.
 }}}

  The path /usr/lib/libcrypto.dylib also doesn't exist.
 {{{
 $ ls /usr/lib/libcrypto.dylib
 ls: cannot access '/usr/lib/libcrypto.dylib': No such file or directory
 }}}

 This is normal. Since macOS 11, Apple no longer ships any dylibs in the
 filesystem for things that are contained within the dyld shared cache.

  Is this true for '''any''' dylib? If you try to `dlopen` something that
 doesn't exist on disk but does exist in the shared cache, you just
 magically get the shared cache version (that seems bad)? Or a special case
 bug libcrypto.dylib (less bad)?

 There’s no special-casing here. This is how dyld behaves now on macOS 12,
 although it reflects new additions to the search path and not strictly the
 shared cache per se—but libraries can certainly be provided by the shared
 cache, as in the `libcrypto.dylib` case. It’s contrary to the `dlopen`
 documentation and, in my opinion, it’s a bug.

-- 
Ticket URL: <https://trac.macports.org/ticket/63415#comment:41>
MacPorts <https://www.macports.org/>
Ports system for macOS


More information about the macports-tickets mailing list