#include "sieve-match-types.h"
#include "sieve-comparators.h"
+#include "sieve-interpreter.h"
#include "sieve-match.h"
#include <string.h>
/* FIXME: Naive substring match implementation. Should switch to more
* efficient algorithm if large values need to be searched (e.g. message body).
+ *
+ * The inner loop polls the interpreter CPU time limit periodically so that a
+ * single O(N*M) match on a large value cannot run for many times the
+ * configured sieve_max_cpu_time (which is otherwise only checked between
+ * bytecode operations).
*/
+#define SIEVE_CONTAINS_CPU_CHECK_INTERVAL 4096
+
static int mcht_contains_match_key
(struct sieve_match_context *mctx, const char *val, size_t val_size,
const char *key, size_t key_size)
const char *kend = (const char *) key + key_size;
const char *vp = val;
const char *kp = key;
+ unsigned int counter = 0;
if ( val_size == 0 )
return ( key_size == 0 ? 1 : 0 );
while ( (vp < vend) && (kp < kend) ) {
if ( !cmp->def->char_match(cmp, &vp, vend, &kp, kend) )
vp++;
+
+ if ( ++counter >= SIEVE_CONTAINS_CPU_CHECK_INTERVAL ) {
+ counter = 0;
+ if ( sieve_runtime_cpu_limit_exceeded(mctx->runenv) ) {
+ sieve_runtime_error(
+ mctx->runenv, NULL,
+ "execution exceeded CPU time limit");
+ mctx->exec_status =
+ SIEVE_EXEC_RESOURCE_LIMIT;
+ return -1;
+ }
+ }
}
return ( kp == kend ? 1 : 0 );
#include "sieve-match-types.h"
#include "sieve-comparators.h"
+#include "sieve-interpreter.h"
#include "sieve-match.h"
#include <string.h>
#endif
/* FIXME: Naive implementation, substitute this with dovecot src/lib/str-find.c
+ *
+ * The inner loop polls the interpreter CPU time limit periodically so that a
+ * single O(N*M) search on a large value cannot run for many times the
+ * configured sieve_max_cpu_time. Returns 1 on match, 0 on exhaustion, or -1
+ * when the CPU time limit was exceeded (mctx->exec_status is set).
*/
-static inline bool _string_find(const struct sieve_comparator *cmp,
- const char **valp, const char *vend, const char **keyp, const char *kend)
+#define SIEVE_MATCHES_CPU_CHECK_INTERVAL 4096
+
+static int
+_string_find(struct sieve_match_context *mctx,
+ const struct sieve_comparator *cmp,
+ const char **valp, const char *vend,
+ const char **keyp, const char *kend,
+ unsigned int *counter)
{
while ( (*valp < vend) && (*keyp < kend) ) {
if ( !cmp->def->char_match(cmp, valp, vend, keyp, kend) )
(*valp)++;
+ if (++(*counter) >= SIEVE_MATCHES_CPU_CHECK_INTERVAL) {
+ *counter = 0;
+ if (sieve_runtime_cpu_limit_exceeded(mctx->runenv)) {
+ sieve_runtime_error(
+ mctx->runenv, NULL,
+ "execution exceeded CPU time limit");
+ mctx->exec_status =
+ SIEVE_EXEC_RESOURCE_LIMIT;
+ return -1;
+ }
+ }
}
- return (*keyp == kend);
+ return (*keyp == kend ? 1 : 0);
}
static char _scan_key_section
char wcard = '\0'; /* Current wildcard */
char next_wcard = '\0'; /* Next widlcard */
unsigned int key_offset = 0;
+ unsigned int counter = 0;
if ( cmp->def == NULL || cmp->def->char_match == NULL )
return 0;
while (kp < kend && vp < vend ) {
const char *needle, *nend;
+ if (++counter >= SIEVE_MATCHES_CPU_CHECK_INTERVAL) {
+ counter = 0;
+ if (sieve_runtime_cpu_limit_exceeded(mctx->runenv)) {
+ sieve_runtime_error(
+ mctx->runenv, NULL,
+ "execution exceeded CPU time limit");
+ mctx->exec_status =
+ SIEVE_EXEC_RESOURCE_LIMIT;
+ sieve_match_values_abort(&mvalues);
+ return -1;
+ }
+ }
+
if ( !backtrack ) {
/* Search the next '*' wildcard in the key string */
/* Match may happen at any offset (>= key offset): find substring */
vp += key_offset;
- if ( (vp >= vend) || !_string_find(cmp, &vp, vend, &needle, nend) ) {
+ if (vp >= vend) {
debug_printf(" failed to find needle at an offset\n");
break;
}
+ int fres = _string_find(mctx, cmp, &vp, vend,
+ &needle, nend, &counter);
+ if (fres < 0) {
+ sieve_match_values_abort(&mvalues);
+ return -1;
+ }
+ if (fres == 0) {
+ debug_printf(" failed to find needle at an offset\n");
+ break;
+ }
prv = vp - str_len(section);
prk = kp;
struct sieve_runtime_trace trace;
struct sieve_resource_usage rusage;
+ /* CPU time limit for the current sieve_interpreter_continue() call;
+ NULL when no limit is configured or not currently executing. Exposed
+ via sieve_runtime_cpu_limit_exceeded() so long-running runtime code
+ can enforce the limit without waiting for the next bytecode
+ boundary. */
+ struct cpu_limit *climit;
+
/* Current operation */
struct sieve_operation oprtn;
return interp->runenv.exec_env->svinst;
}
+bool sieve_runtime_cpu_limit_exceeded(const struct sieve_runtime_env *renv)
+{
+ struct sieve_interpreter *interp = renv->interp;
+
+ if (interp->climit == NULL)
+ return FALSE;
+ return cpu_limit_exceeded(interp->climit);
+}
+
/* Do not use this function for normal sieve extensions. This is intended for
* the testsuite only.
*/
climit = cpu_limit_init(svinst->set->max_cpu_time,
CPU_LIMIT_TYPE_USER);
}
+ interp->climit = climit;
while (ret == SIEVE_EXEC_OK && !interp->interrupted &&
*address < sieve_binary_block_get_size(renv->sblock)) {
ret = sieve_interpreter_operation_execute(interp);
}
+ interp->climit = NULL;
+
if (climit != NULL) {
sieve_resource_usage_init(&rusage);
rusage.cpu_time_msecs =