After reading your description, I was expecting the patch to include a line 
setting options to null after freeing it. Whatever else we do, shouldn't we do 
that too?


On Fri, Mar 15, 2013 at 00:02, Maxime Villard wrote:
> 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

Reply via email to