Skip to content
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

Add the ability to run drgn against the live kernel as non-root user #347

Merged
merged 3 commits into from
Sep 11, 2023

Conversation

brenns10
Copy link
Contributor

(As described in Slack)

/proc/kcore can only be opened by root, so live debugging has been restricted to running drgn as root. But running drgn as root has downsides:

  1. The PYTHONPATH does not include your home directory
  2. Python may drop pyc files as root (this can be avoided with -B)
  3. All files and other actions your script might take are done by root.

It would be very nice to be able to run Drgn as your current user, and only use escalated privileges to open /proc/kcore. One approach would be to drop privileges after creating the program, which improves numbers 2 and 3. Unfortunately the PYTHONPATH, PATH, USER, and HOME environment variables would be unchanged, so much of the standard library would still believe you are root (e.g. getpass.getuser()). This can be worked around, but it's just a bit messy.

Instead, we can run a helper program with sudo that will transmit the opened file descriptor back to Drgn via a Unix socket.

To do this, we add Program.set_fd() and program_from_fd() which behave just like set_core_dump(), etc, except they take the FD rather than the path.

We add a helper script to do the "unix socket magic", and then we tie it all together in the CLI code. When the CLI code runs against the kernel, if it is not running as root, it will use the helper function to get the file descriptor. If the CLI is already running as root, nothing changes.

Running as non-root breaks drgn's module & section iterators, since drgn expects to be able to read data out of /sys/modules -- unfortunately some of it has 400 permissions and can only be read by root. Thankfully, that code is just an optimized code path. We can disable that if we're running as a non-root user.

Putting this all together, we have a way to run drgn as non-root user, and have all the features we've come to expect!

@brenns10 brenns10 force-pushed the sudo_fd branch 2 times, most recently from 8e8d809 to 16c1b4a Compare August 17, 2023 07:50
@brenns10
Copy link
Contributor Author

(Sorry for the force pushes, my pre-commit hooks weren't setup on my non-work laptop)

Copy link
Owner

@osandov osandov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't looked at the actual sudohelper in detail yet, but I wanted to get the comments on the rest out before I head out for the weekend. Thanks!

_drgn.pyi Outdated Show resolved Hide resolved
_drgn.pyi Outdated Show resolved Hide resolved
libdrgn/linux_kernel.c Outdated Show resolved Hide resolved
libdrgn/program.c Outdated Show resolved Hide resolved
@brenns10 brenns10 force-pushed the sudo_fd branch 3 times, most recently from b6c19ee to 97cd2db Compare August 19, 2023 02:30
@brenns10
Copy link
Contributor Author

What I'm learning is that git rebase does not run pre-commit hooks. Sorry for the noise.

Copy link
Owner

@osandov osandov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I only looked at sudohelper this time around, so I haven't looked at your other updates yet.

drgn/internal/sudohelper.py Outdated Show resolved Hide resolved
drgn/internal/sudohelper.py Outdated Show resolved Hide resolved
drgn/internal/sudohelper.py Outdated Show resolved Hide resolved
drgn/internal/sudohelper.py Outdated Show resolved Hide resolved
drgn/internal/sudohelper.py Outdated Show resolved Hide resolved
drgn/internal/sudohelper.py Outdated Show resolved Hide resolved
drgn/internal/sudohelper.py Outdated Show resolved Hide resolved
drgn/internal/sudohelper.py Outdated Show resolved Hide resolved
Copy link
Owner

@osandov osandov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks awesome, just a few more tweaks.

_drgn.pyi Outdated Show resolved Hide resolved
libdrgn/drgn.h.in Outdated Show resolved Hide resolved
_drgn.pyi Outdated Show resolved Hide resolved
libdrgn/linux_kernel.c Outdated Show resolved Hide resolved
libdrgn/linux_kernel.c Outdated Show resolved Hide resolved
drgn/cli.py Outdated Show resolved Hide resolved
drgn/internal/sudohelper.py Outdated Show resolved Hide resolved
drgn/internal/sudohelper.py Outdated Show resolved Hide resolved
@brenns10 brenns10 force-pushed the sudo_fd branch 3 times, most recently from be34dc7 to cd58049 Compare September 10, 2023 04:55
@brenns10
Copy link
Contributor Author

Thanks for the excellent review feedback, I'm so excited about this now! Try python -m contrib.ptdrgn for extra fun, since I did rebase this :D

This allows an open file descriptor to be passed into Drgn and treated
as a core dump.  There are many use cases, but one interesting one is
that the FD could be sent by a helper process running as root, allowing
a non-root Drgn process to debug the running kernel.

Signed-off-by: Stephen Brennan <stephen@brennan.io>
The files within /sys/module/*/sections seem to normally be 400
permissions, only accessible by root. Normally for live use, this is not
a problem because we are running as root. However, if we're running as
non-root, then we may get EACCES on these files.

To handle this, fall back to using the non-live approach if we get an
EACCES. Even if we do get the error, we can continue to use the
/sys/module/*/notes files to maintain a partial speedup.

Signed-off-by: Stephen Brennan <stephen@brennan.io>
Non-root users can now run Drgn against the running kernel. Drgn will
attempt to use sudo to open /proc/kcore and transmit the opened file
descriptor back to the user process. The file descriptor is then passed
to Program.set_core_dump(). The user must still have sudo privileges.

Signed-off-by: Stephen Brennan <stephen@brennan.io>
Copy link
Owner

@osandov osandov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There was still some funky indentation in kernel_module_section_iterator_init_no_sys_module, so I went ahead and fixed that and added "and program_from_core_dump()" to the commit subject for the first commit. I'll merge this now, thanks! This is a really nice feature.

@osandov osandov merged commit 40c0831 into osandov:main Sep 11, 2023
@brenns10
Copy link
Contributor Author

Thanks for the quick cleanup, sorry I missed it! I'm already using this on my development machines so glad it can be in main now :)

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