Summary
Multithreaded (/mt) builds that launch out-of-process task hosts crash with ArgumentNullException: Value cannot be null. Parameter name: path2 in Path.Combine when multiple RequestBuilder threads simultaneously resolve the task host executable name. Happens approximately 2 times out of 1000 runs.
Error
error MSB4018: The "GetReferenceAssemblyPaths" task failed unexpectedly.
System.ArgumentNullException: Value cannot be null.
Parameter name: path2
at System.IO.Path.Combine(String path1, String path2)
at Microsoft.Build.BackEnd.NodeProviderOutOfProcTaskHost.GetMSBuildExecutablePathForNonNETRuntimes(HandshakeOptions hostContext)
at ...CreateNode > ResolveNodeLaunchConfiguration
at ...AcquireAndSetUpHost
at Microsoft.Build.BackEnd.TaskHostTask.Execute()
Root cause
GetTaskHostNameFromHostContext in NodeProviderOutOfProcTaskHost.cs has an unsafe read-modify-write sequence on the static field s_msbuildName:
if (string.IsNullOrEmpty(s_msbuildName)) // (1) check
{
s_msbuildName = Environment.GetEnvironmentVariable("MSBUILD_EXE_NAME"); // (2) write — may be null
if (!string.IsNullOrEmpty(s_msbuildName))
{
return s_msbuildName;
}
s_msbuildName = Constants.MSBuildExecutableName; // (3) write — always non-null
}
return s_msbuildName; // (4) read
When MSBUILD_EXE_NAME is not set, step (2) writes null into s_msbuildName. Between steps (2) and (3), another thread can read s_msbuildName at step (4)** and get null.
The returned null is passed as toolName into Path.Combine(basePath, toolName), which throws ArgumentNullException on .NET Framework (where Path.Combine does not accept null arguments).
Summary
Multithreaded (
/mt) builds that launch out-of-process task hosts crash withArgumentNullException: Value cannot be null. Parameter name: path2inPath.Combinewhen multipleRequestBuilderthreads simultaneously resolve the task host executable name. Happens approximately 2 times out of 1000 runs.Error
Root cause
GetTaskHostNameFromHostContextinNodeProviderOutOfProcTaskHost.cshas an unsafe read-modify-write sequence on the static fields_msbuildName:When
MSBUILD_EXE_NAMEis not set, step (2) writesnullintos_msbuildName. Between steps (2) and (3), another thread can reads_msbuildNameat step (4)** and getnull.The returned
nullis passed astoolNameintoPath.Combine(basePath, toolName), which throwsArgumentNullExceptionon .NET Framework (wherePath.Combinedoes not accept null arguments).