From 9f8f5e0d45165b99d0f3ce9bf37382738e0bddb7 Mon Sep 17 00:00:00 2001 From: Michael Chang Date: Thu, 17 Jul 2025 13:29:48 +0800 Subject: [PATCH 4/4] tftp: Fix hang when file is a directory We observed an issue when accessing a remote directory via TFTP: GRUB hangs indefinitely instead of returning an error immediately. The packet capture [1] shows that the server doesn't send an error packet right after the RRQ. Instead, it first replies with an OACK indicating tsize=22, as if the target were a regular file. After GRUB sends its ACK, the server then sends an error "Is a directory". GRUB ignores this delayed error and hangs while waiting for data that never arrives. This happens because GRUB currently assumes any error must follow the RRQ immediately, before data transfer begins. Once it receives an OACK, it switches to expecting data packets and neglects any error that arrives afterward. To work around this, we detect an error on block 0 immediately after OACK, set the eof and stall flags to break out of the receive loop, and close the socket (so that tftp_close() won't send another error). GRUB will then report the error and exit properly. [1] Source Destination Info 192.168.100.122 192.168.100.2 Read Request, File: /grub/i386-pc, Transfer type: octet, blksize=1024, tsize=0 192.168.100.2 192.168.100.122 Option Acknowledgement, blksize=1024, tsize=22 192.168.100.122 192.168.100.2 Acknowledgement, Block: 0 192.168.100.2 192.168.100.122 Error Code, Code: Not defined, Message: Is a directory Signed-off-by: Michael Chang --- grub-core/net/tftp.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/grub-core/net/tftp.c b/grub-core/net/tftp.c index 409b1d09b..93452fe3b 100644 --- a/grub-core/net/tftp.c +++ b/grub-core/net/tftp.c @@ -250,6 +250,14 @@ tftp_receive (grub_net_udp_socket_t sock __attribute__ ((unused)), grub_netbuff_free (nb); return GRUB_ERR_NONE; case TFTP_ERROR: + if (data->have_oack == 1 && data->block == 0) + { + file->device->net->eof = 1; + file->device->net->stall = 1; + /* Do not send closed error code back to server in tftp_close() */ + grub_net_udp_close (data->sock); + data->sock = NULL; + } data->have_oack = 1; grub_error (GRUB_ERR_IO, "%s", tftph->u.err.errmsg); grub_error_save (&data->save_err); -- 2.50.0