Skip to content

Commit 8551e7e

Browse files
committed
windivert: Reinstall driver on transient device teardown
A driver marked for deletion unloads when its last handle closes, so the next Open must reinstall it. An Open that races the in-progress unload gets ERROR_NO_SUCH_DEVICE from CreateFile — the \\.\WinDivert symlink still resolves while the device object behind it is torn down — not a clean ERROR_FILE_NOT_FOUND. The old reinstall path only recognized FILE_NOT_FOUND/PATH_NOT_FOUND, so a second Open right after a Close failed instead of reinstalling. Recognize NO_SUCH_DEVICE too and retry the install/open a bounded number of times to ride out the teardown.
1 parent c4801b7 commit 8551e7e

2 files changed

Lines changed: 59 additions & 22 deletions

File tree

common/windivert/driver_windows.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,63 @@ const (
2626
// so Open can try CreateFile first and only install on FILE_NOT_FOUND.
2727
var driverDevName, _ = windows.UTF16PtrFromString(driverDeviceName)
2828

29+
// acquireDevice opens the kernel device, installing the driver when it is
30+
// absent. The driver is marked for deletion at install time (see
31+
// installDriver), so it unloads once its last handle closes; the next Open
32+
// must reinstall it. When that Open races the still-in-progress unload,
33+
// CreateFile does not report a clean ERROR_FILE_NOT_FOUND — the
34+
// \\.\WinDivert symlink still resolves while the device object behind it is
35+
// torn down, so the open fails with ERROR_NO_SUCH_DEVICE. Treat every
36+
// "device not currently openable" code as a reinstall trigger and retry a
37+
// bounded number of times so the teardown of a prior instance settles.
38+
func acquireDevice() (windows.Handle, error) {
39+
const maxRetries = 20
40+
for retry := 0; ; retry++ {
41+
device, err := openDevice()
42+
if err == nil {
43+
return device, nil
44+
}
45+
fatal := driverOpenFatal(err)
46+
if fatal != nil {
47+
return 0, fatal
48+
}
49+
err = installDriver()
50+
if err != nil {
51+
return 0, err
52+
}
53+
device, err = openDevice()
54+
if err == nil {
55+
return device, nil
56+
}
57+
fatal = driverOpenFatal(err)
58+
if fatal != nil {
59+
return 0, fatal
60+
}
61+
// Still absent right after a successful install: a prior instance's
62+
// lingering device object shadows the freshly loaded one. Back off
63+
// and retry the whole install/open.
64+
if retry >= maxRetries {
65+
return 0, E.Cause(err, "windivert: open device")
66+
}
67+
time.Sleep(50 * time.Millisecond)
68+
}
69+
}
70+
71+
// driverOpenFatal maps an openDevice failure to the error the caller should
72+
// surface, or nil when the failure means the driver is absent and a
73+
// (re)install should be attempted.
74+
func driverOpenFatal(err error) error {
75+
if errors.Is(err, windows.ERROR_ACCESS_DENIED) {
76+
return E.Cause(err, "windivert: open device (administrator required)")
77+
}
78+
if errors.Is(err, windows.ERROR_FILE_NOT_FOUND) ||
79+
errors.Is(err, windows.ERROR_PATH_NOT_FOUND) ||
80+
errors.Is(err, windows.ERROR_NO_SUCH_DEVICE) {
81+
return nil
82+
}
83+
return E.Cause(err, "windivert: open device")
84+
}
85+
2986
// Requires SeLoadDriverPrivilege (Administrator). Running the 386 build
3087
// under WOW64 on a 64-bit kernel is rejected — use the amd64 build.
3188
func installDriver() error {

common/windivert/handle_windows.go

Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -51,29 +51,9 @@ func Open(filter *Filter, layer Layer, priority int16, flags Flag) (*Handle, err
5151
if err != nil {
5252
return nil, err
5353
}
54-
device, err := openDevice()
54+
device, err := acquireDevice()
5555
if err != nil {
56-
if !errors.Is(err, windows.ERROR_FILE_NOT_FOUND) &&
57-
!errors.Is(err, windows.ERROR_PATH_NOT_FOUND) {
58-
if errors.Is(err, windows.ERROR_ACCESS_DENIED) {
59-
return nil, E.Cause(err, "windivert: open device (administrator required)")
60-
}
61-
return nil, E.Cause(err, "windivert: open device")
62-
}
63-
// Device node missing: kernel driver not loaded. Install + retry.
64-
// Matches WinDivertOpen's lazy-install path; avoids racing StartService
65-
// against a still-loaded driver whose SCM record is marked for deletion.
66-
err = installDriver()
67-
if err != nil {
68-
return nil, err
69-
}
70-
device, err = openDevice()
71-
if err != nil {
72-
if errors.Is(err, windows.ERROR_ACCESS_DENIED) {
73-
return nil, E.Cause(err, "windivert: open device (administrator required)")
74-
}
75-
return nil, E.Cause(err, "windivert: open device")
76-
}
56+
return nil, err
7757
}
7858
event, err := windows.CreateEvent(nil, 1, 0, nil) // manual reset, unsignaled
7959
if err != nil {

0 commit comments

Comments
 (0)