]> git.ipfire.org Git - thirdparty/rspamd.git/commitdiff
[Fix] Use runtime Hyperscan version instead of compile-time version for database...
authorVsevolod Stakhov <vsevolod@rspamd.com>
Mon, 3 Nov 2025 11:05:31 +0000 (11:05 +0000)
committerVsevolod Stakhov <vsevolod@rspamd.com>
Mon, 3 Nov 2025 11:05:31 +0000 (11:05 +0000)
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.

src/libserver/hyperscan_tools.cxx

index 75863bf39b8ec99efc8bcbabb9d7ec9e6c7a8de8..02176104763fc517271c9be3f976daa631c2a609 100644 (file)
@@ -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<bool, std::string>
 {
@@ -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;
 }