You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
NetworkInterface.GetAllNetworkInterfaces throws DirectoryNotFoundException on Linux when /etc does not exist #135568
On Linux, NetworkInterface.GetAllNetworkInterfaces() throws System.IO.DirectoryNotFoundException when /etc does not exist. When /etc exists but /etc/resolv.conf does not, the same call returns the interfaces with no DNS server addresses.
/etc/resolv.conf is read with File.ReadAllText, and the catch covers only FileNotFoundException and UnauthorizedAccessException:
caseError.ENOENT:// For Windows compatibility, throw DirectoryNotFoundException instead of FileNotFoundException// when the parent folder does not exist.if(!isDirError&&(pathisnull||ParentDirectoryExists(path))){return!string.IsNullOrEmpty(path)?newFileNotFoundException(SR.Format(SR.IO_FileNotFound_FileName,path),path):newFileNotFoundException(SR.IO_FileNotFound);}gotocaseError.ENOTDIR;caseError.ENOTDIR:return!string.IsNullOrEmpty(path)?
#if NET11_0_OR_GREATERnewDirectoryNotFoundException(SR.Format(SR.IO_PathNotFound_Path,path),path):
#else
newDirectoryNotFoundException(SR.Format(SR.IO_PathNotFound_Path,path)):
#endif
new DirectoryNotFoundException(SR.IO_PathNotFound_NoPathName);
The two /etc/resolv.conf readers in UnixIPInterfaceProperties, which the BSD and Apple implementations call from the IPInterfaceProperties constructor, catch only FileNotFoundException (UnixIPInterfaceProperties.cs:87-110). Those platforms were not run.
Reproduction Steps
Run as root on Linux. run.sh runs the app twice, each time in its own mount namespace: once with an empty tmpfs mounted over /etc (so /etc exists but /etc/resolv.conf does not), and once chrooted into a tmpfs root that has no /etc. The chroot root contains only /bin, /lib, /lib64, /usr, /proc, /sys, /dev, /tmp, /home and /root, so in the second case the real paths (run.sh applies readlink -f) of the app and the dotnet host must be under one of those.
<ProjectSdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net11.0</TargetFramework>
<ImplicitUsings>disable</ImplicitUsings>
<Nullable>enable</Nullable>
<!-- Lets the app run on a locally built runtime with a higher major version. -->
<RollForward>LatestMajor</RollForward>
</PropertyGroup>
</Project>
run.sh:
#!/bin/sh# Usage (as root): ./run.sh <path to dotnet host> <path to repro.dll># Runs the repro with /etc present but no /etc/resolv.conf, then with no /etc at all.# Each case runs in its own mount namespace (unshare --mount), so the host's /etc is untouched.set -u
DOTNET=$(readlink -f "$1")
APP=$(readlink -f "$2")export DOTNET
echo"== /etc present, /etc/resolv.conf missing (empty tmpfs mounted over /etc)"
unshare --mount sh -c 'mount -t tmpfs tmpfs /etc && "$DOTNET" "$0"'"$APP"echo"exit code: $?"echo"== /etc missing (chroot into a tmpfs root that has no /etc)"
unshare --mount sh -c ' ROOT=$(mktemp -d) mount -t tmpfs tmpfs "$ROOT" for d in bin lib lib64 usr proc sys dev tmp home root; do if [ -L "/$d" ]; then cp -P "/$d" "$ROOT/$d" elif [ -d "/$d" ]; then mkdir "$ROOT/$d" && mount --bind "/$d" "$ROOT/$d" fi done exec chroot "$ROOT" "$DOTNET" "$0"'"$APP"echo"exit code: $?"
With no /etc, the same result as with no /etc/resolv.conf: the interfaces are returned, DnsAddresses is empty and IsDnsEnabled is false.
Actual behavior
Output on a runtime built from 9bfbe1d (source paths and the file-open frames between Interop.CheckIo and LinuxNetworkInterfaceSystemProperties..ctor trimmed):
== /etc present, /etc/resolv.conf missing (empty tmpfs mounted over /etc)
/etc exists: True, /etc/resolv.conf exists: False
lo: IsDnsEnabled=False, DnsAddresses=0
eth0: IsDnsEnabled=False, DnsAddresses=0
exit code: 0
== /etc missing (chroot into a tmpfs root that has no /etc)
/etc exists: False, /etc/resolv.conf exists: False
Unhandled exception. System.IO.DirectoryNotFoundException: Could not find a part of the path '/etc/resolv.conf'.
Directory path: '/etc/resolv.conf'
at Interop.ThrowExceptionForIoErrno(ErrorInfo errorInfo, String path, Boolean isDirError)
at Interop.CheckIo(Error error, String path, Boolean isDirError)
...
at System.Net.NetworkInformation.LinuxNetworkInterface.LinuxNetworkInterfaceSystemProperties..ctor()
at System.Net.NetworkInformation.LinuxNetworkInterface.GetLinuxNetworkInterfaces()
at System.Net.NetworkInformation.NetworkInterfacePal.GetAllNetworkInterfaces()
at System.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces()
at Program.<Main>$(String[] args)
Aborted (core dumped)
exit code: 134
Regression?
Not checked against earlier releases.
Known Workarounds
An /etc directory, even an empty one, avoids the exception (the first case in the output above).
Configuration
Runtime built from dotnet/runtime 9bfbe1d (./build.sh clr+libs -rc Release), app built with SDK 11.0.100-rc.1.26420.103. Ubuntu 22.04.5 LTS on WSL2, x64, Linux 6.18.40.1-microsoft-standard-WSL2. Only seen when /etc is missing.
Other information
Found while porting .NET to another operating system, where /etc did not exist in the test environment.
A fix: read /etc/resolv.conf through one internal helper that returns null for both FileNotFoundException and DirectoryNotFoundException, used by all three readers. No public API change.
A missing /etc/resolv.conf used to make DnsAddresses throw PlatformNotSupportedException on Linux (#109582); #111471 fixed that by setting an empty DnsAddresses in the Linux reader's FileNotFoundException catch. A missing /etc throws DirectoryNotFoundException instead, which that catch does not cover.
I have a fix with a test ready and will open a PR for it shortly. Could this be assigned to me?
Note
AI-generated, written at my direction and reviewed by me before posting. Verified on dotnet/runtime 9bfbe1d, on Ubuntu 22.04.5 LTS x64 on WSL2 (Linux 6.18.40.1), with the runtime from ./build.sh clr+libs -rc Release: the repro commands above were run as written, as root, from a directory under /home, with dotnet on PATH being the repo's SDK (.dotnet/dotnet) for the build and the built runtime's host (artifacts/bin/testhost/net11.0-linux-Debug-x64/dotnet) for run.sh.
Description
On Linux,
NetworkInterface.GetAllNetworkInterfaces()throwsSystem.IO.DirectoryNotFoundExceptionwhen/etcdoes not exist. When/etcexists but/etc/resolv.confdoes not, the same call returns the interfaces with no DNS server addresses./etc/resolv.confis read withFile.ReadAllText, and the catch covers onlyFileNotFoundExceptionandUnauthorizedAccessException:LinuxNetworkInterface.cs:55-64
When the parent directory is missing,
ENOENTis mapped toDirectoryNotFoundException, which that catch does not cover:Interop.IOErrors.cs:120-138
The two
/etc/resolv.confreaders inUnixIPInterfaceProperties, which the BSD and Apple implementations call from theIPInterfacePropertiesconstructor, catch onlyFileNotFoundException(UnixIPInterfaceProperties.cs:87-110). Those platforms were not run.Reproduction Steps
Run as root on Linux.
run.shruns the app twice, each time in its own mount namespace: once with an empty tmpfs mounted over/etc(so/etcexists but/etc/resolv.confdoes not), and once chrooted into a tmpfs root that has no/etc. The chroot root contains only/bin,/lib,/lib64,/usr,/proc,/sys,/dev,/tmp,/homeand/root, so in the second case the real paths (run.shappliesreadlink -f) of the app and thedotnethost must be under one of those.Program.cs:repro.csproj:run.sh:dotnet build -c Release -o out ./run.sh "$(command -v dotnet)" out/repro.dllExpected behavior
With no
/etc, the same result as with no/etc/resolv.conf: the interfaces are returned,DnsAddressesis empty andIsDnsEnabledisfalse.Actual behavior
Output on a runtime built from 9bfbe1d (source paths and the file-open frames between
Interop.CheckIoandLinuxNetworkInterfaceSystemProperties..ctortrimmed):Regression?
Not checked against earlier releases.
Known Workarounds
An
/etcdirectory, even an empty one, avoids the exception (the first case in the output above).Configuration
Runtime built from dotnet/runtime 9bfbe1d (
./build.sh clr+libs -rc Release), app built with SDK 11.0.100-rc.1.26420.103. Ubuntu 22.04.5 LTS on WSL2, x64, Linux 6.18.40.1-microsoft-standard-WSL2. Only seen when/etcis missing.Other information
Found while porting .NET to another operating system, where
/etcdid not exist in the test environment.A fix: read
/etc/resolv.confthrough one internal helper that returnsnullfor bothFileNotFoundExceptionandDirectoryNotFoundException, used by all three readers. No public API change.A missing
/etc/resolv.confused to makeDnsAddressesthrowPlatformNotSupportedExceptionon Linux (#109582); #111471 fixed that by setting an emptyDnsAddressesin the Linux reader'sFileNotFoundExceptioncatch. A missing/etcthrowsDirectoryNotFoundExceptioninstead, which that catch does not cover.I have a fix with a test ready and will open a PR for it shortly. Could this be assigned to me?
Note
AI-generated, written at my direction and reviewed by me before posting. Verified on dotnet/runtime 9bfbe1d, on Ubuntu 22.04.5 LTS x64 on WSL2 (Linux 6.18.40.1), with the runtime from
./build.sh clr+libs -rc Release: the repro commands above were run as written, as root, from a directory under/home, withdotnetonPATHbeing the repo's SDK (.dotnet/dotnet) for the build and the built runtime's host (artifacts/bin/testhost/net11.0-linux-Debug-x64/dotnet) forrun.sh.