summaryrefslogtreecommitdiffhomepage
path: root/pkg/sentry/fs/proc
diff options
context:
space:
mode:
authorZach Koopmans <zkoopmans@google.com>2019-02-05 10:00:22 -0800
committerShentubot <shentubot@google.com>2019-02-05 10:02:06 -0800
commit0cf7fc4e115c2dcc40901c44b238ab36b5d966fc (patch)
tree0b283a209ab28fa6a32f8f357264e9ee947b4d84 /pkg/sentry/fs/proc
parent3eae03fe4f2813dc56d6e33cd7feb7760fccfb25 (diff)
Change /proc/PID/cmdline to read environment vector.
- Change proc to return envp on overwrite of argv with limitations from upstream. - Add unit tests - Change layout of argv/envp on the stack so that end of argv is contiguous with beginning of envp. PiperOrigin-RevId: 232506107 Change-Id: I993880499ab2c1220f6dc456a922235c49304dec
Diffstat (limited to 'pkg/sentry/fs/proc')
-rw-r--r--pkg/sentry/fs/proc/exec_args.go55
1 files changed, 49 insertions, 6 deletions
diff --git a/pkg/sentry/fs/proc/exec_args.go b/pkg/sentry/fs/proc/exec_args.go
index a716eb5f5..9daad5d2b 100644
--- a/pkg/sentry/fs/proc/exec_args.go
+++ b/pkg/sentry/fs/proc/exec_args.go
@@ -15,6 +15,7 @@
package proc
import (
+ "bytes"
"fmt"
"io"
@@ -139,20 +140,62 @@ func (f *execArgFile) Read(ctx context.Context, _ *fs.File, dst usermem.IOSequen
// N.B. Technically this should be usermem.IOOpts.IgnorePermissions = true
// until Linux 4.9 (272ddc8b3735 "proc: don't use FOLL_FORCE for reading
// cmdline and environment").
- copyN, copyErr := m.CopyIn(ctx, start, buf, usermem.IOOpts{})
+ copyN, err := m.CopyIn(ctx, start, buf, usermem.IOOpts{})
if copyN == 0 {
// Nothing to copy.
- return 0, copyErr
+ return 0, err
}
buf = buf[:copyN]
- // TODO: On Linux, if the NUL byte at the end of the
- // argument vector has been overwritten, it continues reading the
- // environment vector as part of the argument vector.
+ // On Linux, if the NUL byte at the end of the argument vector has been
+ // overwritten, it continues reading the environment vector as part of
+ // the argument vector.
+
+ if f.arg == cmdlineExecArg && buf[copyN-1] != 0 {
+ // Linux will limit the return up to and including the first null character in argv
+
+ copyN = bytes.IndexByte(buf, 0)
+ if copyN == -1 {
+ copyN = len(buf)
+ }
+ // If we found a NUL character in argv, return upto and including that character.
+ if copyN < len(buf) {
+ buf = buf[:copyN]
+ } else { // Otherwise return into envp.
+ lengthEnvv := int(m.EnvvEnd() - m.EnvvStart())
+
+ // Upstream limits the returned amount to one page of slop.
+ // https://elixir.bootlin.com/linux/v4.20/source/fs/proc/base.c#L208
+ // we'll return one page total between argv and envp because of the
+ // above page restrictions.
+ if lengthEnvv > usermem.PageSize-len(buf) {
+ lengthEnvv = usermem.PageSize - len(buf)
+ }
+ // Make a new buffer to fit the whole thing
+ tmp := make([]byte, length+lengthEnvv)
+ copyNE, err := m.CopyIn(ctx, m.EnvvStart(), tmp[copyN:], usermem.IOOpts{})
+ if err != nil {
+ return 0, err
+ }
+
+ // Linux will return envp up to and including the first NUL character, so find it.
+ for i, c := range tmp[copyN:] {
+ if c == 0 {
+ copyNE = i
+ break
+ }
+ }
+
+ copy(tmp, buf)
+ buf = tmp[:copyN+copyNE]
+
+ }
+
+ }
n, dstErr := dst.CopyOut(ctx, buf)
if dstErr != nil {
return int64(n), dstErr
}
- return int64(n), copyErr
+ return int64(n), err
}