Summary

  • A delayed TFTP acknowledgement could make both endpoints retransmit the packets they currently held, turning one timeout into persistent duplicate traffic.
  • RFC 1123 required the DATA sender to ignore duplicate ACKs; RFC 1350 later recorded the 1992 repair. The bug threatened completion through congestion, not the correctness of a file that arrived.

A protocol that remembered only one packet

In June 1981, Karen Sollins published the second revision of the Trivial File Transfer Protocol. The word “trivial” described the interface, not the network conditions it had to survive. A client could request a read or write by filename and mode. TFTP did not offer a directory listing, and its specification provided no user authentication. Its appeal was that an implementation could be small. Each data packet carried at most 512 bytes, and the receiver acknowledged one block before the sender sent the next. The sender therefore needed to keep only its current packet for retry.

Stop-and-wait also supplied flow control and removed the need to reorder data blocks.

That bargain was useful in places where a large protocol stack was a liability. A host needed to receive its operating system before it could behave like a fully configured machine. The Internet host requirements published in 1989 describe TFTP’s principal use as bootstrapping over a local network: the protocol was small enough to fit in EPROM. BOOTP could provide configuration; TFTP could then fetch the image. The two protocols solved adjacent problems, but the file-transfer step still depended on timing and acknowledgements.

TFTP ran over UDP, so it supplied its own reliability. There was no transport connection quietly absorbing a lost packet. If an expected response did not arrive before the timer expired, a sender could retransmit the packet it was waiting on. This was simple until a packet was delayed rather than lost.

How one old ACK could keep making new duplicates

Imagine host A has received acknowledgement X−1 and sends DATA X. Host B receives it and replies ACK X, but the acknowledgement is delayed. A’s timer expires, so A retransmits DATA X. B has already accepted X. It recognizes the repeated block and sends ACK X again. When the delayed first ACK finally arrives, A advances and sends DATA X+1. Then the second ACK X arrives. Under the original rule, A responds to this old acknowledgement by retransmitting the DATA packet it currently has: X+1.

B receives X+1 twice and sends ACK X+1 twice. If one of those acknowledgements arrives after A has advanced to DATA X+2, the remaining old ACK can make A repeat X+2. The same pattern then moves forward with the transfer. The stale packet does not replay an old block in isolation; it prompts a response based on each endpoint’s current state. A duplicated packet can therefore induce a fresh duplicate from the other side.

Robert Braden’s RFC 1123 called this the Sorcerer’s Apprentice syndrome. Its example shows the remainder of the transfer being sent in duplicate unless a datagram is lost and breaks the repetition. The problem was not that TFTP’s file contents became wrong. RFC 1123 says that a completed transfer remains correct. The risk was that repeated packets consumed time and capacity until the transfer timed out instead of completing.

That distinction matters because “reliable” did not mean the same thing as “immune to delay.” TFTP’s sequence numbers could identify the block being acknowledged; they did not make every response to an old block harmless. The protocol’s symmetric behavior let a DATA sender and an ACK sender both answer duplicates by repeating their current packet. Each retry looked locally reasonable. Together, they formed a feedback loop.

The delay that feeds the congestion

The 1989 requirements document made the failure mechanism explicit. A delayed acknowledgement can arrive after a timeout has already caused a retransmission. The repeated DATA generates another ACK. If the earlier ACK arrives after the sender has advanced, the sender may repeat the new DATA block, which generates more ACKs. Unless a packet loss interrupts the chain, later blocks may travel in duplicate.

Congestion can be both the trigger and the result. A crowded path delays a packet long enough to start the cycle. Duplicate DATA and ACKs then occupy more of that path, making further delays more likely. The RFC warns that this can produce additional congestion and more delayed packets. This is not a claim that every slow TFTP transfer entered the syndrome, or that a particular historical network outage was caused by it. It is a protocol-level positive feedback mechanism documented in the host requirements.

RFC 1123 also separated this issue from TFTP’s ordinary timeout policy. An adaptive timeout was required, with exponential backoff recommended as a minimum. Better timer values help a sender tolerate variable delay, but they do not remove the rule that lets an old ACK trigger another DATA packet. The repair had to change what the sender did after it saw a duplicate acknowledgement.

A small rule before a new revision

RFC 1123 made the fix mandatory: the side sending DATA must never resend its current DATA packet when it receives a duplicate ACK. Once that response is suppressed, a repeated ACK no longer causes the next duplicate DATA packet, and the loop cannot carry itself forward. The receiver’s own retransmission timer can then be removed because retransmitting an ACK will not trigger action. RFC 1123 says it may remain if a repeated acknowledgement could replace one that was genuinely lost; the DATA sender still needs a retransmission timer.

That was a useful maintenance outcome for a protocol meant to live in a boot program. A receiver can be simpler without a timer, provided the sender follows the corrected rule. The fix did not require enlarging the packet window, changing the 512-byte block, or replacing UDP. It broke the dangerous feedback path while preserving the one-packet memory model.

In July 1992, RFC 1350 replaced RFC 783 and became STD 33. Its acknowledgements say that a May 1992 revision by Noel Chiappa fixed the Sorcerer’s Apprentice protocol bug. That sequence is easy to blur: the 1989 host requirements already stated the mandatory implementation behavior, while the 1992 document recorded the correction in the revised TFTP specification. A later specification date is not evidence that implementations were all broken until then, or that every implementation had already been updated in 1989.

Later TFTP extensions pursued a different boundary. The 1998 blocksize option permitted larger blocks, and later options added transfer-size and timeout negotiation; a 2015 window option permitted multiple outstanding blocks. Those changes improved performance where stop-and-wait was inefficient. They should not be mistaken for the duplicate-ACK fix. One change enlarged throughput; the other stopped an old acknowledgement from multiplying current traffic.

TFTP’s history is a reminder that a minimalist design can place its complexity in state transitions instead of packet formats. The original protocol economized on memory and implementation work by keeping one block active. The failure surfaced when delay made the two endpoints’ ideas of “current packet” diverge. The correction was equally small: do not let a duplicate acknowledgement command the DATA sender to repeat what it is now sending. Reliability depended not just on retries, but on knowing when a retry had stopped being useful.

Sources