Repository navigation
Fixes from the Qodo review of the whole codebase (#34); v1.12 - #36
Conversation
Qodo reviewed the full tree in PR #35 and reported 13 bugs; 12 are fixed here, one is not a bug (udprelay: r->fd is set to -1 by the failed socket() call before the cleanup path, so descriptor 0 is never closed). - netcore/stack: apply option 121/249 routes of every DHCPACK while bound (renewals keep the address, so status_cb never saw them) - vtun_client: only complete frames refresh last_rx; negative tuning values are rejected; reconnect backoff doubling cannot overflow - proxy: SOCKS5 handshakes get a deadline (connect timeout) - cli: checked parsing of --seconds and the --fetch port; the stop hook waits for earlier hooks and is itself waited for - vtesse-udhcpc.script: on-link classless routes without "via 0.0.0.0" - S99vtesse: stop/status match the daemon binary (-x) - ci/build-musl.sh: QEMU test failures fail the build; toolchain archives are verified (pinned SHA256, GitHub digest for the rolling OpenIPC asset) - bench/selftest.py: assert the first frame itself is ECHO_REQ - tests: negative timer settings, a never-finished frame times out Refs #34
PR Summary by QodoFix DHCP renewals, tunnel timeouts, and build verification
AI Description
Diagram
High-Level Assessment
Files changed (11)
|
Code Review by Qodo
1.
|
| static void wait_children(void) | ||
| { | ||
| while (waitpid(-1, NULL, 0) > 0 || errno == EINTR) | ||
| ; | ||
| } |
There was a problem hiding this comment.
2. Client exit can hang on a slow hook 🐞 Bug ☼ Reliability
wait_children() calls blocking waitpid(-1, NULL, 0) without a deadline both before and after the stop hook, after the event loop has returned. An earlier asynchronous hook such as up, reconnect, or down can remain stuck—including udhcpc -b retrying DHCP while the tap is no longer serviced—preventing the DHCP cleanup hook from running; restarting signal handlers keep SIGINT/SIGTERM from interrupting the wait, and S99vtesse restart can start another instance on the same interface while the first still waits.
Agent Prompt
## Issue description
`wait_children()` can wait indefinitely for an earlier asynchronous hook after the event loop stops, preventing the `stop` hook from cleaning up DHCP and delaying teardown. The wait also cannot be interrupted by SIGTERM, allowing a restart to begin while the first instance still waits.
## Fix Focus Areas
- cli/vtesse_cli.c[522-528]
- cli/vtesse_cli.c[911-913]
## Recommended Fix
Replace the blocking wait with polling via `waitpid(-1, NULL, WNOHANG)`, a short sleep, and an overall shutdown deadline (for example, five seconds). Give up early if a stop signal arrives; if an earlier hook remains stuck, log which hooks are still running and terminate or otherwise handle them before continuing to the `stop` hook and teardown. Bound the wait for the `stop` hook as well.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
As PID 1 in a container vtesse-cli also inherits the DHCP client that the "up" hook daemonized, so waitpid(-1) never returned and docker stop had to kill it (bench tap-test: "clean exit on SIGTERM"). The hook PIDs are tracked and only they are waited for.
…limit - netcore/stack: lwIP reuses the xid in the renewal DHCPREQUEST, so a new ACK is detected by dhcp->lease_used going back to 0 (dhcp_bind() resets it on every ACK), not by a changed xid - cli: waiting for hooks is bounded (5 s, then the hook is terminated), so shutdown cannot hang on a stuck hook - cli: --seconds rejects inf and values above 1e9 (the deadline cast would overflow)
|
Qodo round 2 → all three fixed in 177b1ea:
/review |
I can’t rerun the Qodo review from this thread because the review action isn’t available here. The reported fixes cover finding 1, finding 2, and finding 3; you can trigger |
|
/review |
|
Code review by qodo was updated up to the latest commit 177b1ea |
- cli: a hook is started only when a PID slot is free (16), so shutdown can wait for every hook it started; otherwise it is skipped with a message - netcore/stack: a return of the DHCP state to BOUND (after RENEWING, REBINDING, REBOOTING) also applies the ACK's routes: a quick reconnect happens before lease_used was ever incremented - cli: the --seconds message says the value must be positive
|
Qodo round 3 → fixed in 3b6b6c6: |
|
/review |
Thanks — the update addresses the remaining open findings:
The published review still shows these findings as open; they should be marked resolved after a review of commit |
|
Code review by qodo was updated up to the latest commit 3b6b6c6 |
- cli: an interrupted waitpid() is retried and never taken for a finished hook, so a running hook keeps its slot and is waited for at shutdown - ci/build-musl.sh: the GitHub API call for the OpenIPC toolchain digest uses GITHUB_TOKEN/GH_TOKEN when set (the anonymous limit is 60/hour per IP) and reports a failed request as such; the workflow passes the token
|
Qodo round 4 → both fixed in 4536fa1: |
|
/review |
Thanks—acknowledged. The fixes for finding 2 (interrupted waits) and finding 6 (GitHub API rate-limit handling) are reported in commit |
|
Code review by qodo was updated up to the latest commit 4536fa1 |
- netcore/stack: option 121/249 is recorded only from an ACK lwIP accepts (REQUESTING, REBOOTING, RENEWING, REBINDING); a duplicate ACK while BOUND is parsed but ignored by lwIP and must not leave pending routes - ci: jq in ci/Dockerfile.cross, and build-musl.sh says so if it is missing - cli: hooks run in their own process group; a hook still running at shutdown is ended as a group (SIGTERM, SIGKILL after 1 s), so a udhcpc still in its foreground is ended too
|
Qodo round 5 → all three fixed in 06bc832: |
|
/review |
Thanks—I've noted commit |
|
Code review by qodo was updated up to the latest commit 06bc832 |
…limit - netcore/stack: lwIP reuses the xid in the renewal DHCPREQUEST, so a new ACK is detected by dhcp->lease_used going back to 0 (dhcp_bind() resets it on every ACK), not by a changed xid - cli: waiting for hooks is bounded (5 s, then the hook is terminated), so shutdown cannot hang on a stuck hook - cli: --seconds rejects inf and values above 1e9 (the deadline cast would overflow)
- cli: a hook is started only when a PID slot is free (16), so shutdown can wait for every hook it started; otherwise it is skipped with a message - netcore/stack: a return of the DHCP state to BOUND (after RENEWING, REBINDING, REBOOTING) also applies the ACK's routes: a quick reconnect happens before lease_used was ever incremented - cli: the --seconds message says the value must be positive
- cli: an interrupted waitpid() is retried and never taken for a finished hook, so a running hook keeps its slot and is waited for at shutdown - ci/build-musl.sh: the GitHub API call for the OpenIPC toolchain digest uses GITHUB_TOKEN/GH_TOKEN when set (the anonymous limit is 60/hour per IP) and reports a failed request as such; the workflow passes the token
Fixes for the findings of the first full Qodo review (#35, review-only PR).
r->fd = socket()is -1 on failure beforegoto failstophook waits for earlier hooksvia 0.0.0.0last_rx-x "$DAEMON"--secondscheckedTested: unit tests (native and ASan/UBSan, 111 + 63 checks), two new tests fail on the old client and pass now;
ci/build-musl.shfor arm32, mipsel and arm64 from an empty cache (downloads verified). The bench acceptance runs in CI.Refs #34