Skip to content

Commit 1b102e5

Browse files
nlacassegvisor-bot
authored andcommitted
Clean up host.TTYFileOperations.
We used to track the foreground process group & session on the TTYFileOperation, but these are already tracked in kernel.TTY.ThreadGroup. So remove TTYFileOperations.fgProcessGroup and .session, and replace them with a kernel.TTY. This is analogous to how sentry-internal tty's already work. Updates #10925 PiperOrigin-RevId: 679758576
1 parent 4a0bf84 commit 1b102e5

File tree

15 files changed

+263
-117
lines changed

15 files changed

+263
-117
lines changed

Diff for: pkg/sentry/control/proc.go

+5-6
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,10 @@ func (proc *Proc) execAsync(args *ExecArgs) (*kernel.ThreadGroup, kernel.ThreadI
272272
return nil, 0, nil, err
273273
}
274274

275+
if ttyFile != nil {
276+
initArgs.TTY = ttyFile.TTY()
277+
}
278+
275279
// Set cgroups to the new exec task if cgroups are mounted.
276280
cgroupRegistry := proc.Kernel.CgroupRegistry()
277281
initialCgrps := map[kernel.Cgroup]struct{}{}
@@ -292,11 +296,6 @@ func (proc *Proc) execAsync(args *ExecArgs) (*kernel.ThreadGroup, kernel.ThreadI
292296
return nil, 0, nil, err
293297
}
294298

295-
// Set the foreground process group on the TTY before starting the process.
296-
if ttyFile != nil {
297-
ttyFile.InitForegroundProcessGroup(tg.ProcessGroup())
298-
}
299-
300299
// Start the newly created process.
301300
proc.Kernel.StartProcess(tg)
302301

@@ -469,7 +468,7 @@ func ttyName(tty *kernel.TTY) string {
469468
if tty == nil {
470469
return "?"
471470
}
472-
return fmt.Sprintf("pts/%d", tty.Index)
471+
return fmt.Sprintf("pts/%d", tty.Index())
473472
}
474473

475474
// ContainerUsage retrieves per-container CPU usage.

Diff for: pkg/sentry/devices/ttydev/BUILD

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ go_library(
1212
"//pkg/abi/linux",
1313
"//pkg/context",
1414
"//pkg/errors/linuxerr",
15+
"//pkg/sentry/kernel",
1516
"//pkg/sentry/vfs",
1617
],
1718
)

Diff for: pkg/sentry/devices/ttydev/ttydev.go

+14-2
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,14 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15-
// Package ttydev implements an unopenable vfs.Device for /dev/tty.
15+
// Package ttydev implements a vfs.Device for /dev/tty.
1616
package ttydev
1717

1818
import (
1919
"gvisor.dev/gvisor/pkg/abi/linux"
2020
"gvisor.dev/gvisor/pkg/context"
2121
"gvisor.dev/gvisor/pkg/errors/linuxerr"
22+
"gvisor.dev/gvisor/pkg/sentry/kernel"
2223
"gvisor.dev/gvisor/pkg/sentry/vfs"
2324
)
2425

@@ -35,7 +36,18 @@ type ttyDevice struct{}
3536

3637
// Open implements vfs.Device.Open.
3738
func (ttyDevice) Open(ctx context.Context, mnt *vfs.Mount, vfsd *vfs.Dentry, opts vfs.OpenOptions) (*vfs.FileDescription, error) {
38-
return nil, linuxerr.EIO
39+
t := kernel.TaskFromContext(ctx)
40+
if t == nil {
41+
return nil, linuxerr.ENXIO
42+
}
43+
tty := t.ThreadGroup().TTY()
44+
if tty == nil {
45+
return nil, linuxerr.ENXIO
46+
}
47+
// Opening /dev/tty does not set the controlling terminal. See Linux
48+
// tty_open().
49+
opts.Flags |= linux.O_NOCTTY
50+
return tty.Open(ctx, mnt, vfsd, opts)
3951
}
4052

4153
// Register registers all devices implemented by this package in vfsObj.

Diff for: pkg/sentry/fsimpl/devpts/devpts.go

+8-1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import (
2727
"gvisor.dev/gvisor/pkg/context"
2828
"gvisor.dev/gvisor/pkg/errors/linuxerr"
2929
"gvisor.dev/gvisor/pkg/sentry/fsimpl/kernfs"
30+
"gvisor.dev/gvisor/pkg/sentry/kernel"
3031
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
3132
"gvisor.dev/gvisor/pkg/sentry/vfs"
3233
)
@@ -265,7 +266,13 @@ func (i *rootInode) allocateTerminal(ctx context.Context, creds *auth.Credential
265266
}
266267

267268
// Create the new terminal and replica.
268-
t := newTerminal(idx)
269+
t := &Terminal{
270+
n: idx,
271+
root: i,
272+
}
273+
t.masterKTTY = kernel.NewTTY(idx, t)
274+
t.replicaKTTY = kernel.NewTTY(idx, t)
275+
t.ld = newLineDiscipline(linux.DefaultReplicaTermios, t)
269276
replica := &replicaInode{
270277
root: i,
271278
t: t,

Diff for: pkg/sentry/fsimpl/devpts/replica.go

+1-20
Original file line numberDiff line numberDiff line change
@@ -53,26 +53,7 @@ var _ kernfs.Inode = (*replicaInode)(nil)
5353

5454
// Open implements kernfs.Inode.Open.
5555
func (ri *replicaInode) Open(ctx context.Context, rp *vfs.ResolvingPath, d *kernfs.Dentry, opts vfs.OpenOptions) (*vfs.FileDescription, error) {
56-
t := kernel.TaskFromContext(ctx)
57-
if t == nil {
58-
panic("open must be called from a task goroutine")
59-
}
60-
fd := &replicaFileDescription{
61-
inode: ri,
62-
}
63-
fd.LockFD.Init(&ri.locks)
64-
if err := fd.vfsfd.Init(fd, opts.Flags, rp.Mount(), d.VFSDentry(), &vfs.FileDescriptionOptions{}); err != nil {
65-
return nil, err
66-
}
67-
if opts.Flags&linux.O_NOCTTY == 0 {
68-
// Opening a replica sets the process' controlling TTY when
69-
// possible. An error indicates it cannot be set, and is
70-
// ignored silently.
71-
_ = t.ThreadGroup().SetControllingTTY(fd.inode.t.replicaKTTY, false /* steal */, fd.vfsfd.IsReadable())
72-
}
73-
ri.t.ld.replicaOpen()
74-
return &fd.vfsfd, nil
75-
56+
return ri.t.Open(ctx, rp.Mount(), d.VFSDentry(), opts)
7657
}
7758

7859
// Valid implements kernfs.Inode.Valid.

Diff for: pkg/sentry/fsimpl/devpts/terminal.go

+36-8
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,16 @@ package devpts
1616

1717
import (
1818
"gvisor.dev/gvisor/pkg/abi/linux"
19+
"gvisor.dev/gvisor/pkg/context"
20+
"gvisor.dev/gvisor/pkg/errors/linuxerr"
1921
"gvisor.dev/gvisor/pkg/sentry/kernel"
22+
"gvisor.dev/gvisor/pkg/sentry/vfs"
2023
)
2124

2225
// Terminal is a pseudoterminal.
2326
//
27+
// Terminal implements kernel.TTYOperations.
28+
//
2429
// +stateify savable
2530
type Terminal struct {
2631
// n is the terminal index. It is immutable.
@@ -29,6 +34,9 @@ type Terminal struct {
2934
// ld is the line discipline of the terminal. It is immutable.
3035
ld *lineDiscipline
3136

37+
// root is the rootInode for this devpts mount. It is immutable.
38+
root *rootInode
39+
3240
// masterKTTY contains the controlling process of the master end of
3341
// this terminal. This field is immutable.
3442
masterKTTY *kernel.TTY
@@ -38,13 +46,33 @@ type Terminal struct {
3846
replicaKTTY *kernel.TTY
3947
}
4048

41-
func newTerminal(n uint32) *Terminal {
42-
t := &Terminal{
43-
n: n,
44-
masterKTTY: &kernel.TTY{Index: n},
45-
replicaKTTY: &kernel.TTY{Index: n},
46-
}
47-
t.ld = newLineDiscipline(linux.DefaultReplicaTermios, t)
49+
var _ kernel.TTYOperations = (*Terminal)(nil)
4850

49-
return t
51+
// Open implements kernel.TTYOperations.Open.
52+
func (t *Terminal) Open(ctx context.Context, mnt *vfs.Mount, vfsd *vfs.Dentry, opts vfs.OpenOptions) (*vfs.FileDescription, error) {
53+
tsk := kernel.TaskFromContext(ctx)
54+
if tsk == nil {
55+
return nil, linuxerr.EIO
56+
}
57+
t.root.mu.Lock()
58+
ri, ok := t.root.replicas[t.replicaKTTY.Index()]
59+
t.root.mu.Unlock()
60+
if !ok {
61+
return nil, linuxerr.EIO
62+
}
63+
fd := &replicaFileDescription{
64+
inode: ri,
65+
}
66+
fd.LockFD.Init(&ri.locks)
67+
if err := fd.vfsfd.Init(fd, opts.Flags, mnt, vfsd, &vfs.FileDescriptionOptions{}); err != nil {
68+
return nil, err
69+
}
70+
if opts.Flags&linux.O_NOCTTY == 0 {
71+
// Opening a replica sets the process' controlling TTY when
72+
// possible. An error indicates it cannot be set, and is
73+
// ignored silently. See Linux tty_open().
74+
_ = tsk.ThreadGroup().SetControllingTTY(t.replicaKTTY, false /* steal */, fd.vfsfd.IsReadable())
75+
}
76+
ri.t.ld.replicaOpen()
77+
return &fd.vfsfd, nil
5078
}

Diff for: pkg/sentry/fsimpl/host/host.go

+1-9
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ import (
3232
"gvisor.dev/gvisor/pkg/sentry/arch"
3333
"gvisor.dev/gvisor/pkg/sentry/fsimpl/kernfs"
3434
"gvisor.dev/gvisor/pkg/sentry/hostfd"
35-
"gvisor.dev/gvisor/pkg/sentry/kernel"
3635
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
3736
"gvisor.dev/gvisor/pkg/sentry/memmap"
3837
unixsocket "gvisor.dev/gvisor/pkg/sentry/socket/unix"
@@ -669,14 +668,7 @@ func (i *inode) open(ctx context.Context, d *kernfs.Dentry, mnt *vfs.Mount, file
669668

670669
case unix.S_IFREG, unix.S_IFIFO, unix.S_IFCHR:
671670
if i.isTTY {
672-
fd := &TTYFileDescription{
673-
fileDescription: fileDescription{inode: i},
674-
termios: linux.DefaultReplicaTermios,
675-
}
676-
if task := kernel.TaskFromContext(ctx); task != nil {
677-
fd.fgProcessGroup = task.ThreadGroup().ProcessGroup()
678-
fd.session = fd.fgProcessGroup.Session()
679-
}
671+
fd := NewTTYFileDescription(i)
680672
fd.LockFD.Init(&i.locks)
681673
vfsfd := &fd.vfsfd
682674
if err := vfsfd.Init(fd, flags, mnt, d.VFSDentry(), &vfs.FileDescriptionOptions{}); err != nil {

Diff for: pkg/sentry/fsimpl/host/tty.go

+44-47
Original file line numberDiff line numberDiff line change
@@ -31,51 +31,58 @@ import (
3131
// TTYFileDescription implements vfs.FileDescriptionImpl for a host file
3232
// descriptor that wraps a TTY FD.
3333
//
34+
// It implements kernel.TTYOperations.
35+
//
3436
// +stateify savable
3537
type TTYFileDescription struct {
3638
fileDescription
3739

3840
// mu protects the fields below.
3941
mu sync.Mutex `state:"nosave"`
4042

41-
// session is the session attached to this TTYFileDescription.
42-
session *kernel.Session
43-
44-
// fgProcessGroup is the foreground process group that is currently
45-
// connected to this TTY.
46-
fgProcessGroup *kernel.ProcessGroup
47-
4843
// termios contains the terminal attributes for this TTY.
4944
termios linux.KernelTermios
45+
46+
// tty is the kernel.TTY associated with this host tty.
47+
tty *kernel.TTY
5048
}
5149

52-
// InitForegroundProcessGroup sets the foreground process group and session for
53-
// the TTY. This should only be called once, after the foreground process group
54-
// has been created, but before it has started running.
55-
func (t *TTYFileDescription) InitForegroundProcessGroup(pg *kernel.ProcessGroup) {
56-
t.mu.Lock()
57-
defer t.mu.Unlock()
58-
if t.fgProcessGroup != nil {
59-
panic("foreground process group is already set")
50+
// NewTTYFileDescription returns a new TTYFileDescription.
51+
func NewTTYFileDescription(i *inode) *TTYFileDescription {
52+
fd := &TTYFileDescription{
53+
fileDescription: fileDescription{inode: i},
54+
termios: linux.DefaultReplicaTermios,
6055
}
61-
t.fgProcessGroup = pg
62-
t.session = pg.Session()
56+
// Index does not matter here. This tty is not coming from a devpts
57+
// mount, so it won't collide with any of the ptys created there.
58+
fd.tty = kernel.NewTTY(0, fd)
59+
return fd
6360
}
6461

65-
// ForegroundProcessGroup returns the foreground process for the TTY.
66-
func (t *TTYFileDescription) ForegroundProcessGroup() *kernel.ProcessGroup {
67-
t.mu.Lock()
68-
defer t.mu.Unlock()
69-
return t.fgProcessGroup
62+
// Open re-opens the tty fd, for example via open(/dev/tty). See Linux's
63+
// tty_repoen().
64+
func (t *TTYFileDescription) Open(_ context.Context, _ *vfs.Mount, _ *vfs.Dentry, _ vfs.OpenOptions) (*vfs.FileDescription, error) {
65+
t.vfsfd.IncRef()
66+
return &t.vfsfd, nil
7067
}
7168

7269
// Release implements fs.FileOperations.Release.
7370
func (t *TTYFileDescription) Release(ctx context.Context) {
71+
t.fileDescription.Release(ctx)
72+
}
73+
74+
// TTY returns the kernel.TTY.
75+
func (t *TTYFileDescription) TTY() *kernel.TTY {
7476
t.mu.Lock()
75-
t.fgProcessGroup = nil
76-
t.mu.Unlock()
77+
defer t.mu.Unlock()
78+
return t.tty
79+
}
7780

78-
t.fileDescription.Release(ctx)
81+
// ThreadGroup returns the kernel.ThreadGroup associated with this tty.
82+
func (t *TTYFileDescription) ThreadGroup() *kernel.ThreadGroup {
83+
t.mu.Lock()
84+
defer t.mu.Unlock()
85+
return t.tty.ThreadGroup()
7986
}
8087

8188
// PRead implements vfs.FileDescriptionImpl.PRead.
@@ -206,9 +213,14 @@ func (t *TTYFileDescription) Ioctl(ctx context.Context, io usermem.IO, sysno uin
206213
t.mu.Lock()
207214
defer t.mu.Unlock()
208215

216+
fgpg, err := t.tty.ThreadGroup().ForegroundProcessGroup(t.tty)
217+
if err != nil {
218+
return 0, err
219+
}
220+
209221
// Map the ProcessGroup into a ProcessGroupID in the task's PID namespace.
210-
pgID := primitive.Int32(pidns.IDOfProcessGroup(t.fgProcessGroup))
211-
_, err := pgID.CopyOut(task, args[2].Pointer())
222+
pgID := primitive.Int32(pidns.IDOfProcessGroup(fgpg))
223+
_, err = pgID.CopyOut(task, args[2].Pointer())
212224
return 0, err
213225

214226
case linux.TIOCSPGRP:
@@ -230,7 +242,7 @@ func (t *TTYFileDescription) Ioctl(ctx context.Context, io usermem.IO, sysno uin
230242
}
231243

232244
// Check that calling task's process group is in the TTY session.
233-
if task.ThreadGroup().Session() != t.session {
245+
if task.ThreadGroup().Session() != t.tty.ThreadGroup().Session() {
234246
return 0, linuxerr.ENOTTY
235247
}
236248

@@ -239,25 +251,10 @@ func (t *TTYFileDescription) Ioctl(ctx context.Context, io usermem.IO, sysno uin
239251
return 0, err
240252
}
241253
pgID := kernel.ProcessGroupID(pgIDP)
242-
243-
// pgID must be non-negative.
244-
if pgID < 0 {
245-
return 0, linuxerr.EINVAL
246-
}
247-
248-
// Process group with pgID must exist in this PID namespace.
249-
pidns := task.PIDNamespace()
250-
pg := pidns.ProcessGroupWithID(pgID)
251-
if pg == nil {
252-
return 0, linuxerr.ESRCH
253-
}
254-
255-
// Check that new process group is in the TTY session.
256-
if pg.Session() != t.session {
257-
return 0, linuxerr.EPERM
254+
if err := t.tty.ThreadGroup().SetForegroundProcessGroupID(t.tty, pgID); err != nil {
255+
return 0, err
258256
}
259257

260-
t.fgProcessGroup = pg
261258
return 0, nil
262259

263260
case linux.TIOCGWINSZ:
@@ -341,12 +338,12 @@ func (t *TTYFileDescription) checkChange(ctx context.Context, sig linux.Signal)
341338
// If the session for the task is different than the session for the
342339
// controlling TTY, then the change is allowed. Seems like a bad idea,
343340
// but that's exactly what linux does.
344-
if tg.Session() != t.fgProcessGroup.Session() {
341+
if tg.Session() != t.tty.ThreadGroup().Session() {
345342
return nil
346343
}
347344

348345
// If we are the foreground process group, then the change is allowed.
349-
if pg == t.fgProcessGroup {
346+
if fgpg, _ := t.tty.ThreadGroup().ForegroundProcessGroup(t.tty); pg == fgpg {
350347
return nil
351348
}
352349

Diff for: pkg/sentry/kernel/kernel.go

+10
Original file line numberDiff line numberDiff line change
@@ -990,6 +990,9 @@ type CreateProcessArgs struct {
990990

991991
// Origin indicates how the task was first created.
992992
Origin TaskOrigin
993+
994+
// TTY is the optional controlling TTY to associate with this process.
995+
TTY *TTY
993996
}
994997

995998
// NewContext returns a context.Context that represents the task that will be
@@ -1221,6 +1224,13 @@ func (k *Kernel) CreateProcess(args CreateProcessArgs) (*ThreadGroup, ThreadID,
12211224
}
12221225
t.traceExecEvent(image) // Simulate exec for tracing.
12231226

1227+
// Set TTY if configured.
1228+
if args.TTY != nil {
1229+
if err := t.tg.SetControllingTTY(args.TTY, false /* steal */, true /* isReadable */); err != nil {
1230+
return nil, 0, fmt.Errorf("setting controlling tty: %w", err)
1231+
}
1232+
}
1233+
12241234
// Success.
12251235
cu.Release()
12261236
tgid := k.tasks.Root.IDOfThreadGroup(tg)

0 commit comments

Comments
 (0)