Skip to content

Commit e33cc07

Browse files
authoredDec 12, 2024··
Merge pull request #1825 from tyrielv/mitigate-machineconfig
Attempt to mitigate locked machine.config
2 parents 1d5c172 + f9b304c commit e33cc07

File tree

2 files changed

+47
-3
lines changed

2 files changed

+47
-3
lines changed
 

‎GVFS/GVFS.Common/Http/HttpRequestor.cs

+33-3
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using System.Net;
99
using System.Net.Http;
1010
using System.Net.Http.Headers;
11+
using System.Runtime.InteropServices;
1112
using System.Text;
1213
using System.Threading;
1314
using System.Threading.Tasks;
@@ -27,9 +28,15 @@ public abstract class HttpRequestor : IDisposable
2728

2829
static HttpRequestor()
2930
{
30-
ServicePointManager.SecurityProtocol = ServicePointManager.SecurityProtocol | SecurityProtocolType.Tls12;
31-
ServicePointManager.DefaultConnectionLimit = Environment.ProcessorCount;
32-
availableConnections = new SemaphoreSlim(ServicePointManager.DefaultConnectionLimit);
31+
/* If machine.config is locked, then initializing ServicePointManager will fail and be unrecoverable.
32+
* Machine.config locking is typically very brief (~1ms by the antivirus scanner) so we can attempt to lock
33+
* it ourselves (by opening it for read) *beforehand and briefly wait if it's locked */
34+
using (var machineConfigLock = GetMachineConfigLock())
35+
{
36+
ServicePointManager.SecurityProtocol = ServicePointManager.SecurityProtocol | SecurityProtocolType.Tls12;
37+
ServicePointManager.DefaultConnectionLimit = Environment.ProcessorCount;
38+
availableConnections = new SemaphoreSlim(ServicePointManager.DefaultConnectionLimit);
39+
}
3340
}
3441

3542
protected HttpRequestor(ITracer tracer, RetryConfig retryConfig, Enlistment enlistment)
@@ -329,5 +336,28 @@ private static bool TryGetResponseMessageFromHttpRequestException(HttpRequestExc
329336
return true;
330337

331338
}
339+
340+
private static FileStream GetMachineConfigLock()
341+
{
342+
var machineConfigLocation = RuntimeEnvironment.SystemConfigurationFile;
343+
var tries = 0;
344+
var maxTries = 3;
345+
while (tries++ < maxTries)
346+
{
347+
try
348+
{
349+
/* Opening with FileShare.Read will fail if another process (eg antivirus) has opened the file for write,
350+
but will still let ServicePointManager read the file.*/
351+
FileStream stream = File.Open(machineConfigLocation, FileMode.Open, FileAccess.Read, FileShare.Read);
352+
return stream;
353+
}
354+
catch (IOException e) when ((uint)e.HResult == 0x80070020) // SHARING_VIOLATION
355+
{
356+
Thread.Sleep(10);
357+
}
358+
}
359+
/* Couldn't get the lock - the process will likely fail. */
360+
return null;
361+
}
332362
}
333363
}

‎GVFS/GitHooksLoader/GitHooksLoader.cpp

+14
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,18 @@ int ExecuteHook(const std::wstring &applicationName, wchar_t *hookName, int argc
117117
si.dwFlags = STARTF_USESTDHANDLES;
118118

119119
ZeroMemory(&pi, sizeof(pi));
120+
121+
/* The child process will inherit ErrorMode from this process.
122+
* SEM_FAILCRITICALERRORS will prevent the .NET runtime from
123+
* creating a dialog box for critical errors - in particular
124+
* if antivirus has locked the machine.config file.
125+
* Disabling the dialog box lets the child process (typically GVFS.Hooks.exe)
126+
* continue trying to run, and if it still needs machine.config then it
127+
* can handle the exception at that time (whereas the dialog box would
128+
* hang the app until clicked, and is not handleable by our code).
129+
*/
130+
UINT previousErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS);
131+
120132
if (!CreateProcess(
121133
NULL, // Application name
122134
const_cast<LPWSTR>(commandLine.c_str()),
@@ -131,8 +143,10 @@ int ExecuteHook(const std::wstring &applicationName, wchar_t *hookName, int argc
131143
)
132144
{
133145
fwprintf(stderr, L"Could not execute '%s'. CreateProcess error (%d).\n", applicationName.c_str(), GetLastError());
146+
SetErrorMode(previousErrorMode);
134147
exit(3);
135148
}
149+
SetErrorMode(previousErrorMode);
136150

137151
// Wait until child process exits.
138152
WaitForSingleObject(pi.hProcess, INFINITE);

0 commit comments

Comments
 (0)
Please sign in to comment.