api: ensure that device object exists before returning from open/create

Some users are seeing errors like this after rebooting from Windows
Update:

2021-01-28 18:39:45.220197: [TUN] Creating Wintun interface
2021-01-28 18:39:49.420116: [TUN] [Wintun] CreateAdapter: Creating adapter
2021-01-28 18:39:53.704007: [TUN] [Wintun] OpenDeviceObject: Failed to connect to adapter: The system cannot find the path specified. (Code 0x00000003)
2021-01-28 18:39:53.704007: [TUN] [Wintun] WintunStartSession: Failed to open adapter device object
2021-01-28 18:39:54.097037: [TUN] Unable to create Wintun interface: Error starting session: The system cannot find the path specified.

It appears that creation of the device object file might happen
asynchronously, so this commit polls for it.

Reported-by: Artem Kuroptev <artem@kuroptev.com>
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
This commit is contained in:
Jason A. Donenfeld 2021-02-03 22:32:58 +01:00
parent 2628412d71
commit fb416747dc

View File

@ -178,7 +178,7 @@ IsOurAdapter(_In_ HDEVINFO DevInfo, _In_ SP_DEVINFO_DATA *DevInfoData)
return IsOurs;
}
static _Return_type_success_(return != INVALID_HANDLE_VALUE) HANDLE OpenDeviceObject(_In_opt_z_ const WCHAR *InstanceId)
static _Return_type_success_(return != NULL) WCHAR *GetDeviceObjectFileName(_In_opt_z_ const WCHAR *InstanceId)
{
ULONG InterfacesLen;
DWORD LastError = CM_MapCrToWin32Err(
@ -191,12 +191,11 @@ static _Return_type_success_(return != INVALID_HANDLE_VALUE) HANDLE OpenDeviceOb
if (LastError != ERROR_SUCCESS)
{
SetLastError(LOG_ERROR(L"Failed to query associated instances size", LastError));
return INVALID_HANDLE_VALUE;
return NULL;
}
WCHAR *Interfaces = Alloc(InterfacesLen * sizeof(WCHAR));
if (!Interfaces)
return INVALID_HANDLE_VALUE;
HANDLE Handle = INVALID_HANDLE_VALUE;
return NULL;
LastError = CM_MapCrToWin32Err(
CM_Get_Device_Interface_ListW(
(GUID *)&GUID_DEVINTERFACE_NET,
@ -208,24 +207,57 @@ static _Return_type_success_(return != INVALID_HANDLE_VALUE) HANDLE OpenDeviceOb
if (LastError != ERROR_SUCCESS)
{
LOG_ERROR(L"Failed to get associated instances", LastError);
goto cleanupBuf;
Free(Interfaces);
SetLastError(LastError);
return NULL;
}
Handle = CreateFileW(
Interfaces,
return Interfaces;
}
static _Return_type_success_(return != INVALID_HANDLE_VALUE) HANDLE OpenDeviceObject(_In_opt_z_ const WCHAR *InstanceId)
{
WCHAR *Filename = GetDeviceObjectFileName(InstanceId);
if (!Filename)
return INVALID_HANDLE_VALUE;
HANDLE Handle = CreateFileW(
Filename,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL,
OPEN_EXISTING,
0,
NULL);
LastError = Handle != INVALID_HANDLE_VALUE ? ERROR_SUCCESS : LOG_LAST_ERROR(L"Failed to connect to adapter");
cleanupBuf:
Free(Interfaces);
if (LastError != ERROR_SUCCESS)
SetLastError(LastError);
Free(Filename);
if (Handle == INVALID_HANDLE_VALUE)
LOG_LAST_ERROR(L"Failed to connect to adapter");
return Handle;
}
static BOOL
EnsureDeviceObject(_In_opt_z_ const WCHAR *InstanceId)
{
WCHAR *Filename = GetDeviceObjectFileName(InstanceId);
if (!Filename)
return FALSE;
BOOL Exists = TRUE;
const int Attempts = 100;
for (int i = 0; i < Attempts; ++i)
{
HANDLE Handle = CreateFileW(Filename, 0, 0, NULL, OPEN_EXISTING, 0, NULL);
if (Handle != INVALID_HANDLE_VALUE)
{
CloseHandle(Handle);
goto out;
}
if (i != Attempts - 1)
Sleep(50);
}
Exists = FALSE;
out:
Free(Filename);
return Exists;
}
#define TUN_IOCTL_FORCE_CLOSE_HANDLES CTL_CODE(51820U, 0x971U, METHOD_NEITHER, FILE_READ_DATA | FILE_WRITE_DATA)
static _Return_type_success_(return != FALSE) BOOL
@ -679,7 +711,19 @@ _Return_type_success_(return != NULL) WINTUN_ADAPTER *WINAPI
}
Adapter = CreateAdapterData(Pool, DevInfo, &DevInfoData);
LastError = Adapter ? ERROR_SUCCESS : LOG(WINTUN_LOG_ERR, L"Failed to create adapter data");
if (!Adapter)
{
LastError = LOG(WINTUN_LOG_ERR, L"Failed to create adapter data");
goto cleanupDevInfo;
}
if (!EnsureDeviceObject(Adapter->DevInstanceID))
{
LastError = LOG_LAST_ERROR(L"Device object file did not appear");
goto cleanupDevInfo;
}
LastError = ERROR_SUCCESS;
goto cleanupDevInfo;
}
LastError = ERROR_FILE_NOT_FOUND;
@ -1591,6 +1635,11 @@ static _Return_type_success_(return != NULL) WINTUN_ADAPTER *CreateAdapter(
else
break;
}
if (!EnsureDeviceObject(Adapter->DevInstanceID))
{
LastError = LOG_LAST_ERROR(L"Device object file did not appear");
goto cleanupTcpipAdapterRegKey;
}
LastError = ERROR_SUCCESS;
cleanupTcpipAdapterRegKey: