A Sieve script's CPU limit counted instructions only, and one header, address or body test can read a whole message once for every key it lists. A user's script with a few thousand keys in one body test, tested against a large message anyone can send, spent seconds of CPU on each delivery while staying far below the 5000-instruction limit; more such tests multiplied it. Tests now charge what they scan: each value compared times the number of keys, and the header block each time a header is looked up. Every 64 KiB scanned counts as one instruction. A test stops as soon as the run is over the limit, and the script fails with the same CPU limit error as for too many instructions (a deleteheader cut short deletes nothing). Under 64 KiB scanned costs nothing extra, so a usual script on a usual message runs as before; a user script's default of 5000 allows about 300 MiB scanned, a system script's 1,048,576 about 64 GiB. Test: on main one body test with 100 keys over a 4 MiB message ran in 3 instructions and a 100-pass loop over it in 503 (8,000 keys over 20 MiB: 14.2 s); both now stop with CPULimitReached (the 20 MiB case in 10 ms), and a script on a 16 KiB message fits the same limit as before.
3.5 KiB
sieve-rs, vendored
The published sieve-rs 0.7.3, taken
from crates.io under AGPL-3.0-only, with the upstream project's name taken
out of the identifiers it spells into the Sieve language (docs/spec/SPEC.md
§2.4). The root Cargo.toml patches it in with [patch.crates-io].
Changes
Every changed file carries the AGPL 5(a) notice in its header.
src/compiler/grammar/mod.rs: the extensions scriptsrequireand ManageSieve advertises arevnd.inbuxa.whileandvnd.inbuxa.expressions. The pre-renamevnd.stalwart.*spellings are still accepted, never advertised, so a script written for upstream compiles when it's edited. Without them, the name is treated as an unknown extension andletorevalfails with "Undeclared capability". Thelegacy_extension_namestests cover both.src/runtime/mod.rs: the defaultenvironment "name"isinbuxa Sieve(the server sets its own name on both of its runtimes anyway), and the test-only capability isvnd.inbuxa.testsuite.src/lib.rs: the test-suite environment variables arevnd.inbuxa.*.src/runtime/tests/comparator.rs,src/compiler/grammar/mod.rs: every:regexpattern is built throughbuild_regex, which holds the compiled matcher and its lazy DFA to 1 MiB each (fancy-regex's default is 10 MiB), and a pattern built from variables is compiled once per run (RegexCache, 32 patterns, a field ofContextinsrc/lib.rsandsrc/runtime/context.rs) instead of once for every value it is tested against. Every caller ofComparator::regexpasses the cache. Thesieve_regexes_are_size_limitedandsieve_variable_patterns_compile_oncetests incrates/commoncover both.src/runtime/context.rs,src/lib.rs,src/runtime/tests/test_header.rs,test_address.rs,test_body.rs,test_string.rs,test_exists.rs,src/runtime/actions/action_editheader.rs: the bytes that tests scan count against the cpu limit, one instruction for every 64 KiB (SCAN_BYTES_PER_INSTRUCTION). Header, address, body and string tests anddeleteheadercharge each value they compare times the number of keys, and every search for a header charges the part's header block; the total isContext::scanned_bytes. A test stops scanning once the run is over the limit, and the run fails withCPULimitReachedas it does for too many instructions (deleteheaderthen deletes nothing). Thescan_limit_testsincontext.rscover it; they run from this directory withcargo test --offline --target-dir ../../target/sieve-rs-tests scan_limit(the crate'stest_suiteandparse_rfcneed upstream'stests/, which isn't kept).Cargo.toml: the example target is dropped, with the top-levelexamples/andtests/, which the server never builds. (src/keeps its owntestsmodules: the crate declares them.)
Updating
When the server's Cargo.lock moves sieve-rs to a new version, Cargo stops
using this copy and only warns that the patch went unused. The unit test
sieve_extensions_carry_the_fork_name in crates/common fails when that
happens. Re-vendor the new version from ~/.cargo/registry/src/*/sieve-rs-*,
dropping the top-level tests/ and examples/ (only those: rsync --exclude /tests --exclude /examples), Cargo.lock and Cargo.toml.orig, repeat
the changes above, and update the version here and in the root Cargo.toml
comment. tools/fork/name-check.py covers vendor/ too, so a rename missed
in a new version fails CI.