diff --git a/src/SharpEmu.Libs/Kernel/KernelMemoryCompatExports.cs b/src/SharpEmu.Libs/Kernel/KernelMemoryCompatExports.cs index 87587d5..c14e48c 100644 --- a/src/SharpEmu.Libs/Kernel/KernelMemoryCompatExports.cs +++ b/src/SharpEmu.Libs/Kernel/KernelMemoryCompatExports.cs @@ -1966,6 +1966,12 @@ public static partial class KernelMemoryCompatExports return (int)OrbisGen2Result.ORBIS_GEN2_OK; } + if (KernelSocketCompatExports.TryCloseSocketFd(fd)) + { + ctx[CpuRegister.Rax] = 0; + return (int)OrbisGen2Result.ORBIS_GEN2_OK; + } + FileStream? stream; lock (_fdGate) { diff --git a/src/SharpEmu.Libs/Kernel/KernelSocketCompatExports.cs b/src/SharpEmu.Libs/Kernel/KernelSocketCompatExports.cs index 45ea32c..462e934 100644 --- a/src/SharpEmu.Libs/Kernel/KernelSocketCompatExports.cs +++ b/src/SharpEmu.Libs/Kernel/KernelSocketCompatExports.cs @@ -160,7 +160,6 @@ internal static class KernelSocketCompatExports if (!TryParseGuestSockaddrIn(sockaddrAddress, addrlen, ctx, out var ipAddress, out var port)) { LogNet($"connect sockaddr parse failed: fd={fd} addr=0x{sockaddrAddress:X} len={addrlen}"); - RemoveEmulatedSocketFd(fd); ctx[CpuRegister.Rax] = unchecked((ulong)0xFFFFFFFFFFFFFFFF); return (int)OrbisGen2Result.ORBIS_GEN2_OK; } @@ -174,7 +173,6 @@ internal static class KernelSocketCompatExports if (!IsGuestTcpOutboundAllowed(ipAddress, redirectApplied)) { LogNet($"connect denied by outbound policy: fd={fd} ip={ipAddress} port={port}"); - RemoveEmulatedSocketFd(fd); ctx[CpuRegister.Rax] = unchecked((ulong)0xFFFFFFFFFFFFFFFF); return (int)OrbisGen2Result.ORBIS_GEN2_OK; } @@ -182,7 +180,6 @@ internal static class KernelSocketCompatExports if (!TryEstablishHostTcpConnection(ipAddress, port, out var client, out var stream)) { LogNet($"connect failed: fd={fd} ip={ipAddress} port={port}"); - RemoveEmulatedSocketFd(fd); ctx[CpuRegister.Rax] = unchecked((ulong)0xFFFFFFFFFFFFFFFF); return (int)OrbisGen2Result.ORBIS_GEN2_OK; } @@ -418,17 +415,6 @@ internal static class KernelSocketCompatExports state.Connected = false; } - private static void RemoveEmulatedSocketFd(int fd) - { - lock (Gate) - { - if (Sockets.Remove(fd, out var socketState)) - { - DisposeEmulatedSocket(socketState); - } - } - } - private static void LogNet(string message) { if (string.Equals(Environment.GetEnvironmentVariable("SHARPEMU_LOG_NET"), "1", StringComparison.Ordinal)) diff --git a/tests/SharpEmu.Libs.Tests/Kernel/KernelSocketCompatExportsTests.cs b/tests/SharpEmu.Libs.Tests/Kernel/KernelSocketCompatExportsTests.cs new file mode 100644 index 0000000..fb7a511 --- /dev/null +++ b/tests/SharpEmu.Libs.Tests/Kernel/KernelSocketCompatExportsTests.cs @@ -0,0 +1,50 @@ +// Copyright (C) 2026 SharpEmu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +using SharpEmu.HLE; +using SharpEmu.Libs.Kernel; +using Xunit; + +namespace SharpEmu.Libs.Tests.Kernel; + +public sealed class KernelSocketCompatExportsTests +{ + [Fact] + public void Connect_InvalidSockaddrLeavesFdOpenForGuestClose() + { + const ulong memoryBase = 0x0000_7FFF_3000_0000; + var context = new CpuContext(new FakeCpuMemory(memoryBase, 0x1000), Generation.Gen5); + context[CpuRegister.Rdi] = 2; + context[CpuRegister.Rsi] = 1; + context[CpuRegister.Rdx] = 6; + + Assert.Equal(0, KernelSocketCompatExports.Socket(context)); + Assert.NotEqual(ulong.MaxValue, context[CpuRegister.Rax]); + var guestFd = checked((int)context[CpuRegister.Rax]); + + try + { + context[CpuRegister.Rdi] = unchecked((ulong)guestFd); + context[CpuRegister.Rsi] = memoryBase; + context[CpuRegister.Rdx] = 0; + + Assert.Equal(0, KernelSocketCompatExports.Connect(context)); + Assert.Equal(ulong.MaxValue, context[CpuRegister.Rax]); + + context[CpuRegister.Rdi] = unchecked((ulong)guestFd); + Assert.Equal( + (int)OrbisGen2Result.ORBIS_GEN2_OK, + KernelMemoryCompatExports.PosixClose(context)); + Assert.Equal(0UL, context[CpuRegister.Rax]); + + context[CpuRegister.Rdi] = unchecked((ulong)guestFd); + Assert.Equal( + (int)OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND, + KernelMemoryCompatExports.PosixClose(context)); + } + finally + { + KernelSocketCompatExports.TryCloseSocketFd(guestFd); + } + } +}