Skip to content
GitLab
Explore
Sign in
Register
Primary navigation
Search or go to…
Project
W
website
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Container Registry
Model registry
Operate
Environments
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
redox-os
website
Commits
04bfb9d1
Verified
Commit
04bfb9d1
authored
5 years ago
by
jD91mZM2
Browse files
Options
Downloads
Patches
Plain Diff
RSoC: Implementing ptrace for Redox OS - part 5
parent
4bcc3243
No related branches found
No related tags found
1 merge request
!222
RSoC: Implementing ptrace for Redox OS - part 5
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
content/news/rsoc-ptrace-5.md
+149
-0
149 additions, 0 deletions
content/news/rsoc-ptrace-5.md
with
149 additions
and
0 deletions
content/news/rsoc-ptrace-5.md
0 → 100644
+
149
−
0
View file @
04bfb9d1
+++
title = "RSoC: Implementing ptrace for Redox OS - part 5"
author = "jD91mZM2"
date = "2019-07-13T11:07:22+02:00"
+++
# Introduction
This week I've decided to skip trying to get GDB working
*for now*
(there are so many issues it'll take forever to solve them), and
instead decided to finally give focus to the final concerns I had
about ptrace. Most changes this week was related to getting decent
behavior of child processes, although the design feels... suboptimal,
somehow (not sure why), so I feel I must be able to improve it better
later.
Another change was security: Tracers running as a non-root user can
now in addition to only tracing processes running as the same user,
only trace processes that are directly or indirectly children of the
tracer. In the future this can easily be allowed with some kind of
capability, but currently in Redox there isn't a capability-like
system other than the simple (but really powerful) namespacing system
which sadly I don't think can be used for this.
## Catching child processes with ptrace
The first and most pressing issue was that there needed to be a way to
catch child processes (also threads, Redox makes no distinction) from
ptrace. It should be possible to both stop the child process, or let
it keep running uninterupted.
My design revolves around an event system: When a child process is
created it sends an event with the PID, as well as pauses the child
until we've determined if we want to trace it. Events interrupt any
wait, but returns
`Ok(0)`
to signify that the breakpoint may not have
been reached yet. Then one can
`read`
from the
`proc:<pid>/trace`
file
handle to obtain a few events at a time, which will also clear
them. Setting a breakpoint while an event is not yet read, will not
wait for that breakpoint to be reached, but rather return
`Ok(0)`
immediately.
With this design, you will be able to write code like this:
```
rust
let
mut
written
=
tracer
.write
(
&
[
PTRACE_SYSCALL
])
?
;
while
written
==
0
{
let
mut
event
=
PtraceEvent
::
default
();
// Assert we read exactly one event, as we know there must be at
// least one because of `written == 0`, and we only have storage
// for one event (room for optimization: you *can* read more at
// once)
assert_eq!
(
tracer
.read
(
&
mut
event
)
?
,
mem
::
size_of
::
<
PtraceEvent
>
());
// TODO: Handle event
// Retry the wait, the tracer is still running at this point and
// may or may not have reached the breakpoint we set.
written
=
tracer
.write
(
&
[
PTRACE_WAIT
])
?
}
```
---
The implementation was actually quite simple, which was actually
somewhat of a goal for this design! (Redox is a microkernel,
remember?)
Events are sent to a
`VecDeque`
that handles them. If the queue was
previously empty, send a
`EVENT_READ`
notification to the
`event:`
scheme. Either way, notify the tracer file handle waiting for a
breakpoint to be reached.
I love the
`WaitCondition`
structure, it makes blocking and notifying
so easy :)
[
Here you can see the
code
](
https://gitlab.redox-os.org/jD91mZM2/kernel/commit/be867ae5f13e0b72a9ebd0eae32b5f9e0509e175#1408dd4ba159dbf927129c610817b1faf0e08349_24_124
)
## Catching signals
Another change I made was related to signals. It's tricky to come up
with a design that just
*feels*
right, and I didn't really succeed
here. Let's start with the critera though. I'm not very used to ptrace
so I am far from an expert in even the simple task of giving criteria,
but I'll start with the most basic: The design has to be able to trace
system calls and instructions ran in signal handlers. The design also
has to be able to obtain the actual signal number, as well as run the
program normally up to a signal.
My design is requires 2 new constants:
-
`PTRACE_EVENT_SIGNAL`
is an event similar to the child-process event
above, but on each signal we only send the signal number.
-
`PTRACE_SIGNAL`
for running the program until any signal is reached,
which in case it stops
*before*
the signal handler is invoked. So if
the child sends a signal to itself, it will be somewhere inside the
`SYS_KILL`
syscall.
---
Turns out these simple criteria are still pretty tricky to meet for
two reasons
Firstly, as a signal may happen inside a syscall, they will break the
unwritten rule that each syscall will raise 2 breakpoints in order. If
we take the example above about a program killing itself, we'll see
the following behavior
```
(pre-)syscall: SYS_KILL
(pre-)syscall: <inside the signal handler>
(post-)syscall: <inside the signal handler>
...
(pre-)syscall: SYS_SIGRETURN
(post-)syscall: SYS_KILL
```
This is annoying mostly because the program can't actually detect
whether it's the event is pre or post syscall. So it'll have to make
an intelligent guess using the signal event, which I guess could
work. But this is suboptimal and I have yet to solve this somehow.
Secondly, it is ~~possible~~, nay,
*probable*
that a signal is handled
using either
`SIG_DFL`
or
`SIG_IGN`
. Both of these are handled in the
kernel, and should probably not let you control. Plus, even I wanted
to allow this, the current way of setting breakpoints only works on
user-triggered syscalls (which is probably for the best). So no signal
breakpoint will be caused then, only a signal event. And you can't
abort the signal.
[
Related code for signal handling
(simple!)
](
https://gitlab.redox-os.org/jD91mZM2/kernel/blob/a7da393cf569416314fd1c9e016cece352537929/src/context/signal.rs#L103
)
---
I think something to set as a goal with signals is to hit two birds
with one stone and use them for handling the
`int3`
instruction which
is used by debuggers to set breakpoints. That instruction causes a
`SIGTRAP`
signal, something which I think could be used. Somehow there
has to be a way to inject code into a program (on Linux you search
memory maps for free space and inject machine code there, see
[
this
awesome article
](
https://www.linuxjournal.com/article/6210
)
)
Once again, I'd be extremely thankful for any kind of feedback, even
negative. I'll go read the Linux man page on ptrace a few times now -
we're at that point.
[
RFC
changes
](
https://gitlab.redox-os.org/redox-os/rfcs/merge_requests/14/diffs?diff_id=5320&start_sha=5290757f9b0ec3fd5ab28d9abb1e7cd7332053f9
)
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment