Skip to content

Commit 2fe299c

Browse files
committed
don't infinite loop if udp::send methods receive a buffer too large to ever be sent
1 parent 51d87c6 commit 2fe299c

File tree

1 file changed

+26
-2
lines changed

1 file changed

+26
-2
lines changed

embassy-net/src/udp.rs

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,19 @@ pub enum BindError {
2121
NoRoute,
2222
}
2323

24-
/// Error returned by [`UdpSocket::recv_from`] and [`UdpSocket::send_to`].
24+
/// Error returned by [`UdpSocket::send_to`].
2525
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
2626
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
2727
pub enum SendError {
2828
/// No route to host.
2929
NoRoute,
3030
/// Socket not bound to an outgoing port.
3131
SocketNotBound,
32+
/// There is not enough transmit buffer capacity to ever send this packet.
33+
Truncated,
3234
}
3335

34-
/// Error returned by [`UdpSocket::recv_from`] and [`UdpSocket::send_to`].
36+
/// Error returned by [`UdpSocket::recv_from`].
3537
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
3638
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
3739
pub enum RecvError {
@@ -224,6 +226,8 @@ impl<'a> UdpSocket<'a> {
224226
///
225227
/// This method will wait until the datagram has been sent.
226228
///
229+
/// If the socket's send buffer is too small to fit `buf`, this method will return `Poll::Ready(Err(SendError::Truncated))`
230+
///
227231
/// When the remote endpoint is not reachable, this method will return `Err(SendError::NoRoute)`
228232
pub async fn send_to<T>(&self, buf: &[u8], remote_endpoint: T) -> Result<(), SendError>
229233
where
@@ -240,11 +244,21 @@ impl<'a> UdpSocket<'a> {
240244
/// When the socket's send buffer is full, this method will return `Poll::Pending`
241245
/// and register the current task to be notified when the buffer has space available.
242246
///
247+
/// If the socket's send buffer is too small to fit `buf`, this method will return `Poll::Ready(Err(SendError::Truncated))`
248+
///
243249
/// When the remote endpoint is not reachable, this method will return `Poll::Ready(Err(Error::NoRoute))`.
244250
pub fn poll_send_to<T>(&self, buf: &[u8], remote_endpoint: T, cx: &mut Context<'_>) -> Poll<Result<(), SendError>>
245251
where
246252
T: Into<UdpMetadata>,
247253
{
254+
// Don't need to wake waker in `with_mut` if the buffer will never fit the udp tx_buffer.
255+
let send_capacity_too_small = self
256+
.stack
257+
.with(|i| i.sockets.get::<udp::Socket>(self.handle).payload_send_capacity() < buf.len());
258+
if send_capacity_too_small {
259+
return Poll::Ready(Err(SendError::Truncated));
260+
}
261+
248262
self.with_mut(|s, _| match s.send_slice(buf, remote_endpoint) {
249263
// Entire datagram has been sent
250264
Ok(()) => Poll::Ready(Ok(())),
@@ -268,12 +282,22 @@ impl<'a> UdpSocket<'a> {
268282
/// This method will wait until the buffer can fit the requested size before
269283
/// calling the function to fill its contents.
270284
///
285+
/// If the socket's send buffer is too small to fit `size`, this method will return `Poll::Ready(Err(SendError::Truncated))`
286+
///
271287
/// When the remote endpoint is not reachable, this method will return `Err(SendError::NoRoute)`
272288
pub async fn send_to_with<T, F, R>(&mut self, size: usize, remote_endpoint: T, f: F) -> Result<R, SendError>
273289
where
274290
T: Into<UdpMetadata> + Copy,
275291
F: FnOnce(&mut [u8]) -> R,
276292
{
293+
// Don't need to wake waker in `with_mut` if the buffer will never fit the udp tx_buffer.
294+
let send_capacity_too_small = self
295+
.stack
296+
.with(|i| i.sockets.get::<udp::Socket>(self.handle).payload_send_capacity() < size);
297+
if send_capacity_too_small {
298+
return Err(SendError::Truncated);
299+
}
300+
277301
let mut f = Some(f);
278302
poll_fn(move |cx| {
279303
self.with_mut(|s, _| {

0 commit comments

Comments
 (0)