From de42879925e6e210f21114e48e7c6dfdbce8fb37 Mon Sep 17 00:00:00 2001 From: Vsevolod Stakhov Date: Thu, 18 Feb 2016 09:57:48 +0000 Subject: [PATCH] Add more sanity checks when loading hyperscan cache --- src/hs_helper.c | 2 +- src/libserver/re_cache.c | 58 ++++++++++++++++++++++++++++++++++------ src/libserver/re_cache.h | 2 +- 3 files changed, 52 insertions(+), 10 deletions(-) diff --git a/src/hs_helper.c b/src/hs_helper.c index a34d1d885..7e8f7f1c5 100644 --- a/src/hs_helper.c +++ b/src/hs_helper.c @@ -135,7 +135,7 @@ rspamd_hs_helper_cleanup_dir (struct hs_helper_ctx *ctx, gboolean forced) for (i = 0; i < globbuf.gl_pathc; i++) { if (forced || !rspamd_re_cache_is_valid_hyperscan_file (ctx->cfg->re_cache, - globbuf.gl_pathv[i], TRUE)) { + globbuf.gl_pathv[i], TRUE, TRUE)) { if (unlink (globbuf.gl_pathv[i]) == -1) { msg_err ("cannot unlink %s: %s", globbuf.gl_pathv[i], strerror (errno)); diff --git a/src/libserver/re_cache.c b/src/libserver/re_cache.c index a25f0d1e5..8ab0cd031 100644 --- a/src/libserver/re_cache.c +++ b/src/libserver/re_cache.c @@ -1071,7 +1071,7 @@ rspamd_re_cache_compile_hyperscan (struct rspamd_re_cache *cache, rspamd_snprintf (path, sizeof (path), "%s%c%s.hs", cache_dir, G_DIR_SEPARATOR, re_class->hash); - if (rspamd_re_cache_is_valid_hyperscan_file (cache, path, TRUE)) { + if (rspamd_re_cache_is_valid_hyperscan_file (cache, path, TRUE, TRUE)) { fd = open (path, O_RDONLY, 00600); @@ -1302,7 +1302,7 @@ rspamd_re_cache_compile_hyperscan (struct rspamd_re_cache *cache, gboolean rspamd_re_cache_is_valid_hyperscan_file (struct rspamd_re_cache *cache, - const char *path, gboolean silent) + const char *path, gboolean silent, gboolean try_load) { g_assert (cache != NULL); g_assert (path != NULL); @@ -1310,7 +1310,7 @@ rspamd_re_cache_is_valid_hyperscan_file (struct rspamd_re_cache *cache, #ifndef WITH_HYPERSCAN return FALSE; #else - gint fd; + gint fd, n, ret; guchar magicbuf[RSPAMD_HS_MAGIC_LEN]; GHashTableIter it; gpointer k, v; @@ -1318,6 +1318,8 @@ rspamd_re_cache_is_valid_hyperscan_file (struct rspamd_re_cache *cache, gsize len; const gchar *hash_pos; hs_platform_info_t test_plt; + hs_database_t *test_db = NULL; + guchar *map, *p, *end; len = strlen (path); @@ -1380,9 +1382,49 @@ rspamd_re_cache_is_valid_hyperscan_file (struct rspamd_re_cache *cache, return FALSE; } - /* XXX: add crc check */ close (fd); + if (try_load) { + map = rspamd_file_xmap (path, PROT_READ, &len); + + if (map == NULL) { + msg_err_re_cache ("cannot mmap hyperscan cache file %s: " + "%s", + path, strerror (errno)); + return FALSE; + } + + p = map + RSPAMD_HS_MAGIC_LEN + sizeof (test_plt); + end = map + len; + n = *(gint *)p; + p += sizeof (gint); + + if (n <= 0 || 2 * n * sizeof (gint) + /* IDs + flags */ + sizeof (guint64) + /* crc */ + RSPAMD_HS_MAGIC_LEN + /* header */ + sizeof (cache->plt) > len) { + /* Some wrong amount of regexps */ + msg_err_re_cache ("bad number of expressions in %s: %d", + path, n); + munmap (map, len); + return FALSE; + } + + p += n * sizeof (gint) * 2 + sizeof (guint64); + + if ((ret = hs_deserialize_database (p, end - p, &test_db)) + != HS_SUCCESS) { + msg_err_re_cache ("bad hs database in %s: %d", path, ret); + munmap (map, len); + + return FALSE; + } + + hs_free_database (test_db); + munmap (map, len); + } + /* XXX: add crc check */ + return TRUE; } } @@ -1407,7 +1449,7 @@ rspamd_re_cache_load_hyperscan (struct rspamd_re_cache *cache, return FALSE; #else gchar path[PATH_MAX]; - gint fd, i, n, *hs_ids = NULL, *hs_flags = NULL, total = 0; + gint fd, i, n, *hs_ids = NULL, *hs_flags = NULL, total = 0, ret; GHashTableIter it; gpointer k, v; guint8 *map, *p, *end; @@ -1422,7 +1464,7 @@ rspamd_re_cache_load_hyperscan (struct rspamd_re_cache *cache, rspamd_snprintf (path, sizeof (path), "%s%c%s.hs", cache_dir, G_DIR_SEPARATOR, re_class->hash); - if (rspamd_re_cache_is_valid_hyperscan_file (cache, path, FALSE)) { + if (rspamd_re_cache_is_valid_hyperscan_file (cache, path, FALSE, FALSE)) { msg_debug_re_cache ("load hyperscan database from '%s'", re_class->hash); @@ -1484,9 +1526,9 @@ rspamd_re_cache_load_hyperscan (struct rspamd_re_cache *cache, re_class->hs_scratch = NULL; re_class->hs_db = NULL; - if (hs_deserialize_database (p, end - p, &re_class->hs_db) + if ((ret = hs_deserialize_database (p, end - p, &re_class->hs_db)) != HS_SUCCESS) { - msg_err_re_cache ("bad hs database in %s", path); + msg_err_re_cache ("bad hs database in %s: %d", path, ret); munmap (map, st.st_size); g_free (hs_ids); g_free (hs_flags); diff --git a/src/libserver/re_cache.h b/src/libserver/re_cache.h index 5cb10ea63..6c7999ba1 100644 --- a/src/libserver/re_cache.h +++ b/src/libserver/re_cache.h @@ -152,7 +152,7 @@ gint rspamd_re_cache_compile_hyperscan (struct rspamd_re_cache *cache, * Returns TRUE if the specified file is valid hyperscan cache */ gboolean rspamd_re_cache_is_valid_hyperscan_file (struct rspamd_re_cache *cache, - const char *path, gboolean silent); + const char *path, gboolean silent, gboolean try_load); /** * Loads all hyperscan regexps precompiled -- 2.39.5