diff options
author | Zach Koopmans <zkoopmans@google.com> | 2019-02-05 10:00:22 -0800 |
---|---|---|
committer | Shentubot <shentubot@google.com> | 2019-02-05 10:02:06 -0800 |
commit | 0cf7fc4e115c2dcc40901c44b238ab36b5d966fc (patch) | |
tree | 0b283a209ab28fa6a32f8f357264e9ee947b4d84 /pkg/sentry/fs/proc | |
parent | 3eae03fe4f2813dc56d6e33cd7feb7760fccfb25 (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.go | 55 |
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 } |