From 09e0943398d6223b5671f0cf78054fb8d8bfe66d Mon Sep 17 00:00:00 2001 From: Chris Oliver Date: Mon, 28 Sep 2026 18:24:41 -0500 Subject: [PATCH] Hide the bundled OpenSSL's symbols from other libraries openssl.so links OpenSSL statically but exported all of it, unversioned. A system libpq (the pg gem) then bound its OpenSSL 3 calls to Ruby's copy, and with pg loaded first openssl.so called into libssl.so.3 instead. On the OpenSSL 1.x series (1.8 through 3.1) that crashes Net::HTTPS or fails Postgres TLS handshakes in any app that uses both. Link with --exclude-libs for libssl.a and libcrypto.a. rbconfig keeps the flag, so native gems that link the bundled OpenSSL (puma) get it too, and the package tests now fail a build whose extensions export OpenSSL. Claude-Session: https://claude.ai/code/session_0146HthzF4yTdERCfixDX2Mh --- libexec/package.rb | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/libexec/package.rb b/libexec/package.rb index 6b7acbc..94d5184 100755 --- a/libexec/package.rb +++ b/libexec/package.rb @@ -655,6 +655,11 @@ def ruby_build_env if linux? cppflags << "-I#{File.join(dep_prefix("libxcrypt"), "include")}" ldflags << "-L#{File.join(dep_prefix("libxcrypt"), "lib")}" + # The static OpenSSL linked into openssl.so would otherwise be exported unversioned, + # and a system libpq (pg gem) binds its OpenSSL 3 calls to it: an OpenSSL 1.x series + # segfaults or fails its handshakes. Hiding them also keeps openssl.so on its own copy + # when libssl.so.3 loads first. rbconfig keeps the flag for native gems like puma. + ldflags << "-Wl,--exclude-libs,libssl.a:libcrypto.a" end extra_cflags = [] extra_cflags << "-mno-outline-atomics" if linux_arm64? @@ -983,6 +988,7 @@ def test_installation! check_no_homebrew_paths!(test_root, ruby, env) check_linkage!(test_root) if linux? check_abi!(test_root) if linux? + check_openssl_hidden!(test_root) if linux? end # Nothing in the tree may need a shared library that isn't part of glibc. This is the @@ -1057,6 +1063,14 @@ def check_abi!(root) end end + # See ruby_build_env: an exported OpenSSL symbol lets a system libpq bind to it. + def check_openssl_hidden!(root) + exporters = Dir.glob(File.join(root, "**", "*.so")).select do |path| + capture("nm", "-D", "--defined-only", path, allow_failure: true).match?(/ (SSL_new|CRYPTO_malloc)$/) + end + raise PackageError, "OpenSSL symbols exported by:\n #{exporters.join("\n ")}" unless exporters.empty? + end + def package! run "chmod", "-R", "u+w", @install_prefix platform = @target_recipe.fetch("artifact_platform")