GitHub
11/02/2026, 11:07 AMrollbackUpgrade logged and ignored a failed reinstall of the previous package, a failed snapshot restore and osqueryd not running, then cleared the pending state and deleted the stash. Later syncs could not retry, leaving an unconfirmed package or a stopped agent while status said the rollback finished.
• (Medium): an interrupted flags update can be accepted without being loaded. If osctrld stopped between writing new flags and restarting osquery, resumeFlagsCheck verified without restarting, so the old osqueryd made the new flags look accepted.
• (Medium): an extension state-save failure loses the rollback link. The old binary was moved aside and the new one installed before Previous was saved. If that save failed, a later failed check removed the new binary without restoring the old one.
Change
• Rollback:
• Fails if the previous version is not installed afterwards, the snapshot cannot be loaded or restored, or osqueryd is not running.
• A failure keeps Pending and the stash and returns an error, so resumeUpgrade retries on the next sync.
• A reinstall error with the previous version already installed counts as success.
• The new RollbackAttempts field is saved before each attempt.
• After 3 attempts the target is recorded as rejected / rollback_unavailable.
• Flags resume: resumeFlagsCheck restarts osquery before verifying.
• Extensions: if saving the pending state fails, the previous files and autoload lines are restored right away and the step returns the error. saveExtState is now a test seam.
• README: documents the rollback retry and cap, the restart on resume, and the extension restore.
Security / operational impact
• A host whose rollback keeps failing restarts osqueryd up to 3 times (once per sync) before reporting rollback_unavailable, instead of reporting a misleading rejected with the original reason.
• A resumed flags check costs one extra osquery restart.
• No new reason strings are sent to osctrl.
Validation
• go test ./cmd/osctrld/ -race passes
• golangci-lint run ./... and `GOOS=windows golangci-lint run ./...`: 0 issues
• New tests:
• TestUpgradeRollbackInstallErrorIsRetried
• TestUpgradeRollbackGivesUpAfterMaxAttempts
• TestResumeFlagsCheckRestartsBeforeAccepting
• TestSyncExtensionsStateSaveFailureRestoresPrevious (fails without the fix)
• Existing rollback and resume tests updated to the new behaviour.
• Not exercised against a real package manager or osctrl server.
jmpsec/osctrldGitHub
11/02/2026, 11:22 AM