From 41d6bb5f736459d31bc3207c7ab77f720aaaed63 Mon Sep 17 00:00:00 2001 From: Ayaz Salikhov Date: Wed, 5 Aug 2026 00:27:23 +0100 Subject: [PATCH] build: Fix build on macOS 15 and Nix environment (#7953) --- BUILD.md | 2 ++ CMakeLists.txt | 19 +++++++++++++++++++ conan.lock | 3 ++- conan/profiles/default | 20 ++++++++++++++++++++ conanfile.py | 2 ++ sanitizers/suppressions/ubsan.supp | 4 ++++ src/libxrpl/json/json_reader.cpp | 12 ++++++++++-- 7 files changed, 59 insertions(+), 3 deletions(-) diff --git a/BUILD.md b/BUILD.md index a15c94edc9..edc52fe3b7 100644 --- a/BUILD.md +++ b/BUILD.md @@ -42,6 +42,8 @@ Our Linux CI tooling is distro-independent and uses a Nix-based environment, so ### macOS Many `xrpld` engineers use macOS for development. +The minimum supported version is macOS 15 (Sequoia). +CI testing is done in macOS 26 (Tahoe), but the build defaults `CMAKE_OSX_DEPLOYMENT_TARGET` to 15. ### Windows diff --git a/CMakeLists.txt b/CMakeLists.txt index f2e8fb3ae5..b7e1c0cad0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,6 +13,23 @@ if(DEFINED CMAKE_MODULE_PATH) endif() list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") +# Must be set before project() because project() consumes it when configuring the compiler and SDK. +# A user-provided -DCMAKE_OSX_DEPLOYMENT_TARGET still takes precedence. +# +# CMAKE_SYSTEM_NAME can't be used before project(), so CMAKE_HOST_SYSTEM_NAME is used instead. +# +# When CMAKE_OSX_DEPLOYMENT_TARGET is bumped to >=26.0, FastFloat dependency won't be needed anymore +if( + CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin" + AND NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET +) + set(CMAKE_OSX_DEPLOYMENT_TARGET + "15.0" + CACHE STRING + "Minimum macOS deployment version" + ) +endif() + project(xrpl) set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_CXX_STANDARD 23) @@ -87,6 +104,7 @@ include(deps/Boost) add_subdirectory(external/antithesis-sdk) find_package(date REQUIRED) find_package(ed25519 REQUIRED) +find_package(FastFloat REQUIRED) find_package(gRPC REQUIRED) find_package(LibArchive REQUIRED) find_package(lz4 REQUIRED) @@ -102,6 +120,7 @@ target_link_libraries( xrpl_libs INTERFACE ed25519::ed25519 + FastFloat::fast_float lz4::lz4 mpt-crypto::mpt-crypto OpenSSL::Crypto diff --git a/conan.lock b/conan.lock index c6a4070c77..0e1461ba9c 100644 --- a/conan.lock +++ b/conan.lock @@ -20,6 +20,7 @@ "jemalloc/5.3.1#1fc58d55316041f10fbc1e8a2eae632a%1776700028.228", "gtest/1.17.0#5224b3b3ff3b4ce1133cbdd27d53ee7d%1782392402.791979", "grpc/1.81.1#f729f6d75992d20f9c72828e9142d62f%1783945160.094135", + "fast_float/8.2.10#f6f28d6bb22112078e7dbda611caf681%1782494504.298", "ed25519/2015.03#ae761bdc52730a843f0809bdf6c1b1f6%1782307148.15562", "date/3.0.4#862e11e80030356b53c2c38599ceb32b%1782392402.538492", "c-ares/1.34.6#545240bb1c40e2cacd4362d6b8967650%1782392402.681654", @@ -34,7 +35,7 @@ "protobuf/6.33.5#ff253ead763bd8d9904a52979cd21e81%1782392410.233933", "nasm/2.16.01#31e26f2ee3c4346ecd347911bd126904%1782395690.33162", "msys2/cci.latest#d22fe7b2808f5fd34d0a7923ace9c54f%1770657326.649", - "m4/1.4.19#34c4bbc3eeebe98ca6edf2f52d602e7d%1777282960.259", + "m4/1.4.19#1727f439cf74e83826ec96d0b4904eee%1784541921.659", "cmake/4.3.3#840cf00ea09777e05c2050a50a82c722%1782392418.696091", "b2/5.4.2#ffd6084a119587e70f11cd45d1a386e2%1782392402.624226", "automake/1.16.5#b91b7c384c3deaa9d535be02da14d04f%1755524470.56", diff --git a/conan/profiles/default b/conan/profiles/default index 6534f8092b..f2d93213ac 100644 --- a/conan/profiles/default +++ b/conan/profiles/default @@ -5,6 +5,13 @@ {% if os == "Linux" %} {% set compiler_version = detect_api.default_compiler_version(compiler, version) %} {% endif %} +{% if os == "Macos" %} +{# Minimum macOS the dependencies target. #} +{# Without this, Conan builds each dependency against the (possibly newer) host SDK, so the #} +{# dependency objects target a newer macOS than the binary and the linker warns. #} +{# Keep at or below CMAKE_OSX_DEPLOYMENT_TARGET in CMakeLists.txt. #} +{% set min_macos_version = "15.0" %} +{% endif %} [settings] os={{ os }} @@ -18,6 +25,9 @@ compiler.runtime=static {% else %} compiler.libcxx={{ detect_api.detect_libcxx(compiler, version, compiler_exe) }} {% endif %} +{% if os == "Macos" %} +os.version={{ min_macos_version }} +{% endif %} [conf] {# The Boost recipe builds with b2, which doesn't use Conan's toolchain files. #} @@ -41,3 +51,13 @@ tools.build:compiler_executables={'c':'{{ cc_exe }}','cpp':'{{ cxx_exe }}'} {# More info: https://docs.conan.io/2/reference/extensions/binary_compatibility.html #} user.package:cppstd_version=23 tools.info.package_id:confs+=["user.package:cppstd_version"] + +{% if os == "Macos" %} +[buildenv] +{# os.version adds -mmacosx-version-min to compiler command lines, #} +{# but Boost.Context's b2 assembly (.S) rule ignores it, #} +{# so those objects keep the host SDK version and still warn at link time. #} +{# clang's assembler honors this env var regardless, pinning them. #} +{# Scoped to boost/* since it is the only gap. #} +boost/*:MACOSX_DEPLOYMENT_TARGET={{ min_macos_version }} +{% endif %} diff --git a/conanfile.py b/conanfile.py index f883761f0e..b9fa513d67 100644 --- a/conanfile.py +++ b/conanfile.py @@ -29,6 +29,7 @@ class Xrpl(ConanFile): requires = [ "ed25519/2015.03", + "fast_float/8.2.10", "grpc/1.81.1", "libarchive/3.8.7", "nudb/2.0.9", @@ -211,6 +212,7 @@ class Xrpl(ConanFile): "boost::thread", "date::date", "ed25519::ed25519", + "fast_float::fast_float", "grpc::grpc++", "libarchive::libarchive", "lz4::lz4", diff --git a/sanitizers/suppressions/ubsan.supp b/sanitizers/suppressions/ubsan.supp index cb93a617aa..56f2c77204 100644 --- a/sanitizers/suppressions/ubsan.supp +++ b/sanitizers/suppressions/ubsan.supp @@ -102,6 +102,10 @@ undefined:nudb # Snappy compression library intentional overflows unsigned-integer-overflow:snappy.cc +# fast_float parses floats with a SWAR trick (parse_eight_digits_unrolled) that +# multiplies eight packed digits modulo 2^64; the wraparound is by design. +unsigned-integer-overflow:fast_float + # Abseil intentional overflows in hashing, RNG and time arithmetic. # Matched at library scope (like boost above): the wraparound is by design # across many absl files (hash mixing, raw_hash_set probing, duration math, diff --git a/src/libxrpl/json/json_reader.cpp b/src/libxrpl/json/json_reader.cpp index f9134e6629..8598f94491 100644 --- a/src/libxrpl/json/json_reader.cpp +++ b/src/libxrpl/json/json_reader.cpp @@ -3,9 +3,11 @@ #include #include +#include // IWYU pragma: keep +#include + #include #include -#include #include #include #include @@ -605,8 +607,14 @@ Reader::decodeNumber(Token& token) bool Reader::decodeDouble(Token& token) { + // Sanity check to avoid buffer overflow exploits. + if (token.end < token.start) + { + return addError("Unable to parse token length", token); + } + double value = 0; - auto const [ptr, ec] = std::from_chars(token.start, token.end, value); + auto const [ptr, ec] = fast_float::from_chars(token.start, token.end, value); // Reject anything from_chars could not turn into a finite double: // - ec != std::errc{}: no valid conversion, or an out-of-range magnitude