[KCrash] Establish socket to allow change of ptracer
ClosedPublic

Authored by croick on Mar 11 2018, 3:11 PM.

Details

Summary
  • On Linux recent kernels only allow attaching a debugger to a process, if
    • the right to attach to processes is enabled system-wide
    • the debugger is an ancestor of the debuggee
    • or the debuggee sets a tracer process by a call to prctl(PR_SET_PTRACER, debugger_pid, 0, 0, 0);
  • also see bug 245529
  • If DrKonqi suggests debugging the crashed application with an external debugger, this right should be transferred to the debugger by the debuggee.
  • The patch opens a socket connection which allows DrKonqi to request a transfer of this right to the debugger.
Test Plan
  • try to attach gdb or KDevelop to the process by using the debug button: operation will not be permitted
  • apply corresponding patches to KCrash and DrKonqi D11235
  • the process can now be debugged
  • after detaching the debugger again, a backtrace can be created using the DrKonqi dialog as usual

Diff Detail

Repository
R285 KCrash
Lint
Automatic diff as part of commit; lint not applicable.
Unit
Automatic diff as part of commit; unit tests not applicable.
There are a very large number of changes, so older changes are hidden. Show Older Changes
croick requested review of this revision.Mar 11 2018, 3:11 PM
croick updated this revision to Diff 30980.Mar 31 2018, 12:01 AM
  • change socket file location and delete file after use
sitter added a subscriber: sitter.Apr 3 2018, 11:43 AM

Maybe I am missing something here but wouldn't this allow any application to get ptrace access?

e.g. if a malicious program watches /tmp/kcrash_*, then writes its own pid to a new socket before kcrash writes the debugger's... now the malicious program has ptrace access.

I also think *printf isn't save to call in a signal handler. Not sure about atoi.

croick updated this revision to Diff 31244.Apr 3 2018, 10:46 PM
  • check for peer PID to restrict socket connection to DrKonqi
  • add ifdefs to deactivate code for non-linuxes

Maybe I am missing something here but wouldn't this allow any application to get ptrace access?

e.g. if a malicious program watches /tmp/kcrash_*, then writes its own pid to a new socket before kcrash writes the debugger's... now the malicious program has ptrace access.

That's a valid point. In the updated revision the peer PID is checked and must match the one of DrKonqi, before prctl is called.

I also think *printf isn't save to call in a signal handler. Not sure about atoi.

atoi seems to be safe, (f)printf isn't indeed. Nevertheless it's used in the existing code already. Maybe that should be addressed in a different patch?

Thank you for your remarks!

croick updated this revision to Diff 34672.May 22 2018, 8:35 PM
  • use QDir::tempPath()
Restricted Application added a subscriber: kde-frameworks-devel. ยท View Herald TranscriptMay 22 2018, 8:35 PM
croick updated this revision to Diff 34673.May 22 2018, 8:38 PM
  • remove superfluous line...

Is there anybody who feels like reviewing this?
I'm happily using the patch on my system, but I understand that there might still be hidden issues here.

croick updated this revision to Diff 43020.Oct 7 2018, 11:13 AM
  • check for interrupted signal calls when polling
  • create path string to socket only once
  • add comments
dfaure added a subscriber: dfaure.Oct 7 2018, 4:34 PM

This looks like the kind of commit where a review by Oswald Buddenhagen <ossi@kde.org> would be useful.

src/kcrash.cpp
675

Use {} rather than ';' for the body of the while loop, otherwise many tools (and humans...) will think the ';' is an error.

dfaure added a reviewer: ossi.Oct 7 2018, 4:35 PM
croick updated this revision to Diff 43560.Oct 13 2018, 9:05 PM
  • Use empty body for while loop
ossi requested changes to this revision.Jan 14 2019, 4:39 PM

yay, i finally have "some" time for this. ^^

it took me a while to get a coherent picture of the problem and solution, because your description unnecessarily dwells on the irrelevant cases of ptrace restrictions, but omits the crucial fact that this is about tracers that are not descendant processes of drkonqi, specifically kdevelop launched via kdeinit. see also comment on DefaultDebuggerLauncher in the complementary change. please make sure to point that out in the commit messages.

the socket code seemed like an unnecessary complication at first, but then i realized that launching via kdeinit makes it impossible to just talk to the child via stdout/stderr or at least pass a file descriptor of a socketpair end. that's something that could be actually addressed in the klauncher api, because it's possible to pass open fds to other processes over sockets.

src/kcrash.cpp
658โ€“659

you need to cover that branch as well.

671

use RuntimeLocation, as the kdeinit-related code (at start of setCrashHandler()) does.

889

you need to unlink first, otherwise stale sockets will throw you off.

903

why don't you use poll()? this code is linux-specific anyway, so you can rely on posix functions from the previous decade.

923

EAGAIN is a workaround for some unspecified broken non-linux systems, so you can drop that here.

934

that's bogus use of perror()

This revision now requires changes to proceed.Jan 14 2019, 4:39 PM
In D11236#393063, @ossi wrote:

yay, i finally have "some" time for this. ^^

Great! :) And thanks for your input.

it took me a while to get a coherent picture of the problem and solution, because your description unnecessarily dwells on the irrelevant cases of ptrace restrictions, but omits the crucial fact that this is about tracers that are not descendant processes of drkonqi, specifically kdevelop launched via kdeinit. see also comment on DefaultDebuggerLauncher in the complementary change. please make sure to point that out in the commit messages.

Actually it's only the internal process that creates the backtraces, that is a descendant of DrKonqi. All debuggers that are launched via the "Debug" button are processes on their own (to remain alive, even if DrKonqi already quit). But I will change the commit message to point that out.

src/kcrash.cpp
658โ€“659

What an oversight :/ Will do of course.

ossi added a comment.Jan 15 2019, 6:27 PM

Actually it's only the internal process that creates the backtraces, that is a descendant of DrKonqi. All debuggers that are launched via the "Debug" button are processes on their own (to remain alive, even if DrKonqi already quit).

if by that you're implying that external debuggers are always launched via kdeinit, then please put that as the reason in the commit message.
irrespective of that, the "to remain alive" argument is bogus, because detaching via double fork() (as qprocess does) "protects" the child process, but does *not* break the permission inheritance.

ossi added a comment.Jan 15 2019, 7:25 PM
In D11236#393789, @ossi wrote:

detaching via double fork() "protects" the child process,

but does *not* break the permission inheritance.

ok, i take that back and claim the opposite - i misinterpreted the kernel's task_struct's real_parent member's purpose.

that also means that the kernel doc is mildly misleading ("descendant" clearly refers to the current relationship after possible re-parenting, not the real ancestry at time of forking), but i suppose one is expected to know the exact definitions of the used terminology.

croick updated this revision to Diff 49609.Jan 16 2019, 10:21 AM
croick retitled this revision from [KCrash] Establish socket to allow change of ptrace scope to [KCrash] Establish socket to allow change of ptracer.
croick edited the summary of this revision. (Show Details)
  • use poll instead of select
  • set ptracer pid and poll for request for the directly started DrKonqi as well and make the ifdef-section more readable
  • replace bogus perror
ossi requested changes to this revision.Jan 16 2019, 10:49 AM

mostly minor issues left.

please explicitly mark all handled issues as done - you'll notice on the way that you didn't address some of them.

you adjusted the summary kinda the wrong way around ... but come to think of it, i was actually kinda wrong - you indeed need to list all three cases to illustrate that neither applies. the ancestry case is special only in the sense that it automatically makes the "internal" debugger work (i'd mention that in parentheses of that case's bullet point).

src/kcrash.cpp
673

not really significant, but imo it's backwards that you let the drkonqi pid rather than the kcrash pid determine the socket name.

679

please consistently put a space after the // marker. also, stick to the file's capitalization style.

691

hmm, what about the last sentence? it seems to me that some adjustment (possibly just additional comments) is required.

872

i'd move that down, before the member init.

This revision now requires changes to proceed.Jan 16 2019, 10:49 AM
croick updated this revision to Diff 49617.Jan 16 2019, 11:55 AM
croick marked 2 inline comments as done.
  • unlink old sockets before binding
  • remove superfluous EAGAIN check
  • use debuggee pid instead of DrKonqi pid for socket name
  • fix comments
croick marked 10 inline comments as done.Jan 16 2019, 12:04 PM

please explicitly mark all handled issues as done - you'll notice on the way that you didn't address some of them.

Sorry, you were just responding too quickly.

you adjusted the summary kinda the wrong way around ... but come to think of it, i was actually kinda wrong - you indeed need to list all three cases to illustrate that neither applies. the ancestry case is special only in the sense that it automatically makes the "internal" debugger work (i'd mention that in parentheses of that case's bullet point).

Actually the point I removed does not seem to be true any longer. I'm almost certain, that the prctrl(PR_SET_PTRACER, ...) was not required until some time ago when starting DrKonqi as a fork. But now the internal backtrace is not working without. I cannot tell, when that started (currently using kernel 4.20).

src/kcrash.cpp
679

That comment was just copied from the old code.

691

see line 656

ossi added a comment.Jan 16 2019, 3:36 PM

Actually the point I removed does not seem to be true any longer. I'm almost certain, that the prctrl(PR_SET_PTRACER, ...) was not required until some time ago when starting DrKonqi as a fork. But now the internal backtrace is not working without. I cannot tell, when that started (currently using kernel 4.20).

that sounds suspicious. i don't think the kernel's behavior did changed, and the process hierarchy presumably didn't, either. the right is handed down the ancestry, and that's irrespective of whether the tracer is a "natural" ancestor or the tracee, or was set via prctl(). maybe your sysctl settings simply changed?

src/kcrash.cpp
659โ€“660

ok, so now it's claimed to apply to the entire block. but how can that be? thepollDrKonqiSocket() couldn't possibly be called then ...

909

remove eagain here as well.

croick updated this revision to Diff 49650.Jan 16 2019, 4:07 PM
croick marked 2 inline comments as done.
  • modify comment and remove check for EAGAIN
croick marked 2 inline comments as done.Jan 16 2019, 4:11 PM

that sounds suspicious. i don't think the kernel's behavior did changed, and the process hierarchy presumably didn't, either. the right is handed down the ancestry, and that's irrespective of whether the tracer is a "natural" ancestor or the tracee, or was set via prctl(). maybe your sysctl settings simply changed?

At least I didn't do that knowingly ... I will check what could be the reason.

src/kcrash.cpp
659โ€“660

True, actually DrKonqi puts the debugger in a block between SIGCONT and SIGSTOP.

croick edited the summary of this revision. (Show Details)Jan 17 2019, 11:49 AM
ossi added a comment.Jan 17 2019, 12:10 PM

commit message inverted here as well.

src/kcrash.cpp
660

"when it is about to attach a debugger".

croick updated this revision to Diff 49711.Jan 17 2019, 12:35 PM
croick marked an inline comment as done.
  • invert debugger-debuggee hierarchy in comment
croick marked an inline comment as done.Jan 17 2019, 1:25 PM
ossi added inline comments.Jan 17 2019, 1:28 PM
src/kcrash.cpp
660

ok. but come to think of it, maybe that should be revised - that SIGSTOPping sounds a tad paranoid to start with. feel like researching the history of that?

I think it's quite useful (see BUG 175362). Remaining threads remain quite busy otherwise, especially since KCrash is closing all file descriptors by default, which leads to a lot of polling of non-existant FDs in the background.
Maybe you would like to join the discussion in D18245 about this ;)

croick edited the summary of this revision. (Show Details)Jan 17 2019, 2:09 PM
croick edited the test plan for this revision. (Show Details)
ossi added a comment.Jan 17 2019, 2:37 PM

right, threads, i didn't think of that. i suppose it might make sense to pthread_kill() from within kcrash to freeze the other threads, but that will be non-atomic, and i wonder whether it wouldn't have undesirable interactions with the attached debugger and/or exiting the process.

ossi added a comment.Jan 17 2019, 5:22 PM

the 3rd line is still wrong ...

croick edited the summary of this revision. (Show Details)Jan 20 2019, 9:43 PM

the 3rd line is still wrong ...

So I used the word "tracer" now. Otherwise I don't know what you mean.

ossi added a comment.Jan 21 2019, 12:24 PM

i said "3rd line", not "3rd case". it's about the ancestry; see the other change.

croick edited the summary of this revision. (Show Details)Jan 23 2019, 11:06 PM

Ok, now I got it. After reading the YAMA doc again (more carefully) I realize that it really just is the parent that can be attached to the child by default and not the other way around.
I still wonder why I seem to recall that there was a working backtrace for the directly started DrKonqi in the tests, although the tracer was never set. Sorry about that.

ossi accepted this revision.Jan 24 2019, 11:21 AM

here too, it should be "ancestor" not "parent" for extra correctness.
amending the code comment is optional, too.

src/kcrash.cpp
664

you could make it more specific by saying "a debugger it is not an ancestor of (because it started it via kdeinit or startDetached())"

This revision is now accepted and ready to land.Jan 24 2019, 11:21 AM
croick edited the summary of this revision. (Show Details)Jan 25 2019, 8:53 AM
This revision was automatically updated to reflect the committed changes.