good point.

On 16/03/2013, at 10:17 PM, Maxime Villard <rusty...@gmx.fr> wrote:

> Le 15/03/2013 12:17, David Gwynne a écrit :
>> 
>> On 15/03/2013, at 9:02 AM, Maxime Villard <rusty...@gmx.fr> wrote:
>> 
>>> Hi,
>>> there is a huge bug in the tftp daemon.
>> 
>> sure, but then there's also a huge bug in your fix by your own definition of 
>> huge.
>> when oack fails it frees the client struct and everything hanging off it. 
>> now you avoid the unconditional free of of the client options straight after 
>> oack is called by going to the error label if oack fails, which calls nak, 
>> which then uses client and tries to free it again.
>> 
> 
> 
> No. I removed the call to client_free() in oack():
> 
> -     client_free(client);
> +     return -1;
> 
> There is no double-free in my patch. But I saw you committed
> fixes, so ok now.
> 
> 
>> nice find though. i'll put a fix in shortly which will look very much like 
>> yours.
>> 
>> dlg
>> 
>> 
>>> 
>>> -- /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