From: Vsevolod Stakhov Date: Mon, 3 Nov 2025 11:05:31 +0000 (+0000) Subject: [Fix] Use runtime Hyperscan version instead of compile-time version for database... X-Git-Tag: 3.14.0~22^2~1 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=6e49e901ad3da152afb3f8d591cf10486edf2534;p=thirdparty%2Frspamd.git [Fix] Use runtime Hyperscan version instead of compile-time version for database validation The issue was that the database version check used HS_DB_VERSION macro defined in headers at compile time, while Hyperscan .so library writes the version from its own headers. When the system updates the Hyperscan package but Rspamd isn't recompiled, this causes a version mismatch and database validation fails. The fix calls hs_version() at runtime to get the actual library version and uses that for validation instead. This ensures compatibility when the Hyperscan library is updated independently. --- diff --git a/src/libserver/hyperscan_tools.cxx b/src/libserver/hyperscan_tools.cxx index 75863bf39b..0217610476 100644 --- a/src/libserver/hyperscan_tools.cxx +++ b/src/libserver/hyperscan_tools.cxx @@ -334,6 +334,42 @@ struct real_hs_db { std::uint64_t platform; std::uint32_t crc32; }; + +/** + * Get the runtime version of Hyperscan library in 32-bit format + * This is important because the .so version might differ from headers version + */ +static auto +hs_get_runtime_db_version() -> std::uint32_t +{ + static std::uint32_t cached_version = 0; + + if (cached_version == 0) { + const char *version_str = hs_version(); + unsigned int major = 0, minor = 0, patch = 0; + + // Parse version string like "5.4.0" + if (sscanf(version_str, "%u.%u.%u", &major, &minor, &patch) == 3) { + // Format: (major << 24) | (minor << 16) | (patch << 8) | 0 + cached_version = (major << 24) | (minor << 16) | (patch << 8); + msg_debug_hyperscan("detected hyperscan runtime version: %s (0x%08x)", + version_str, cached_version); + } + else { + msg_err_hyperscan("cannot parse hyperscan version string: %s", version_str); + // Fallback to compile-time version if available +#ifdef HS_DB_VERSION + cached_version = HS_DB_VERSION; + msg_debug_hyperscan("using compile-time hyperscan version: 0x%08x", cached_version); +#else + cached_version = 0; +#endif + } + } + + return cached_version; +} + static auto hs_is_valid_database(void *raw, std::size_t len, std::string_view fname) -> tl::expected { @@ -350,12 +386,12 @@ hs_is_valid_database(void *raw, std::size_t len, std::string_view fname) -> tl:: fname, test.magic, HS_DB_MAGIC)); } -#ifdef HS_DB_VERSION - if (test.version != HS_DB_VERSION) { - return tl::make_unexpected(fmt::format("cannot load hyperscan database from {}: invalid version: {} ({} expected)", - fname, test.version, HS_DB_VERSION)); + // Use runtime version instead of compile-time version + auto runtime_version = hs_get_runtime_db_version(); + if (runtime_version != 0 && test.version != runtime_version) { + return tl::make_unexpected(fmt::format("cannot load hyperscan database from {}: invalid version: 0x{:08x} (0x{:08x} expected)", + fname, test.version, runtime_version)); } -#endif return true; }