From 77d56fd5def6f0138ca18a1b309bb59a18f6ccd6 Mon Sep 17 00:00:00 2001
From: Stephan Bosch <stephan.bosch@open-xchange.com>
Date: Thu, 3 Dec 2020 03:36:06 +0100
Subject: [PATCH] lib-sieve: sieve - Add API for resource usage manipulation
 and evaluation.

---
 src/lib-sieve/sieve-common.h   |  3 +--
 src/lib-sieve/sieve-limits.h   |  1 +
 src/lib-sieve/sieve-settings.c |  4 ++--
 src/lib-sieve/sieve-types.h    | 10 ++++++++
 src/lib-sieve/sieve.c          | 42 ++++++++++++++++++++++++++++++++++
 src/lib-sieve/sieve.h          | 27 ++++++++++++++++++++++
 6 files changed, 83 insertions(+), 4 deletions(-)

diff --git a/src/lib-sieve/sieve-common.h b/src/lib-sieve/sieve-common.h
index ea1f420f7..b2a218328 100644
--- a/src/lib-sieve/sieve-common.h
+++ b/src/lib-sieve/sieve-common.h
@@ -3,8 +3,7 @@
 
 #include "lib.h"
 
-#include "sieve-config.h"
-#include "sieve-types.h"
+#include "sieve.h"
 
 #include <sys/types.h>
 
diff --git a/src/lib-sieve/sieve-limits.h b/src/lib-sieve/sieve-limits.h
index 8e60b448e..1a767b295 100644
--- a/src/lib-sieve/sieve-limits.h
+++ b/src/lib-sieve/sieve-limits.h
@@ -31,6 +31,7 @@
  */
 
 #define SIEVE_MAX_MATCH_VALUES                          32
+#define SIEVE_HIGH_CPU_TIME_MSECS                       1500
 #define SIEVE_DEFAULT_MAX_CPU_TIME_SECS                 30
 
 /*
diff --git a/src/lib-sieve/sieve-settings.c b/src/lib-sieve/sieve-settings.c
index b9b60f5d4..ead6029fb 100644
--- a/src/lib-sieve/sieve-settings.c
+++ b/src/lib-sieve/sieve-settings.c
@@ -219,8 +219,8 @@ void sieve_settings_load(struct sieve_instance *svinst)
 	svinst->max_cpu_time_secs = SIEVE_DEFAULT_MAX_CPU_TIME_SECS;
 	if (sieve_setting_get_duration_value(svinst, "sieve_max_cpu_time",
 					     &period)) {
-		if (period > UINT_MAX)
-			svinst->max_cpu_time_secs = UINT_MAX;
+		if (period > (UINT_MAX / 1000))
+			svinst->max_cpu_time_secs = (UINT_MAX / 1000);
 		else
 			svinst->max_cpu_time_secs = (unsigned int)period;
 	}
diff --git a/src/lib-sieve/sieve-types.h b/src/lib-sieve/sieve-types.h
index df8928c9a..e90f6da6e 100644
--- a/src/lib-sieve/sieve-types.h
+++ b/src/lib-sieve/sieve-types.h
@@ -256,6 +256,16 @@ struct sieve_script_env {
 #define SIEVE_SCRIPT_DEFAULT_MAILBOX(senv) \
 	(senv->default_mailbox == NULL ? "INBOX" : senv->default_mailbox )
 
+/*
+ * Resource usage
+ */
+
+struct sieve_resource_usage {
+	/* The total amount of system + user CPU time consumed while executing
+	   the Sieve script. */
+	unsigned int cpu_time_msecs;
+};
+
 /*
  * Script execution status
  */
diff --git a/src/lib-sieve/sieve.c b/src/lib-sieve/sieve.c
index db1c246ab..b93ad560e 100644
--- a/src/lib-sieve/sieve.c
+++ b/src/lib-sieve/sieve.c
@@ -33,6 +33,7 @@
 
 #include "sieve.h"
 #include "sieve-common.h"
+#include "sieve-limits.h"
 #include "sieve-error-private.h"
 
 #include <sys/types.h>
@@ -1181,3 +1182,44 @@ const char *sieve_get_postmaster_address(const struct sieve_script_env *senv)
 	message_address_write(addr, postmaster);
 	return str_c(addr);
 }
+
+/*
+ * Resource usage
+ */
+
+void sieve_resource_usage_init(struct sieve_resource_usage *rusage_r)
+{
+	i_zero(rusage_r);
+}
+
+void sieve_resource_usage_add(struct sieve_resource_usage *dst,
+			      const struct sieve_resource_usage *src)
+{
+	if ((UINT_MAX - dst->cpu_time_msecs) < src->cpu_time_msecs)
+		dst->cpu_time_msecs = UINT_MAX;
+	else
+		dst->cpu_time_msecs += src->cpu_time_msecs;
+}
+
+bool sieve_resource_usage_is_high(struct sieve_instance *svinst ATTR_UNUSED,
+				  const struct sieve_resource_usage *rusage)
+{
+	return (rusage->cpu_time_msecs > SIEVE_HIGH_CPU_TIME_MSECS);
+}
+
+bool sieve_resource_usage_is_excessive(
+	struct sieve_instance *svinst,
+	const struct sieve_resource_usage *rusage)
+{
+	i_assert(svinst->max_cpu_time_secs <= (UINT_MAX / 1000));
+	return (rusage->cpu_time_msecs > (svinst->max_cpu_time_secs * 1000));
+}
+
+const char *
+sieve_resource_usage_get_summary(const struct sieve_resource_usage *rusage)
+{
+	if (rusage->cpu_time_msecs == 0)
+		return "no usage recorded";
+
+	return t_strdup_printf("cpu time = %u ms", rusage->cpu_time_msecs);
+}
diff --git a/src/lib-sieve/sieve.h b/src/lib-sieve/sieve.h
index 3cbf2771e..16bbffc54 100644
--- a/src/lib-sieve/sieve.h
+++ b/src/lib-sieve/sieve.h
@@ -212,4 +212,31 @@ void sieve_trace_log_free(struct sieve_trace_log **_trace_log);
 int sieve_trace_config_get(struct sieve_instance *svinst,
 			   struct sieve_trace_config *tr_config);
 
+/*
+ * Resource usage
+ */
+
+/* Initialize the resource usage struct, clearing all usage statistics. */
+void sieve_resource_usage_init(struct sieve_resource_usage *rusage_r);
+
+/* Calculate the sum of the provided resource usage statistics, writing the
+   result to the first. */
+void sieve_resource_usage_add(struct sieve_resource_usage *dst,
+			      const struct sieve_resource_usage *src);
+
+/* Returns TRUE if the resource usage is sufficiently high to warrant recording
+   for checking cumulative resource limits (across several different script
+   executions). */
+bool sieve_resource_usage_is_high(struct sieve_instance *svinst,
+				  const struct sieve_resource_usage *rusage);
+/* Returns TRUE when the provided resource usage statistics exceed a configured
+   policy limit. */
+bool sieve_resource_usage_is_excessive(
+	struct sieve_instance *svinst,
+	const struct sieve_resource_usage *rusage);
+/* Returns a string containing a description of the resource usage (to be used
+   log messages). */
+const char *
+sieve_resource_usage_get_summary(const struct sieve_resource_usage *rusage);
+
 #endif
-- 
GitLab