Skip to content

Commit 09c224c

Browse files
nixprimegvisor-bot
authored andcommitted
sentry/kernel: don't use Task.mu to protect Task.creds
Task.creds is documented as being "owned by the task goroutine", i.e. it can only be mutated by the task goroutine, precluding the need for writer synchronization. (AFAIK, no current Linux feature permits one task to change another's credentials; capset(2) did before the introduction of VFS capabilities, but gVisor doesn't support this. Libcs implement functions like setresuid() by signaling every thread to execute the setresuid() syscall to change their own credentials.) Prior to cl/185803179 and cl/205315612, the mutex was needed to avoid data races on auth.Credentials fields; then prior to cl/254989492, the mutex was needed to avoid data races on the auth.Credentials pointer; now the mutex is not needed at all. PiperOrigin-RevId: 777692758
1 parent 501a663 commit 09c224c

File tree

5 files changed

+43
-46
lines changed

5 files changed

+43
-46
lines changed

pkg/sentry/kernel/task.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -454,10 +454,9 @@ type Task struct {
454454

455455
// creds is the task's credentials.
456456
//
457-
// creds.Load() may be called without synchronization. creds.Store() is
458-
// serialized by mu. creds is owned by the task goroutine. All
459-
// auth.Credentials objects that creds may point to, or have pointed to
460-
// in the past, must be treated as immutable.
457+
// creds is owned by the task goroutine. All auth.Credentials objects that
458+
// creds may point to, or have pointed to in the past, must be treated as
459+
// immutable.
461460
creds auth.AtomicPtrCredentials
462461

463462
// utsns is the task's UTS namespace.

pkg/sentry/kernel/task_exec.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -245,10 +245,10 @@ func (r *runSyscallAfterExecStop) execute(t *Task) taskRunState {
245245

246246
// Switch to the new process.
247247
t.MemoryManager().Deactivate()
248-
t.mu.Lock()
249248
// Update credentials to reflect the execve. This should precede switching
250249
// MMs to ensure that dumpability has been reset first, if needed.
251-
t.updateCredsForExecLocked()
250+
t.updateCredsForExec()
251+
t.mu.Lock()
252252
oldImage := t.image
253253
t.image = *r.image
254254
t.mu.Unlock()

pkg/sentry/kernel/task_identity.go

Lines changed: 36 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -44,15 +44,14 @@ func (t *Task) HasCapability(cp linux.Capability) bool {
4444
}
4545

4646
// SetUID implements the semantics of setuid(2).
47+
//
48+
// Preconditions: The caller must be running on the task goroutine.
4749
func (t *Task) SetUID(uid auth.UID) error {
4850
// setuid considers -1 to be invalid.
4951
if !uid.Ok() {
5052
return linuxerr.EINVAL
5153
}
5254

53-
t.mu.Lock()
54-
defer t.mu.Unlock()
55-
5655
creds := t.Credentials()
5756
kuid := creds.UserNamespace.MapToKUID(uid)
5857
if !kuid.Ok() {
@@ -63,7 +62,7 @@ func (t *Task) SetUID(uid auth.UID) error {
6362
// the CAP_SETUID capability), the real UID and saved set-user-ID are also
6463
// set." - setuid(2)
6564
if creds.HasCapability(linux.CAP_SETUID) {
66-
t.setKUIDsUncheckedLocked(kuid, kuid, kuid)
65+
t.setKUIDsUnchecked(kuid, kuid, kuid)
6766
return nil
6867
}
6968
// "EPERM: The user is not privileged (Linux: does not have the CAP_SETUID
@@ -72,14 +71,14 @@ func (t *Task) SetUID(uid auth.UID) error {
7271
if kuid != creds.RealKUID && kuid != creds.SavedKUID {
7372
return linuxerr.EPERM
7473
}
75-
t.setKUIDsUncheckedLocked(creds.RealKUID, kuid, creds.SavedKUID)
74+
t.setKUIDsUnchecked(creds.RealKUID, kuid, creds.SavedKUID)
7675
return nil
7776
}
7877

7978
// SetREUID implements the semantics of setreuid(2).
79+
//
80+
// Preconditions: The caller must be running on the task goroutine.
8081
func (t *Task) SetREUID(r, e auth.UID) error {
81-
t.mu.Lock()
82-
defer t.mu.Unlock()
8382
// "Supplying a value of -1 for either the real or effective user ID forces
8483
// the system to leave that ID unchanged." - setreuid(2)
8584
creds := t.Credentials()
@@ -116,14 +115,14 @@ func (t *Task) SetREUID(r, e auth.UID) error {
116115
if r.Ok() || (e.Ok() && newE != creds.EffectiveKUID) {
117116
newS = newE
118117
}
119-
t.setKUIDsUncheckedLocked(newR, newE, newS)
118+
t.setKUIDsUnchecked(newR, newE, newS)
120119
return nil
121120
}
122121

123122
// SetRESUID implements the semantics of the setresuid(2) syscall.
123+
//
124+
// Preconditions: The caller must be running on the task goroutine.
124125
func (t *Task) SetRESUID(r, e, s auth.UID) error {
125-
t.mu.Lock()
126-
defer t.mu.Unlock()
127126
// "Unprivileged user processes may change the real UID, effective UID, and
128127
// saved set-user-ID, each to one of: the current real UID, the current
129128
// effective UID or the current saved set-user-ID. Privileged processes (on
@@ -154,12 +153,12 @@ func (t *Task) SetRESUID(r, e, s auth.UID) error {
154153
return err
155154
}
156155
}
157-
t.setKUIDsUncheckedLocked(newR, newE, newS)
156+
t.setKUIDsUnchecked(newR, newE, newS)
158157
return nil
159158
}
160159

161-
// Preconditions: t.mu must be locked.
162-
func (t *Task) setKUIDsUncheckedLocked(newR, newE, newS auth.KUID) {
160+
// Preconditions: The caller must be running on the task goroutine.
161+
func (t *Task) setKUIDsUnchecked(newR, newE, newS auth.KUID) {
163162
creds := t.Credentials().Fork() // The credentials object is immutable. See doc for creds.
164163
root := creds.UserNamespace.MapToKUID(auth.RootUID)
165164
oldR, oldE, oldS := creds.RealKUID, creds.EffectiveKUID, creds.SavedKUID
@@ -221,35 +220,33 @@ func (t *Task) setKUIDsUncheckedLocked(newR, newE, newS auth.KUID) {
221220
}
222221

223222
// SetGID implements the semantics of setgid(2).
223+
//
224+
// Preconditions: The caller must be running on the task goroutine.
224225
func (t *Task) SetGID(gid auth.GID) error {
225226
if !gid.Ok() {
226227
return linuxerr.EINVAL
227228
}
228229

229-
t.mu.Lock()
230-
defer t.mu.Unlock()
231-
232230
creds := t.Credentials()
233231
kgid := creds.UserNamespace.MapToKGID(gid)
234232
if !kgid.Ok() {
235233
return linuxerr.EINVAL
236234
}
237235
if creds.HasCapability(linux.CAP_SETGID) {
238-
t.setKGIDsUncheckedLocked(kgid, kgid, kgid)
236+
t.setKGIDsUnchecked(kgid, kgid, kgid)
239237
return nil
240238
}
241239
if kgid != creds.RealKGID && kgid != creds.SavedKGID {
242240
return linuxerr.EPERM
243241
}
244-
t.setKGIDsUncheckedLocked(creds.RealKGID, kgid, creds.SavedKGID)
242+
t.setKGIDsUnchecked(creds.RealKGID, kgid, creds.SavedKGID)
245243
return nil
246244
}
247245

248246
// SetREGID implements the semantics of setregid(2).
247+
//
248+
// Preconditions: The caller must be running on the task goroutine.
249249
func (t *Task) SetREGID(r, e auth.GID) error {
250-
t.mu.Lock()
251-
defer t.mu.Unlock()
252-
253250
creds := t.Credentials()
254251
newR := creds.RealKGID
255252
if r.Ok() {
@@ -277,17 +274,16 @@ func (t *Task) SetREGID(r, e auth.GID) error {
277274
if r.Ok() || (e.Ok() && newE != creds.EffectiveKGID) {
278275
newS = newE
279276
}
280-
t.setKGIDsUncheckedLocked(newR, newE, newS)
277+
t.setKGIDsUnchecked(newR, newE, newS)
281278
return nil
282279
}
283280

284281
// SetRESGID implements the semantics of the setresgid(2) syscall.
282+
//
283+
// Preconditions: The caller must be running on the task goroutine.
285284
func (t *Task) SetRESGID(r, e, s auth.GID) error {
286285
var err error
287286

288-
t.mu.Lock()
289-
defer t.mu.Unlock()
290-
291287
creds := t.Credentials()
292288
newR := creds.RealKGID
293289
if r.Ok() {
@@ -310,11 +306,12 @@ func (t *Task) SetRESGID(r, e, s auth.GID) error {
310306
return err
311307
}
312308
}
313-
t.setKGIDsUncheckedLocked(newR, newE, newS)
309+
t.setKGIDsUnchecked(newR, newE, newS)
314310
return nil
315311
}
316312

317-
func (t *Task) setKGIDsUncheckedLocked(newR, newE, newS auth.KGID) {
313+
// Preconditions: The caller must be running on the task goroutine.
314+
func (t *Task) setKGIDsUnchecked(newR, newE, newS auth.KGID) {
318315
creds := t.Credentials().Fork() // The credentials object is immutable. See doc for creds.
319316
oldE := creds.EffectiveKGID
320317
creds.RealKGID, creds.EffectiveKGID, creds.SavedKGID = newR, newE, newS
@@ -338,6 +335,8 @@ func (t *Task) setKGIDsUncheckedLocked(newR, newE, newS auth.KGID) {
338335

339336
// SetExtraGIDs attempts to change t's supplemental groups. All IDs are
340337
// interpreted as being in t's user namespace.
338+
//
339+
// Preconditions: The caller must be running on the task goroutine.
341340
func (t *Task) SetExtraGIDs(gids []auth.GID) error {
342341
t.mu.Lock()
343342
defer t.mu.Unlock()
@@ -364,9 +363,9 @@ var weakCaps = auth.CapabilitySetOf(linux.CAP_NET_RAW)
364363

365364
// SetCapabilitySets attempts to change t's permitted, inheritable, and
366365
// effective capability sets.
366+
//
367+
// Preconditions: The caller must be running on the task goroutine.
367368
func (t *Task) SetCapabilitySets(permitted, inheritable, effective auth.CapabilitySet) error {
368-
t.mu.Lock()
369-
defer t.mu.Unlock()
370369
// "Permitted: This is a limiting superset for the effective capabilities
371370
// that the thread may assume." - capabilities(7)
372371
if effective & ^permitted != 0 {
@@ -407,9 +406,9 @@ func (t *Task) SetCapabilitySets(permitted, inheritable, effective auth.Capabili
407406

408407
// DropBoundingCapability attempts to drop capability cp from t's capability
409408
// bounding set.
409+
//
410+
// Preconditions: The caller must be running on the task goroutine.
410411
func (t *Task) DropBoundingCapability(cp linux.Capability) error {
411-
t.mu.Lock()
412-
defer t.mu.Unlock()
413412
creds := t.Credentials()
414413
if !creds.HasCapability(linux.CAP_SETPCAP) {
415414
return linuxerr.EPERM
@@ -421,10 +420,9 @@ func (t *Task) DropBoundingCapability(cp linux.Capability) error {
421420
}
422421

423422
// SetUserNamespace attempts to move c into ns.
423+
//
424+
// Preconditions: The caller must be running on the task goroutine.
424425
func (t *Task) SetUserNamespace(ns *auth.UserNamespace) error {
425-
t.mu.Lock()
426-
defer t.mu.Unlock()
427-
428426
creds := t.Credentials()
429427
// "A process reassociating itself with a user namespace must have the
430428
// CAP_SYS_ADMIN capability in the target user namespace." - setns(2)
@@ -457,6 +455,8 @@ func (t *Task) SetUserNamespace(ns *auth.UserNamespace) error {
457455
}
458456

459457
// SetKeepCaps will set the keep capabilities flag PR_SET_KEEPCAPS.
458+
//
459+
// Preconditions: The caller must be running on the task goroutine.
460460
func (t *Task) SetKeepCaps(k bool) {
461461
t.mu.Lock()
462462
defer t.mu.Unlock()
@@ -465,7 +465,7 @@ func (t *Task) SetKeepCaps(k bool) {
465465
t.creds.Store(creds)
466466
}
467467

468-
// updateCredsForExecLocked updates t.creds to reflect an execve().
468+
// updateCredsForExec updates t.creds to reflect an execve().
469469
//
470470
// NOTE(b/30815691): We currently do not implement privileged executables
471471
// (set-user/group-ID bits and file capabilities). This allows us to make a lot
@@ -485,9 +485,7 @@ func (t *Task) SetKeepCaps(k bool) {
485485
// - Task.ptraceAttach does not serialize with execve as it does in Linux,
486486
// since no_new_privs being set has the same effect as the presence of an
487487
// unprivileged tracer.
488-
//
489-
// Preconditions: t.mu must be locked.
490-
func (t *Task) updateCredsForExecLocked() {
488+
func (t *Task) updateCredsForExec() {
491489
// """
492490
// During an execve(2), the kernel calculates the new capabilities of
493491
// the process using the following algorithm:

pkg/sentry/loader/loader.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -321,7 +321,7 @@ func Load(ctx context.Context, args LoadArgs, extraAuxv []arch.AuxEntry, vdso *V
321321
arch.AuxEntry{linux.AT_GID, hostarch.Addr(c.RealKGID.In(c.UserNamespace).OrOverflow())},
322322
arch.AuxEntry{linux.AT_EGID, hostarch.Addr(c.EffectiveKGID.In(c.UserNamespace).OrOverflow())},
323323
// The conditions that require AT_SECURE = 1 never arise. See
324-
// kernel.Task.updateCredsForExecLocked.
324+
// kernel.Task.updateCredsForExec.
325325
arch.AuxEntry{linux.AT_SECURE, 0},
326326
arch.AuxEntry{linux.AT_CLKTCK, linux.CLOCKS_PER_SEC},
327327
arch.AuxEntry{linux.AT_EXECFN, execfn},

pkg/sentry/syscalls/linux/sys_prctl.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ func Prctl(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr,
166166
return 0, nil, linuxerr.EINVAL
167167
}
168168
// PR_SET_NO_NEW_PRIVS is assumed to always be set.
169-
// See kernel.Task.updateCredsForExecLocked.
169+
// See kernel.Task.updateCredsForExec.
170170
return 0, nil, nil
171171

172172
case linux.PR_GET_NO_NEW_PRIVS:

0 commit comments

Comments
 (0)