1
0
mirror of https://github.com/rkd77/elinks.git synced 2024-09-28 03:06:20 -04:00

Bug 1053: Fix crash when download ends prematurely.

Call stacks reported by valgrind:

==14702==    at 0x80DD791: read_from_socket (socket.c:945)
==14702==    by 0x8104D0C: read_more_http_data (http.c:1180)
==14702==    by 0x81052FE: read_http_data (http.c:1388)
==14702==    by 0x80DD69B: read_select (socket.c:910)
==14702==    by 0x80D27AA: select_loop (select.c:307)
==14702==    by 0x80D1ADE: main (main.c:358)
==14702==  Address 0x4F4E598 is 56 bytes inside a block of size 81 free'd
==14702==    at 0x402210F: free (vg_replace_malloc.c:233)
==14702==    by 0x812BED8: debug_mem_free (memdebug.c:484)
==14702==    by 0x80D7C82: done_connection (connection.c:479)
==14702==    by 0x80D8A44: abort_connection (connection.c:769)
==14702==    by 0x80D99CE: cancel_download (connection.c:1053)
==14702==    by 0x8110EB6: abort_download (download.c:143)
==14702==    by 0x81115BC: download_data_store (download.c:337)
==14702==    by 0x8111AFB: download_data (download.c:446)
==14702==    by 0x80D7B33: notify_connection_callbacks (connection.c:458)
==14702==    by 0x80D781E: set_connection_state (connection.c:388)
==14702==    by 0x80D7132: set_connection_socket_state (connection.c:234)
==14702==    by 0x80DD78D: read_from_socket (socket.c:943)

read_from_socket() attempted to read socket->fd in order to set
handlers on it, but the socket had already been freed.  Incidentally,
socket->fd was -1, which would have resulted in an assertion failure
if valgrind hadn't caught the bug first.

To fix this, add a list of weak references to sockets.
read_from_socket() registers a weak reference on entry and unregisters
it before exit.  done_socket() breaks any weak references to the
specified socket.  read_from_socket() then checks whether the weak
reference was broken, and doesn't access the socket any more if so.
This commit is contained in:
Kalle Olavi Niemitalo 2008-10-04 13:46:43 +03:00 committed by Kalle Olavi Niemitalo
parent bda58a124a
commit 00f5831812

View File

@ -69,6 +69,16 @@ struct connect_info {
struct uri *uri; /* For updating the blacklist. */ struct uri *uri; /* For updating the blacklist. */
}; };
/** For detecting whether a struct socket has been deleted while a
* function was using it. */
struct socket_weak_ref {
LIST_HEAD(struct socket_weak_ref);
/** done_socket() resets this to NULL. */
struct socket *socket;
};
static INIT_LIST_OF(struct socket_weak_ref, socket_weak_refs);
/* To enable logging of tranfers, for debugging purposes. */ /* To enable logging of tranfers, for debugging purposes. */
#if 0 #if 0
@ -143,6 +153,8 @@ init_socket(void *conn, struct socket_operations *ops)
void void
done_socket(struct socket *socket) done_socket(struct socket *socket)
{ {
struct socket_weak_ref *ref;
close_socket(socket); close_socket(socket);
if (socket->connect_info) if (socket->connect_info)
@ -150,6 +162,11 @@ done_socket(struct socket *socket)
mem_free_set(&socket->read_buffer, NULL); mem_free_set(&socket->read_buffer, NULL);
mem_free_set(&socket->write_buffer, NULL); mem_free_set(&socket->write_buffer, NULL);
foreach(ref, socket_weak_refs) {
if (ref->socket == socket)
ref->socket = NULL;
}
} }
void void
@ -935,13 +952,26 @@ void
read_from_socket(struct socket *socket, struct read_buffer *buffer, read_from_socket(struct socket *socket, struct read_buffer *buffer,
struct connection_state state, socket_read_T done) struct connection_state state, socket_read_T done)
{ {
const int is_buffer_new = (buffer != socket->read_buffer);
struct socket_weak_ref ref;
select_handler_T write_handler; select_handler_T write_handler;
ref.socket = socket;
add_to_list(socket_weak_refs, &ref);
buffer->done = done; buffer->done = done;
socket->ops->set_timeout(socket, connection_state(0)); socket->ops->set_timeout(socket, connection_state(0));
socket->ops->set_state(socket, state); socket->ops->set_state(socket, state);
del_from_list(&ref);
if (ref.socket == NULL) {
/* socket->ops->set_state deleted the socket. */
if (is_buffer_new)
mem_free(buffer);
return;
}
if (socket->read_buffer && buffer != socket->read_buffer) if (socket->read_buffer && buffer != socket->read_buffer)
mem_free(socket->read_buffer); mem_free(socket->read_buffer);
socket->read_buffer = buffer; socket->read_buffer = buffer;