Skip to content

Fail closed when /schedule time math overflows or mktime fails - #358

Merged
tylerreckart merged 7 commits into
mainfrom
fix/schedule-time-math-fail-closed
Sep 21, 2026
Merged

tylerreckart merged 7 commits into
mainfrom
fix/schedule-time-math-fail-closed

Conversation

@cursor

@cursor cursor Bot commented Sep 19, 2026 •

Copy link
Copy Markdown
Contributor

Problem

Two schedule-time helpers could persist next_fire_at values that list_due treats as immediately due (next_fire_at <= now):

  1. every hour / hourly used raw now + 3600 (and after + 3600 on recompute). #318 already overflow-checks in N hours and every N hours via add_seconds; the hourly keyword was missed. A last-fire near INT64_MAX wraps to a negative next fire.
  2. mktime failure returned (time_t)-1 from make_local_epoch. -1 is always <= now, so a recurring next_fire_for_recur recompute would tight-loop LLM runs. Distinct from Reject impossible /schedule calendar dates instead of overflowing the month #349 (impossible calendar dates at parse time).

Recovery (finalize_orphaned_scheduled_task_leases) and the scheduler tick both call next_fire_for_recur on hourly rows.

Fix

  • Hourly parse + next_fire_for_recur use add_seconds(..., 1, 3600) and fail closed (interval too large / return 0).
  • make_local_epoch returns 0 on mktime == (time_t)-1 (same sentinel next_fire_for_recur already documents as "cannot compute").
  • next_local_at / next_local_weekday_at and the at / tomorrow / on / every day / every <weekday> parse paths reject <= 0 instead of storing it.

Ordinary every hour from a wall-clock now is unchanged (now + 3600).

Tests

unit_schedule_parser:

  • next_fire_for_recur({"every":"hour"}, INT64_MAX-100) returns 0.
  • parse_schedule_phrase("every hour", INT64_MAX-100) is not ok.
  • on 0001-01-01 does not persist next_fire_at = -1.
  • Existing hourly / daily / weekly cases still pass.

Suite 8/8 locally (75 assertions) + ASan + UBSan.

Independently mergeable against main (0456350). git merge-tree --write-tree vs #334 and #349 is CLEAN (different hunks: resume helpers / parse_ymd calendar check). CHANGELOG [Unreleased] sibling-conflicts after the first of #321–#357 merges, same as the rest of the series.

Open in Web View Automation 

Note

Medium Risk
Scheduler recurring-task recompute is on the hot path; incorrect sentinels could tight-loop runs, though behavior for normal wall-clock schedules is unchanged. Unresolved test-file conflict markers in the PR would break the build until resolved.

Overview
Makes /schedule time math fail closed so bad or overflowing timestamps are never stored as next_fire_at values that the scheduler treats as immediately due.

Hourly recurrence (every hour / hourly and {"every":"hour"} recompute) now uses the same overflow-checked add_seconds path as every N hours, returning parse errors or 0 from next_fire_for_recur instead of wrapping near INT64_MAX.

Local calendar math treats mktime failure as unusable: make_local_epoch returns 0 instead of -1, and next_local_at / next_local_weekday_at plus at, tomorrow, on, daily, and weekly parse paths reject non-positive fire times with a shared error instead of persisting them.

Tests add overflow and extreme-now cases; the diff still contains unresolved merge conflict markers in tests/test_schedule_parser.cpp alongside calendar-date tests from main that need to be merged before land.

Reviewed by Cursor Bugbot for commit af1a19a. Bugbot is set up for automated code reviews on this repo. Configure here.

every hour used raw now+3600 (unlike every N hours / add_seconds), so a
near-int64 last-fire could wrap to a negative next_fire_at. mktime
failure returned -1, which list_due treats as always due and a recurring
recompute would tight-loop. Return 0 / a parse error instead.

Co-authored-by: Tyler Reckart <tylerreckart@users.noreply.github.com>
@tylerreckart
tylerreckart marked this pull request as ready for review September 21, 2026 12:58
@tylerreckart
tylerreckart enabled auto-merge (squash) September 21, 2026 12:58
…h-fail-closed

# Conflicts:
#	CHANGELOG.md
#	tests/test_schedule_parser.cpp
@tylerreckart
tylerreckart merged commit 23d5770 into main Sep 21, 2026
2 of 6 checks passed
tylerreckart added a commit that referenced this pull request Sep 21, 2026
#358 and #325 squash-merged unresolved conflict blocks into the
schedule parser and sandbox SSRF tests, so a clean main build failed
at compile time.

Co-authored-by: Cursor <cursoragent@cursor.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants