mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-09-09 08:17:53 -06:00
char: fix use-after-free with dup chardev & reconnect
With a reconnect socket, qemu_char_open() will start a background thread. It should keep a reference on the chardev. Fixes invalid read: READ of size 8 at 0x6040000ac858 thread T7 #0 0x5555598d37b8 in unix_connect_saddr /home/elmarco/src/qq/util/qemu-sockets.c:954 #1 0x5555598d4751 in socket_connect /home/elmarco/src/qq/util/qemu-sockets.c:1109 #2 0x555559707c34 in qio_channel_socket_connect_sync /home/elmarco/src/qq/io/channel-socket.c:145 #3 0x5555596adebb in tcp_chr_connect_client_task /home/elmarco/src/qq/chardev/char-socket.c:1104 #4 0x555559723d55 in qio_task_thread_worker /home/elmarco/src/qq/io/task.c:123 #5 0x5555598a6731 in qemu_thread_start /home/elmarco/src/qq/util/qemu-thread-posix.c:519 #6 0x7ffff40d4431 in start_thread (/lib64/libpthread.so.0+0x9431) #7 0x7ffff40029d2 in __clone (/lib64/libc.so.6+0x1019d2) Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> Message-Id: <20200420112012.567284-1-marcandre.lureau@redhat.com>
This commit is contained in:
parent
14a7a20306
commit
6806601969
2 changed files with 54 additions and 3 deletions
|
@ -1129,7 +1129,8 @@ static void tcp_chr_connect_client_async(Chardev *chr)
|
||||||
*/
|
*/
|
||||||
s->connect_task = qio_task_new(OBJECT(sioc),
|
s->connect_task = qio_task_new(OBJECT(sioc),
|
||||||
qemu_chr_socket_connected,
|
qemu_chr_socket_connected,
|
||||||
chr, NULL);
|
object_ref(OBJECT(chr)),
|
||||||
|
(GDestroyNotify)object_unref);
|
||||||
qio_task_run_in_thread(s->connect_task,
|
qio_task_run_in_thread(s->connect_task,
|
||||||
tcp_chr_connect_client_task,
|
tcp_chr_connect_client_task,
|
||||||
s->addr,
|
s->addr,
|
||||||
|
|
|
@ -904,6 +904,52 @@ typedef struct {
|
||||||
char_socket_cb event_cb;
|
char_socket_cb event_cb;
|
||||||
} CharSocketClientTestConfig;
|
} CharSocketClientTestConfig;
|
||||||
|
|
||||||
|
static void char_socket_client_dupid_test(gconstpointer opaque)
|
||||||
|
{
|
||||||
|
const CharSocketClientTestConfig *config = opaque;
|
||||||
|
QIOChannelSocket *ioc;
|
||||||
|
char *optstr;
|
||||||
|
Chardev *chr1, *chr2;
|
||||||
|
SocketAddress *addr;
|
||||||
|
QemuOpts *opts;
|
||||||
|
Error *local_err = NULL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Setup a listener socket and determine get its address
|
||||||
|
* so we know the TCP port for the client later
|
||||||
|
*/
|
||||||
|
ioc = qio_channel_socket_new();
|
||||||
|
g_assert_nonnull(ioc);
|
||||||
|
qio_channel_socket_listen_sync(ioc, config->addr, 1, &error_abort);
|
||||||
|
addr = qio_channel_socket_get_local_address(ioc, &error_abort);
|
||||||
|
g_assert_nonnull(addr);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Populate the chardev address based on what the server
|
||||||
|
* is actually listening on
|
||||||
|
*/
|
||||||
|
optstr = char_socket_addr_to_opt_str(addr,
|
||||||
|
config->fd_pass,
|
||||||
|
config->reconnect,
|
||||||
|
false);
|
||||||
|
|
||||||
|
opts = qemu_opts_parse_noisily(qemu_find_opts("chardev"),
|
||||||
|
optstr, true);
|
||||||
|
g_assert_nonnull(opts);
|
||||||
|
chr1 = qemu_chr_new_from_opts(opts, NULL, &error_abort);
|
||||||
|
g_assert_nonnull(chr1);
|
||||||
|
|
||||||
|
chr2 = qemu_chr_new_from_opts(opts, NULL, &local_err);
|
||||||
|
g_assert_null(chr2);
|
||||||
|
error_free_or_abort(&local_err);
|
||||||
|
|
||||||
|
object_unref(OBJECT(ioc));
|
||||||
|
qemu_opts_del(opts);
|
||||||
|
object_unparent(OBJECT(chr1));
|
||||||
|
qapi_free_SocketAddress(addr);
|
||||||
|
g_free(optstr);
|
||||||
|
}
|
||||||
|
|
||||||
static void char_socket_client_test(gconstpointer opaque)
|
static void char_socket_client_test(gconstpointer opaque)
|
||||||
{
|
{
|
||||||
const CharSocketClientTestConfig *config = opaque;
|
const CharSocketClientTestConfig *config = opaque;
|
||||||
|
@ -1470,6 +1516,8 @@ int main(int argc, char **argv)
|
||||||
static CharSocketClientTestConfig client7 ## name = \
|
static CharSocketClientTestConfig client7 ## name = \
|
||||||
{ addr, ",reconnect=1", true, false, \
|
{ addr, ",reconnect=1", true, false, \
|
||||||
char_socket_event_with_error }; \
|
char_socket_event_with_error }; \
|
||||||
|
static CharSocketClientTestConfig client8 ## name = \
|
||||||
|
{ addr, ",reconnect=1", false, false, char_socket_event }; \
|
||||||
g_test_add_data_func("/char/socket/client/mainloop/" # name, \
|
g_test_add_data_func("/char/socket/client/mainloop/" # name, \
|
||||||
&client1 ##name, char_socket_client_test); \
|
&client1 ##name, char_socket_client_test); \
|
||||||
g_test_add_data_func("/char/socket/client/wait-conn/" # name, \
|
g_test_add_data_func("/char/socket/client/wait-conn/" # name, \
|
||||||
|
@ -1483,7 +1531,9 @@ int main(int argc, char **argv)
|
||||||
g_test_add_data_func("/char/socket/client/wait-conn-fdpass/" # name, \
|
g_test_add_data_func("/char/socket/client/wait-conn-fdpass/" # name, \
|
||||||
&client6 ##name, char_socket_client_test); \
|
&client6 ##name, char_socket_client_test); \
|
||||||
g_test_add_data_func("/char/socket/client/reconnect-error/" # name, \
|
g_test_add_data_func("/char/socket/client/reconnect-error/" # name, \
|
||||||
&client7 ##name, char_socket_client_test)
|
&client7 ##name, char_socket_client_test); \
|
||||||
|
g_test_add_data_func("/char/socket/client/dupid-reconnect/" # name, \
|
||||||
|
&client8 ##name, char_socket_client_dupid_test)
|
||||||
|
|
||||||
if (has_ipv4) {
|
if (has_ipv4) {
|
||||||
SOCKET_SERVER_TEST(tcp, &tcpaddr);
|
SOCKET_SERVER_TEST(tcp, &tcpaddr);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue