Files
jcoffey-dev 0e532cddca Calendars: give each recurrence expansion one work budget
A calendar's recurrence rules were each allowed 100,000 passes that find
nothing before calcard gave up on them, counted afresh for every rule and
after every instance found, and nothing bounded the total. Anyone who can
put an object on a calendar (a CalDAV or JMAP upload, or an iMIP invitation
the server processes) could send a few thousand components whose rules never
match, such as FREQ=MONTHLY;BYMONTH=2;BYMONTHDAY=31, or rules that match
rarely, and storing or processing that object cost tens of seconds
of CPU. The per-period candidate check doesn't catch these: each period
is small, there are just very many of them.

Expansion now has one budget of 1,000,000 work units for all the rules of
the object together (about 50 ms in a release build). Every pass over a
period costs a unit, a pass that finds nothing a unit per 16 days it
scanned, candidates looked at but not produced a unit each, and so does
each time skipped while looking for the next BYHOUR/BYMINUTE/BYSECOND
match. When it runs out, expansion stops the way it does at the instance
limit: the object is kept and the instances found so far are indexed. The
expansion also reports it, so the server logs a calendar rule expansion
error naming it. Rules are no longer started once the instance limit is
reached. Ordinary rules use a small part of the budget: 3,000 daily,
weekly, monthly, yearly or every-leap-day instances expand as before.

Test: in calcard's new budget tests, 40 rules that never match took
3,999,960 iterations on the vendored copy and a sparse daily rule
2,922,306 (both over the 1,000,000 asserted); both now stop within the
budget and report it. 4,000 never-matching components (412 KB) took
13.7 s (daily), 31.7 s (yearly) and 41.8 s (monthly) in a release build,
now 36 ms, 45 ms and 57 ms.
2026-10-29 06:09:09 -07:00
..