mirror of
https://github.com/tailscale/tailscale.git
synced 2025-10-09 08:01:31 +00:00
cmd/tailscaled,util/syspolicy/source,util/winutil/gp: disallow acquiring the GP lock during service startup
In v1.78, we started acquiring the GP lock when reading policy settings. This led to a deadlock during Tailscale installation via Group Policy Software Installation because the GP engine holds the write lock for the duration of policy processing, which in turn waits for the installation to complete, which in turn waits for the service to enter the running state. In this PR, we prevent the acquisition of GP locks (aka EnterCriticalPolicySection) during service startup and update the Windows Registry-based util/syspolicy/source.PlatformPolicyStore to handle this failure gracefully. The GP lock is somewhat optional; it’s safe to read policy settings without it, but acquiring the lock is recommended when reading multiple values to prevent the Group Policy engine from modifying settings mid-read and to avoid inconsistent results. Fixes #14416 Signed-off-by: Nick Khyl <nickk@tailscale.com>
This commit is contained in:
@@ -55,6 +55,7 @@ import (
|
||||
"tailscale.com/util/osdiag"
|
||||
"tailscale.com/util/syspolicy"
|
||||
"tailscale.com/util/winutil"
|
||||
"tailscale.com/util/winutil/gp"
|
||||
"tailscale.com/version"
|
||||
"tailscale.com/wf"
|
||||
)
|
||||
@@ -70,6 +71,22 @@ func init() {
|
||||
}
|
||||
}
|
||||
|
||||
// permitPolicyLocks is a function to be called to lift the restriction on acquiring
|
||||
// [gp.PolicyLock]s once the service is running.
|
||||
// It is safe to be called multiple times.
|
||||
var permitPolicyLocks = func() {}
|
||||
|
||||
func init() {
|
||||
if isWindowsService() {
|
||||
// We prevent [gp.PolicyLock]s from being acquired until the service enters the running state.
|
||||
// Otherwise, if tailscaled starts due to a GPSI policy installing Tailscale, it may deadlock
|
||||
// while waiting for the write counterpart of the GP lock to be released by Group Policy,
|
||||
// which is itself waiting for the installation to complete and tailscaled to start.
|
||||
// See tailscale/tailscale#14416 for more information.
|
||||
permitPolicyLocks = gp.RestrictPolicyLocks()
|
||||
}
|
||||
}
|
||||
|
||||
const serviceName = "Tailscale"
|
||||
|
||||
// Application-defined command codes between 128 and 255
|
||||
@@ -109,13 +126,13 @@ func tstunNewWithWindowsRetries(logf logger.Logf, tunName string) (_ tun.Device,
|
||||
}
|
||||
}
|
||||
|
||||
func isWindowsService() bool {
|
||||
var isWindowsService = sync.OnceValue(func() bool {
|
||||
v, err := svc.IsWindowsService()
|
||||
if err != nil {
|
||||
log.Fatalf("svc.IsWindowsService failed: %v", err)
|
||||
}
|
||||
return v
|
||||
}
|
||||
})
|
||||
|
||||
// syslogf is a logger function that writes to the Windows event log (ie, the
|
||||
// one that you see in the Windows Event Viewer). tailscaled may optionally
|
||||
@@ -180,6 +197,10 @@ func (service *ipnService) Execute(args []string, r <-chan svc.ChangeRequest, ch
|
||||
changes <- svc.Status{State: svc.Running, Accepts: svcAccepts}
|
||||
syslogf("Service running")
|
||||
|
||||
// It is safe to allow GP locks to be acquired now that the service
|
||||
// is running.
|
||||
permitPolicyLocks()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-doneCh:
|
||||
|
Reference in New Issue
Block a user