-
-
Notifications
You must be signed in to change notification settings - Fork 200
feat: add sentry__process_spawn()
#1318
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
vaind
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Potential NULL pointer dereference
Both Unix and Windows implementations have a potential NULL pointer dereference when checking the executable path.
Unix implementation (src/process/sentry_process_unix.c:151):
if (\!executable || \!executable->path || strcmp(executable->path, "") == 0) {
return false;
}
Windows implementation (src/process/sentry_process_windows.c:12):
if (\!executable || \!executable->path || wcscmp(executable->path, L"") == 0) {
return false;
}
Issue: The code checks \!executable but then directly accesses executable->path without verifying it's not NULL. Since sentry_path_t contains a sentry_pathchar_t *path field, this could be NULL even when the struct itself is valid, leading to a crash.
vaind
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Logic Error: False Success in Double-Fork Pattern
The Unix implementation has a fundamental design flaw in the double-fork pattern where sentry__process_spawn() returns true even when the target executable fails to launch.
Logic Analysis:
- Parent process calls
fork()→ creates first child (pid1) - First child calls
setsid()andfork()again → creates second child (pid2, the grandchild) - Second child (grandchild) calls
execv()- this is where the target process actually runs - First child immediately calls
_exit(0)after creating the grandchild - Parent process waits for the first child to exit with
waitpid(pid1, &status, 0)
The Problem:
// In spawn_process():
if (pid2 == 0) {
// second child (grandchild): actual execv() happens here
execv(argv[0], argv);
SENTRY_ERRORF("execv failed: %s", strerror(errno));
_exit(1); // ← This failure is never seen by parent
} else {
// first child exits immediately after fork
_exit(0); // ← Parent only sees this exit code
}Issue Flow:
- Parent waits for first child with
waitpid(pid1, &status, 0) - First child exits with code 0 after successful second fork
- If
execv()fails in grandchild, it exits with code 1 - But parent already received exit code 0 from first child → returns
true
Impact: Function returns true for non-existent executables, making the boolean return value misleading.
Note: This is inherent to fully detached processes - you can't reliably detect launch success without IPC. Consider either:
- Documenting that
trueonly means "detachment succeeded" - Changing return type to
void - Adding optional launch verification mechanism
This affects the Unix implementation only (lines 84-146).
—
Comment by Claude Code
|
|
Claude says it's because of the previous comment about return values. The valgrind failure I identified earlier is still valid because:
Current Status The CI is still failing with the same valgrind error and the same fundamental issue:
The code changes I can see (NULL pointer checks) were good fixes, but the fundamental double-fork logic issue remains. The function will return true as long as To fix this properly, the implementation would need to either:
The current implementation creates a false expectation that launch failures can be detected, which they cannot be with the double-fork pattern. |
just to see if the CI/Valgrind is happy with the rest
as claude put it > This is inherent to fully detached processes - you can't reliably detect launch success without IPC
vaind
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
JoshuaMoelans
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, although it might make sense to document how this API should only ever be called internally (and not with any arguments coming from user input), as otherwise passing the constructed cli value to the CreateProcessW on Windows could potentially be open to injection attacks.
Alternatively we could sanitize the arguments, but I'm not sure that is strictly necessary.
For spawning a fully detached subprocess (feedback handler / crash reporter) that continues running independently from the (potentially crashed) parent process. Extracted from #1303.
Linux
Classic double-
fork()plussetsid()in between with stdin/out/err detached, andexecv()orexecvp()(same as what Crashpad does too).macOS
Same as Linux except that
.appbundles are launched withopen -a <bundle> --args <args..>to make macOS activate the app correctly and bring it to the foreground.Windows
CreateProcessW+DETACHED_PROCESS#skip-changelog