Package: syslinux
Version: 3:6.04~git20190206.bf6db5b4+dfsg1-3.2
Severity: normal
Tags: patch upstream
X-Debbugs-Cc: [email protected]
lpxelinux's UDP allocator immediately reuses a closed socket's port.
A delayed TFTP error for a missing config can then reach a later request,
causing an existing config to be treated as missing.
Reproduced in QEMU by delaying a missing-file error until the request for
pxelinux.cfg/default. Keeping an allocator cursor avoids the collision;
the same test then loads default successfully. Allocation also handles
occupied ports, wraparound and exhaustion.
Patch: 0022-lpxelinux-rotate-udp-ports.patch
AI assistance was used for debugging, the patch and this report.
-- System Information:
Debian Release: forky/sid
APT prefers unstable
APT policy: (500, 'unstable'), (1, 'experimental')
Architecture: amd64 (x86_64)
Kernel: Linux 7.2.15+deb14-amd64 (SMP w/12 CPU threads; PREEMPT)
Kernel taint flags: TAINT_PROPRIETARY_MODULE, TAINT_USER, TAINT_OOT_MODULE,
TAINT_UNSIGNED_MODULE
Locale: LANG=en_US.UTF-8, LC_CTYPE=en_US.UTF-8 (charmap=UTF-8),
LANGUAGE=en_US:en
Shell: /bin/sh linked to /usr/bin/dash
Init: systemd (via /run/systemd/system)
Versions of packages syslinux depends on:
ii libc6 2.43-7
ii mtools 4.0.49-1
Versions of packages syslinux recommends:
ii syslinux-common 3:6.04~git20190206.bf6db5b4+dfsg1-3.2
Versions of packages syslinux suggests:
ii dosfstools 4.2-1.2
-- no debconf information
Description: lpxelinux: avoid immediate reuse of ephemeral UDP ports
Keep an allocator cursor so delayed TFTP replies do not reach the next
file request after its predecessor closes. Check occupied ports and wrap
within the configured range.
Origin: other, local debugging with AI assistance
Forwarded: no
Last-Update: 2026-11-03
--- a/core/lwip/src/core/udp.c
+++ b/core/lwip/src/core/udp.c
@@ -685,7 +685,7 @@
* @param ipaddr local IP address to bind with. Use IP_ADDR_ANY to
* bind to all local interfaces.
* @param port local UDP port to bind with. Use 0 to automatically bind
- * to a random port between UDP_LOCAL_PORT_RANGE_START and
+ * to an available port between UDP_LOCAL_PORT_RANGE_START and
* UDP_LOCAL_PORT_RANGE_END.
*
* ipaddr & port are expected to be in the same byte order as in the pcb.
@@ -751,24 +751,30 @@
#define UDP_LOCAL_PORT_RANGE_START 0xc000
#define UDP_LOCAL_PORT_RANGE_END 0xffff
#endif
- port = UDP_LOCAL_PORT_RANGE_START;
- ipcb = udp_pcbs;
- while ((ipcb != NULL) && (port != UDP_LOCAL_PORT_RANGE_END)) {
- if (ipcb->local_port == port) {
- /* port is already used by another udp_pcb */
- port++;
- /* restart scanning all udp pcbs */
- ipcb = udp_pcbs;
- } else {
- /* go on with next udp pcb */
- ipcb = ipcb->next;
- }
- }
- if (ipcb != NULL) {
- /* no more ports available in local range */
- LWIP_DEBUGF(UDP_DEBUG, ("udp_bind: out of free UDP ports\n"));
- return ERR_USE;
- }
+ static u16_t next_port = UDP_LOCAL_PORT_RANGE_START;
+ u16_t first_port = next_port;
+
+ /* Do not immediately reuse a closed socket's port: delayed TFTP
+ replies must not be delivered to the next file request. */
+ port = first_port;
+ for (;;) {
+ for (ipcb = udp_pcbs; ipcb != NULL; ipcb = ipcb->next) {
+ if (ipcb->local_port == port)
+ break;
+ }
+ if (ipcb == NULL)
+ break;
+
+ port = port == UDP_LOCAL_PORT_RANGE_END ?
+ UDP_LOCAL_PORT_RANGE_START : port + 1;
+ if (port == first_port) {
+ /* no more ports available in local range */
+ LWIP_DEBUGF(UDP_DEBUG, ("udp_bind: out of free UDP ports\n"));
+ return ERR_USE;
+ }
+ }
+ next_port = port == UDP_LOCAL_PORT_RANGE_END ?
+ UDP_LOCAL_PORT_RANGE_START : port + 1;
}
pcb->local_port = port;
snmp_insert_udpidx_tree(pcb);