From 769a2c08ddb86066097c21f8ce53bf57b007a6cf Mon Sep 17 00:00:00 2001 From: Vadim Ushakov Date: Wed, 20 Oct 2021 17:25:47 +0700 Subject: [PATCH] Fix possible infinite growing of the `SafeRe` regexp cache by limiting the cache size --- ZNE-ChangeLog/ChangeLog-0.8.0.md | 1 + src/util/SafeRe.py | 42 +++++++++++++++++++++++++++----- 2 files changed, 37 insertions(+), 6 deletions(-) diff --git a/ZNE-ChangeLog/ChangeLog-0.8.0.md b/ZNE-ChangeLog/ChangeLog-0.8.0.md index f662d353..9990c683 100644 --- a/ZNE-ChangeLog/ChangeLog-0.8.0.md +++ b/ZNE-ChangeLog/ChangeLog-0.8.0.md @@ -25,6 +25,7 @@ * Implemented the log level overriding for separate modules for easier debugging. * Make the site block check implemented in `ContentFilter` usable from plugins and core modules via `SiteManager.isAddressBlocked()`. +* Fixed possible infinite growing of the `SafeRe` regexp cache by limiting the cache size. ## Docker Image diff --git a/src/util/SafeRe.py b/src/util/SafeRe.py index 6018e2d3..2d519a79 100644 --- a/src/util/SafeRe.py +++ b/src/util/SafeRe.py @@ -1,10 +1,16 @@ import re +import logging + +log = logging.getLogger("SafeRe") + class UnsafePatternError(Exception): pass +max_cache_size = 1000 cached_patterns = {} +old_cached_patterns = {} def isSafePattern(pattern): @@ -22,11 +28,35 @@ def isSafePattern(pattern): return True -def match(pattern, *args, **kwargs): +def compilePattern(pattern): + global cached_patterns + global old_cached_patterns + cached_pattern = cached_patterns.get(pattern) if cached_pattern: - return cached_pattern.match(*args, **kwargs) - else: - if isSafePattern(pattern): - cached_patterns[pattern] = re.compile(pattern) - return cached_patterns[pattern].match(*args, **kwargs) + return cached_pattern + + cached_pattern = old_cached_patterns.get(pattern) + if cached_pattern: + del old_cached_patterns[pattern] + cached_patterns[pattern] = cached_pattern + return cached_pattern + + if isSafePattern(pattern): + cached_pattern = re.compile(pattern) + cached_patterns[pattern] = cached_pattern + log.debug("Compiled new pattern: %s" % pattern) + log.debug("Cache size: %d + %d" % (len(cached_patterns), len(old_cached_patterns))) + + if len(cached_patterns) > max_cache_size: + old_cached_patterns = cached_patterns + cached_patterns = {} + log.debug("Size limit reached. Rotating cache.") + log.debug("Cache size: %d + %d" % (len(cached_patterns), len(old_cached_patterns))) + + return cached_pattern + + +def match(pattern, *args, **kwargs): + cached_pattern = compilePattern(pattern) + return cached_pattern.match(*args, **kwargs)