Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 8 additions & 7 deletions kernel/src/net/socket/inet/datagram/inner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,6 @@ impl UnboundUdp {
Ok(BoundUdp {
inner,
remote: SpinLock::new(None),
local_endpoint: smoltcp::wire::IpEndpoint::new(bind_addr, bind_port),
})
}

Expand All @@ -85,7 +84,6 @@ impl UnboundUdp {
Ok(BoundUdp {
inner,
remote: SpinLock::new(Some(endpoint)),
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: bind_ephemeral stores local endpoint in remote field

In bind_ephemeral, the endpoint variable is constructed from the local address and local bound_port, making it the local endpoint. However, this is stored in the remote field. The newly implemented remote_endpoint() method returns this value, so getpeername() would return the local endpoint instead of the remote endpoint for sockets that went through bind_ephemeral. While this existed in the old code, it's now exposed since remote_endpoint() was changed from todo!() to an actual implementation.

Additional Locations (1)

Fix in Cursor Fix in Web

local_endpoint: endpoint,
})
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: getsockname returns wrong data after implicit bind

The bind_ephemeral function reserves an ephemeral port via port_manager().bind_ephemeral_port() but never binds the smoltcp socket to that port (unlike the bind function which explicitly calls socket.bind()). The old code stored the endpoint in a local_endpoint field and returned it correctly. The new local_endpoint() implementation relies on socket.endpoint() from the smoltcp socket, which returns incorrect/default data for sockets that went through bind_ephemeral since they were never actually bound. This causes getsockname() to return wrong data after implicit binding via sendto().

Additional Locations (1)

Fix in Cursor Fix in Web

}
Expand All @@ -94,7 +92,6 @@ impl UnboundUdp {
pub struct BoundUdp {
inner: BoundInner,
remote: SpinLock<Option<smoltcp::wire::IpEndpoint>>,
local_endpoint: smoltcp::wire::IpEndpoint,
}

impl BoundUdp {
Expand All @@ -117,6 +114,14 @@ impl BoundUdp {
.with::<SmolUdpSocket, _, _>(|socket| socket.endpoint())
}

pub fn remote_endpoint(&self) -> Result<smoltcp::wire::IpEndpoint, SystemError> {
self.remote
.lock()
.as_ref()
.cloned()
.ok_or(SystemError::ENOTCONN)
}

pub fn connect(&self, remote: smoltcp::wire::IpEndpoint) {
self.remote.lock().replace(remote);
}
Expand Down Expand Up @@ -165,10 +170,6 @@ impl BoundUdp {
socket.close();
});
}

pub fn local_endpoint(&self) -> smoltcp::wire::IpEndpoint {
self.local_endpoint
}
}

// Udp Inner 负责其内部资源管理
Expand Down
21 changes: 17 additions & 4 deletions kernel/src/net/socket/inet/datagram/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,9 +128,10 @@ impl UdpSocket {
{
let mut inner_guard = self.inner.write();
let inner = match inner_guard.take().expect("Udp Inner is None") {
// TODO: 此处会为空,需要DEBUG
UdpInner::Bound(bound) => bound,
UdpInner::Unbound(unbound) => unbound
.bind_ephemeral(to.ok_or(SystemError::EADDRNOTAVAIL)?.addr, self.netns())?,
.bind_ephemeral(to.ok_or(SystemError::EDESTADDRREQ)?.addr, self.netns())?,
};
// size = inner.try_send(buf, to)?;
inner_guard.replace(UdpInner::Bound(inner));
Expand Down Expand Up @@ -263,13 +264,25 @@ impl Socket for UdpSocket {
}

fn remote_endpoint(&self) -> Result<Endpoint, SystemError> {
todo!()
match self.inner.read().as_ref().unwrap() {
Copy link

Copilot AI Dec 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The use of unwrap() here could panic if the inner Option is None. While the inner field is initialized as Some(UdpInner::Unbound(...)) in new(), it can be set to None via take() in the close() method (line 93). If remote_endpoint() is called after the socket is closed, this will panic. Consider using a more explicit error handling approach, such as ok_or(SystemError::EBADF)? to return an appropriate error when the socket is closed.

Suggested change
match self.inner.read().as_ref().unwrap() {
match self.inner.read().as_ref().ok_or(SystemError::EBADF)? {

Copilot uses AI. Check for mistakes.
UdpInner::Bound(bound) => Ok(Endpoint::Ip(bound.remote_endpoint()?)),
// TODO: IPv6 support
_ => Err(SystemError::ENOTCONN),
}
}

fn local_endpoint(&self) -> Result<Endpoint, SystemError> {
use smoltcp::wire::{IpAddress::*, IpEndpoint, IpListenEndpoint};
match self.inner.read().as_ref().unwrap() {
Copy link

Copilot AI Dec 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The use of unwrap() here could panic if the inner Option is None. While the inner field is initialized as Some(UdpInner::Unbound(...)) in new(), it can be set to None via take() in the close() method (line 93). If local_endpoint() is called after the socket is closed, this will panic. Consider using a more explicit error handling approach, such as ok_or(SystemError::EBADF)? to return an appropriate error when the socket is closed.

Suggested change
match self.inner.read().as_ref().unwrap() {
match self.inner.read().as_ref().ok_or(SystemError::EBADF)? {

Copilot uses AI. Check for mistakes.
UdpInner::Bound(bound) => Ok(Endpoint::Ip(bound.local_endpoint())),
_ => Err(SystemError::ENOTCONN),
UdpInner::Bound(bound) => {
let IpListenEndpoint { addr, port } = bound.endpoint();
Ok(Endpoint::Ip(IpEndpoint::new(
addr.unwrap_or(Ipv4([0, 0, 0, 0].into())),
port,
)))
}
// TODO: IPv6 support
_ => Ok(Endpoint::Ip(IpEndpoint::new(Ipv4([0, 0, 0, 0].into()), 0))),
Copy link

Copilot AI Dec 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The hardcoded IPv4 address [0, 0, 0, 0] and port 0 create magic numbers. Consider using a named constant like UNSPECIFIED_LOCAL_ENDPOINT_V4 (defined at kernel/src/net/socket/inet/mod.rs:32-33) for better maintainability and consistency with the TCP implementation.

Copilot uses AI. Check for mistakes.
}
}

Expand Down
8 changes: 8 additions & 0 deletions user/apps/tests/syscall/gvisor/blocklists/socket_test
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
SocketTest.UnixSocketPairProtocol
# SocketTest.ProtocolInet # Only Inet sockets are enabled
SocketTest.ProtocolUnix
SocketTest.UnixSocketStat
SocketTest.UnixSocketStatFS
SocketTest.UnixSCMRightsOnlyPassedOnce
SocketTest.Permission
OpenModes/*.Unix/*
81 changes: 81 additions & 0 deletions user/apps/tests/syscall/gvisor/blocklists/udp_socket_test
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
**/1
**/2
UdpInet6SocketTest.ConnectInet4Sockaddr
# **.Creation
# **.Getsockname
# **.Getpeername
**.SendNotConnected/**
**.ConnectBinds/**
**.ReceiveNotBound/**
**.Bind/**
**.BindInUse/**
**.ConnectWriteToInvalidPort/**
**.ConnectSimultaneousWriteToInvalidPort/**
**.ReceiveAfterConnect/**
**.ReceiveAfterDisconnect/**
**.Connect/**
**.ConnectAnyZero/**
**.ConnectAnyWithPort/**
**.DisconnectAfterConnectAny/**
**.DisconnectAfterConnectAnyWithPort/**
**.DisconnectAfterBind/**
**.DisconnectAfterBindToUnspecAndConnect/**
**.DisconnectAfterConnectWithoutBind/**
**.BindToAnyConnnectToLocalhost/**
**.DisconnectAfterBindToAny/**
**.Disconnect/**
**.ConnectBadAddress/**
**.SendToAddressOtherThanConnected/**
**.ConnectAndSendNoReceiver/**
**.RecvErrorConnRefusedOtherAFSockOpt/**
**.RecvErrorConnRefused/**
**.ZerolengthWriteAllowed/**
**.ZerolengthWriteAllowedNonBlockRead/**
**.SendAndReceiveNotConnected/**
**.SendAndReceiveConnected/**
**.ReceiveFromNotConnected/**
**.ReceiveBeforeConnect/**
**.ReceiveFrom/**
**.Listen/**
**.Accept/**
**.ReadShutdownNonblockPendingData/**
**.ReadShutdownSameSocketResetsShutdownState/**
**.ReadShutdown/**
**.ReadShutdownDifferentThread/**
**.WriteShutdown/**
**.SynchronousReceive/**
**.BoundaryPreserved_SendRecv/**
**.BoundaryPreserved_WritevReadv/**
**.BoundaryPreserved_SendMsgRecvMsg/**
**.FIONREADShutdown/**
**.FIONREADWriteShutdown/**
**.Fionread/**
**.FIONREADZeroLengthPacket/**
**.FIONREADZeroLengthWriteShutdown/**
**.SoNoCheckOffByDefault/**
**.SoNoCheck/**
**.ErrorQueue/**
**.SoTimestampOffByDefault/**
**.SoTimestamp/**
**.WriteShutdownNotConnected/**
**.TimestampIoctl/**
**.TimestampIoctlNothingRead/**
**.TimestampIoctlPersistence/**
**.RecvBufLimitsEmptyRcvBuf/**
**.RecvBufLimits/**
**.SetSocketDetachFilter/**
**.SetSocketDetachFilterNoInstalledFilter/**
**.GetSocketDetachFilter/**
**.SendToZeroPort/**
**.ConnectToZeroPortUnbound/**
**.ConnectToZeroPortBound/**
**.ConnectToZeroPortConnected/**
**.SetAndReceiveTOSOrTClass/**
**.SendAndReceiveTOSorTClass/**
**.SetAndReceiveTTLOrHopLimit/**
**.SendAndReceiveTTLOrHopLimit/**
**.SetAndReceivePktInfo/**
**.SendPacketLargerThanSendBufOnNonBlockingSocket/**
**.ReadShutdownOnBoundSocket/**
**.ReconnectDoesNotClearReadShutdown/**
**.ReconnectDoesNotClearWriteShutdown/**
2 changes: 2 additions & 0 deletions user/apps/tests/syscall/gvisor/whitelist.txt
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ madvise_test
#bind_test
#connect_test
#listen_test
socket_test
udp_socket_test

# 信号处理测试
sigaction_test
Expand Down
Loading