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

macOS healthcheck checks dylib install ID as if it was a dependency #1066

Open
mx-psi opened this issue May 5, 2022 · 1 comment
Open

macOS healthcheck checks dylib install ID as if it was a dependency #1066

mx-psi opened this issue May 5, 2022 · 1 comment

Comments

@mx-psi
Copy link

mx-psi commented May 5, 2022

Description

When building for macOS, Omnibus includes a healthcheck of dylibs via otool:

def health_check_otool
current_library = nil
bad_libs = {}
good_libs = {}
read_shared_libs("find #{project.install_dir}/ -type f | egrep '\.(dylib|bundle)$'", "xargs otool -L") do |line|
case line
when /^(.+):$/
current_library = Regexp.last_match[1]
when /^\s+(.+) \(.+\)$/
linked = Regexp.last_match[1]
name = File.basename(linked)
bad_libs, good_libs = check_for_bad_library(bad_libs, good_libs, current_library, name, linked)
end
end
[bad_libs, good_libs]
end

As an example (from python-lz4/python-lz4#244), let's assume that the otool -L output is as follows:

❯ otool -L lz4-4.0.0/lz4/.dylibs/liblz4.1.9.3.dylib
lz4-4.0.0/lz4/.dylibs/liblz4.1.9.3.dylib:
	/DLC/lz4/.dylibs/liblz4.1.9.3.dylib (compatibility version 1.0.0, current version 1.9.3)
	/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1292.0.0)

This roughly does the following:

  1. run otool -L output on all dylibs
  2. for each dylib, take the library name as the first line from the output (which has a :), in this case lz4-4.0.0/lz4/.dylibs/liblz4.1.9.3.dylib
  3. the rest of lines are taken as dependencies of this library, and are passed to check_for_bad_library, which roughly checks that the dependencies refer to paths inside the project (modulo some whitelisting), excluding the library name taken on step (2). In our example, it would check for /DLC/lz4/.dylibs/liblz4.1.9.3.dylib and /usr/lib/libSystem.B.dylib.

This is incorrect in that not all lines of otool -L output are actually dependencies of the library. In particular, the first line can be the install name ID, which usually but not necessarily matches the library path, as explained in matthew-brett/delocate#150 (comment) (continuing with the example above, /DLC/lz4/.dylibs/liblz4.1.9.3.dylib would be the install name ID).

This causes projects that bundle dylibs with install IDs not matching their relative paths to incorrectly fail the healthcheck. The solution is to first run otool -D and exclude this from the libraries checked by check_for_bad_library (see example on delocate library)

Omnibus Version

The code currently on main (as of commit 27c37fc) should reproduce this issue.

Platform Version

This affects macOS builds only.

Replication Case

This can be reproduced with the Python lz4 pip package, version 4.0.0 (again, see python-lz4/python-lz4#244 for many more details). The project where we found this uses a fork of Omnibus and it's hard for me to create MWE, but I hope the detailed information above is enough to reproduce!

Build Output

Healthcheck failure logs

This comes from here, which depending at which point in the future you read, it may be gone from Github.

 The health check failed! Please see above for important information.
            [HealthCheck] E | 2022-03-28T21:44:23+00:00 | The following libraries have unsafe or unmet dependencies:

    --> /opt/datadog-agent//embedded/lib/python3.8/site-packages/lz4/.dylibs/liblz4.1.9.3.dylib
/usr/local/lib/ruby/gems/2.7.0/bundler/gems/omnibus-ruby-012c4078aa84/lib/omnibus/health_check.rb:346:in `block in run!'
  /usr/local/lib/ruby/gems/2.7.0/bundler/gems/omnibus-ruby-012c4078aa84/lib/omnibus/instrumentation.rb:23:in `measure'

            [HealthCheck] E | 2022-03-28T21:44:23+00:00 | The following binaries have unsafe or unmet dependencies:
  /usr/local/lib/ruby/gems/2.7.0/bundler/gems/omnibus-ruby-012c4078aa84/lib/omnibus/health_check.rb:246:in `run!'

  /usr/local/lib/ruby/gems/2.7.0/bundler/gems/omnibus-ruby-012c4078aa84/lib/omnibus/health_check.rb:214:in `run!'
            [HealthCheck] E | 2022-03-28T21:44:23+00:00 | The following libraries cannot be guaranteed to be on target systems:
  /usr/local/lib/ruby/gems/2.7.0/bundler/gems/omnibus-ruby-012c4078aa84/lib/omnibus/project.rb:1416:in `build'
    --> /DLC/lz4/.dylibs/liblz4.1.9.3.dylib
  /usr/local/lib/ruby/gems/2.7.0/bundler/gems/omnibus-ruby-012c4078aa84/lib/omnibus/cli.rb:91:in `build'
  /usr/local/lib/ruby/gems/2.7.0/gems/thor-0.20.3/lib/thor/command.rb:27:in `run'
  /usr/local/lib/ruby/gems/2.7.0/gems/thor-0.20.3/lib/thor/invocation.rb:126:in `invoke_command'
  /usr/local/lib/ruby/gems/2.7.0/gems/thor-0.20.3/lib/thor.rb:387:in `dispatch'
  /usr/local/lib/ruby/gems/2.7.0/bundler/gems/omnibus-ruby-012c4078aa84/lib/omnibus/cli/base.rb:33:in `dispatch'
  /usr/local/lib/ruby/gems/2.7.0/gems/thor-0.20.3/lib/thor/base.rb:466:in `start'
  /usr/local/lib/ruby/gems/2.7.0/bundler/gems/omnibus-ruby-012c4078aa84/lib/omnibus/cli.rb:42:in `execute!'
  /usr/local/lib/ruby/gems/2.7.0/bundler/gems/omnibus-ruby-012c4078aa84/bin/omnibus:16:in `<top (required)>'
  /usr/local/lib/ruby/gems/2.7.0/bin/omnibus:25:in `load'
  /usr/local/lib/ruby/gems/2.7.0/bin/omnibus:25:in `<top (required)>'
  /usr/local/lib/ruby/gems/2.7.0/gems/bundler-2.3.9/lib/bundler/cli/exec.rb:58:in `load'
  /usr/local/lib/ruby/gems/2.7.0/gems/bundler-2.3.9/lib/bundler/cli/exec.rb:58:in `kernel_load'
  /usr/local/lib/ruby/gems/2.7.0/gems/bundler-2.3.9/lib/bundler/cli/exec.rb:23:in `run'
  /usr/local/lib/ruby/gems/2.7.0/gems/bundler-2.3.9/lib/bundler/cli.rb:483:in `exec'
  /usr/local/lib/ruby/gems/2.7.0/gems/bundler-2.3.9/lib/bundler/vendor/thor/lib/thor/command.rb:27:in `run'
  /usr/local/lib/ruby/gems/2.7.0/gems/bundler-2.3.9/lib/bundler/vendor/thor/lib/thor/invocation.rb:127:in `invoke_command'
  /usr/local/lib/ruby/gems/2.7.0/gems/bundler-2.3.9/lib/bundler/vendor/thor/lib/thor.rb:392:in `dispatch'
  /usr/local/lib/ruby/gems/2.7.0/gems/bundler-2.3.9/lib/bundler/cli.rb:31:in `dispatch'
  /usr/local/lib/ruby/gems/2.7.0/gems/bundler-2.3.9/lib/bundler/vendor/thor/lib/thor/base.rb:485:in `start'
  /usr/local/lib/ruby/gems/2.7.0/gems/bundler-2.3.9/lib/bundler/cli.rb:25:in `start'
  /usr/local/lib/ruby/gems/2.7.0/gems/bundler-2.3.9/exe/bundle:48:in `block in <top (required)>'
  /usr/local/lib/ruby/gems/2.7.0/gems/bundler-2.3.9/lib/bundler/friendly_errors.rb:103:in `with_friendly_errors'
  /usr/local/lib/ruby/gems/2.7.0/gems/bundler-2.3.9/exe/bundle:36:in `<top (required)>'
  /usr/local/lib/ruby/gems/2.7.0/bin/bundle:23:in `load'
  /usr/local/lib/ruby/gems/2.7.0/bin/bundle:23:in `<main>'

            [HealthCheck] E | 2022-03-28T21:44:23+00:00 | The precise failures were:
    --> /opt/datadog-agent//embedded/lib/python3.8/site-packages/lz4/.dylibs/liblz4.1.9.3.dylib
    DEPENDS ON: liblz4.1.9.3.dylib
      COUNT: 1
      PROVIDED BY: /DLC/lz4/.dylibs/liblz4.1.9.3.dylib
      FAILED BECAUSE: Unsafe dependency

            [HealthCheck] I | 2022-03-28T21:44:23+00:00 | Health check time: 1.7175s
@mx-psi
Copy link
Author

mx-psi commented Jan 18, 2024

On our fork we fixed this in DataDog/pull/168

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

1 participant