Hi, there is a huge bug in the tftp daemon. -- /usr/src/usr.sbin/tftpd/tftpd.c -- In tftp_open() on line 870, the daemon checks for options (OACK) and handle them through the oack() function. Then, it frees and NULLs the variable client->options.
oack() - l.1390 - does some stuff, and if an error happens, client_free() is called with the client structure, which frees this structure, including client->options, but does not null it. So, when returning after an error, client->options is freed twice, causing a double-free and a crash. I succeeded in making the server crash, by changing the tsize and blksize options before transferring a small file in localhost. In fact, if you look on line 1432, you'll see that you just have to close the socket to make the server crash, just after sending the OACK packet. Typically, I just have to do: $ tftp localhost tftp> tsize 2 Tsize option on. tftp> blksize 10 tftp> get test [...wait ~ 10 sec...] tftpd in free(): error: chunk is already free 0x1a658ca65f40 Here is a patch. I switched oack() to int, so that we can handle errors and call nak() to alert the client before closing the connection. nak() frees the client structure, so all goes well. Ok/Comments? Index: tftpd.c =================================================================== RCS file: /cvs/src/usr.sbin/tftpd/tftpd.c,v retrieving revision 1.8 diff -u -r1.8 tftpd.c --- tftpd.c 13 Jul 2012 02:31:46 -0000 1.8 +++ tftpd.c 15 Mar 2013 00:36:40 -0000 @@ -168,7 +168,7 @@ void tftp(struct tftp_client *, struct tftphdr *, size_t); void tftp_open(struct tftp_client *, const char *); void nak(struct tftp_client *, int); -void oack(struct tftp_client *); +int oack(struct tftp_client *); void oack_done(int, short, void *); void sendfile(struct tftp_client *); @@ -876,8 +876,8 @@ goto error; if (client->options) { - oack(client); - + if (oack(client) == -1) + goto error; free(client->options); client->options = NULL; } else if (client->opcode == WRQ) { @@ -1386,7 +1386,7 @@ /* * Send an oack packet (option acknowledgement). */ -void +int oack(struct tftp_client *client) { struct opt_client *options = client->options; @@ -1436,10 +1436,10 @@ oack_done, client); event_add(&client->sev, &client->tv); - return; + return 0; error: - client_free(client); + return -1; } int