Skip to content

watchdog: disarm on any exit once the watchdog is armed - #58

Merged
wberube merged 2 commits into
OpenIPC:masterfrom
wjames111:fix/watchdog-disarm-on-exit
Oct 8, 2026
Merged

wberube merged 2 commits into
OpenIPC:masterfrom
wjames111:fix/watchdog-disarm-on-exit

Conversation

@wjames111

Copy link
Copy Markdown
Contributor

watchdog_start() runs before sdk_start(), but several exits after that
point never reach the watchdog_stop() at the end of main(): the
HAL_ERROR returns for a failed sdk_start() and for empty RTSP
credentials, and the exit() in handle_error() after a fatal signal. Each
leaves the watchdog armed with nothing feeding it, so on a NOWAYOUT
kernel a startup failure turns into a board reset.

Register watchdog_stop() with atexit() as soon as the device is opened.
watchdog_stop() already returns early when nothing is open, so the
normal teardown call and the atexit call do not clash.

Built for infinity6e (SSC338Q). We've been running this fix on SSC338Q units.

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

Disarm the watchdog on process exit

🐞 Bug fix 🕐 Less than 10 minutes

Grey Divider

AI Description

• Register watchdog cleanup when the device opens so startup failures do not leave it armed.
• Preserve normal teardown; a second cleanup call is harmless when the device is closed.
Diagram

graph TD
  A["App startup"] --> B["Watchdog start"] --> C["Register exit hook"] --> D{"Shutdown path"}
  D -->|"Error return or exit"| E["Exit callback"] --> F["Watchdog stop"] --> G["Watchdog device"]
  D -->|"Normal teardown"| F
Loading
High-Level Assessment

Registering cleanup at watchdog startup covers ordinary early returns and exit() without duplicating cleanup across callers. Adding watchdog_stop() to each error path would be more fragile; the existing closed-device guard keeps normal teardown compatible.

Files changed (1) +5 / -0

Bug fix (1) +5 / -0
watchdog.cRegister watchdog cleanup at process exit +5/-0

Register watchdog cleanup at process exit

• Registers watchdog_stop() with atexit() after opening and configuring the device. This disarms it on early returns from main() and exit() calls that bypass normal teardown.

src/watchdog.c

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Fatal crashes can hang without a watchdog ✓ Resolved
Description
atexit(watchdog_stop) also runs when handle_error() calls exit() from a fatal-signal handler,
and watchdog_stop() logs through fprintf() after disarming the device. If the signal arrives
while another thread holds the stderr lock, that logging can block indefinitely, leaving the crashed
process alive without watchdog recovery.
Code

src/watchdog.c[27]

+    atexit(watchdog_stop);
Evidence
handle_error() is registered for fatal signals and calls exit(). The newly registered callback
writes the watchdog disarm character and closes the descriptor before HAL_INFO calls fprintf().
The application also starts worker threads, so a fatal signal can initiate this path while another
thread is using stderr.

src/main.c[20-26]
src/main.c[32-41]
src/watchdog.c[27-27]
src/watchdog.c[33-40]
src/hal/macros.h[45-49]
src/media.c[560-590]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The new atexit callback can log through stdio while exiting from a fatal-signal handler. It disarms the watchdog before that potentially blocking log call.
## Fix Focus Areas
- src/main.c[20-26]
- src/watchdog.c[27-27]
- src/watchdog.c[33-40]
## Recommended Fix
Keep atexit cleanup for ordinary exits, but give fatal signals a shutdown path that disarms the watchdog using only async-signal-safe operations and terminates without running the stdio-logging callback.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Tip of the day
💡 Did you know, you can keep summaries lean with Findings visible per group, which tucks the rest behind a View link

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread src/watchdog.c
watchdog_start() runs before sdk_start(), but several exits after that
point never reach the watchdog_stop() at the end of main(): the
HAL_ERROR returns for a failed sdk_start() and for empty RTSP
credentials, and the exit() in handle_error() after a fatal signal. Each
leaves the watchdog armed with nothing feeding it, so on a NOWAYOUT
kernel a startup failure turns into a board reset.

Register watchdog_stop() with atexit() as soon as the device is opened.
watchdog_stop() already returns early when nothing is open, so the
normal teardown call and the atexit call do not clash.
handle_error() runs as a signal handler for SIGSEGV, SIGBUS and the
other fatal signals, but called exit(), which runs the atexit handlers
and flushes stdio. That is not async-signal-safe, and with the watchdog
now disarmed from atexit it means a crash could disarm the watchdog and
then block on the stderr lock, leaving a hung process that nothing
resets.

Use _exit() so a crash skips the atexit handlers: the watchdog stays
armed and the board recovers, while startup failures and normal exits
still disarm it.
@wjames111
wjames111 force-pushed the fix/watchdog-disarm-on-exit branch from e753c33 to ebdbcb9 Compare October 7, 2026 17:02
@wjames111

Copy link
Copy Markdown
Contributor Author

Good catch from the review. Pushed a follow-up: handle_error() now leaves through _exit(), so a fatal signal skips the atexit handlers and stdio entirely. A crash keeps the watchdog armed and the board resets as before, while startup failures and normal exits still disarm it. Also rebased on master now that #56 and #57 are in.

@wberube
wberube merged commit 3f80a4a into OpenIPC:master Oct 8, 2026
@wberube

wberube commented Oct 8, 2026

Copy link
Copy Markdown
Member

Awesome, kudos for finding that!

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.

2 participants