/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */ /* Copyright (C) 2017 Red Hat, Inc. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, see . */ /* * This test allocate and free some resources in order to be able to detect * leaks using a leak detector. * * To use with GCC/Clang address sanitizer you can set or add these options: * CFLAGS="-fsanitize=address -fno-omit-frame-pointer" * LDFLAGS="-fsanitize=address -lasan" * Note that early GCC address sanitizer don't have a leak detector. * * To use Valgrind you can run the test with: * valgrind --tool=memcheck --leak-check=full ./test-leaks * For cleaner output you should suppress GLib checks with glib.supp file. */ #include #include #include #include #include "test-glib-compat.h" #include "basic-event-loop.h" #include "test-display-base.h" #include "sys-socket.h" #define PKI_DIR SPICE_TOP_SRCDIR "/server/tests/pki/" static void server_leaks(void) { int result; SpiceCoreInterface *core; SpiceServer *server = spice_server_new(); int sv[2]; g_assert_nonnull(server); core = basic_event_loop_init(); g_assert_nonnull(core); result = spice_server_set_tls(server, 5922, PKI_DIR "ca-cert.pem", PKI_DIR "server-cert.pem", PKI_DIR "server-key.pem", NULL, PKI_DIR "dhparams.pem", NULL); g_assert_cmpint(result, ==, 0); g_assert_cmpint(spice_server_init(server, core), ==, 0); /* cause the allocation of spice name */ spice_server_set_name(server, "Test Spice Name"); /* cause the allocation of security options */ result = spice_server_set_channel_security(server, "main", SPICE_CHANNEL_SECURITY_SSL); g_assert_cmpint(result, ==, 0); /* spice_server_add_ssl_client should not leak when it's given a disconnected socket */ #if (OPENSSL_VERSION_NUMBER >= 0x30000000L) /* Discard the OpenSSL-generated error logs */ g_test_expect_message(G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, "*error:*:SSL*"); #endif g_test_expect_message(G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, "*SSL_accept failed*"); g_assert_cmpint(socketpair(AF_LOCAL, SOCK_STREAM, 0, sv), ==, 0); socket_close(sv[1]); result = spice_server_add_ssl_client(server, sv[0], 1); g_assert_cmpint(result, ==, -1); /* if the function fails, it should not close the socket */ g_assert_cmpint(socket_close(sv[0]), ==, 0); spice_server_destroy(server); basic_event_loop_destroy(); } static int vmc_write(SPICE_GNUC_UNUSED SpiceCharDeviceInstance *sin, SPICE_GNUC_UNUSED const uint8_t *buf, int len) { return len; } static int vmc_read(SPICE_GNUC_UNUSED SpiceCharDeviceInstance *sin, SPICE_GNUC_UNUSED uint8_t *buf, SPICE_GNUC_UNUSED int len) { return 0; } static void vmc_state(SPICE_GNUC_UNUSED SpiceCharDeviceInstance *sin, SPICE_GNUC_UNUSED int connected) { } static SpiceCharDeviceInterface vmc_interface = { .base = { .type = SPICE_INTERFACE_CHAR_DEVICE, .description = "test spice virtual channel char device", .major_version = SPICE_INTERFACE_CHAR_DEVICE_MAJOR, .minor_version = SPICE_INTERFACE_CHAR_DEVICE_MINOR, }, .state = vmc_state, .write = vmc_write, .read = vmc_read, }; static SpiceCharDeviceInstance vmc_instance; static void vmc_leaks(void) { SpiceCoreInterface *core = basic_event_loop_init(); Test *test = test_new(core); int status; vmc_instance.subtype = "usbredir"; vmc_instance.base.sif = &vmc_interface.base; spice_server_add_interface(test->server, &vmc_instance.base); status = spice_server_remove_interface(&vmc_instance.base); g_assert_cmpint(status, ==, 0); vmc_instance.subtype = "port"; vmc_instance.portname = "org.spice-space.webdav.0"; vmc_instance.base.sif = &vmc_interface.base; spice_server_add_interface(test->server, &vmc_instance.base); status = spice_server_remove_interface(&vmc_instance.base); g_assert_cmpint(status, ==, 0); vmc_instance.subtype = "port"; vmc_instance.portname = "default_port"; vmc_instance.base.sif = &vmc_interface.base; spice_server_add_interface(test->server, &vmc_instance.base); status = spice_server_remove_interface(&vmc_instance.base); g_assert_cmpint(status, ==, 0); test_destroy(test); basic_event_loop_destroy(); } static void migrate_cb(SpiceMigrateInstance *sin) { } static const SpiceMigrateInterface migrate_interface = { .base = { .type = SPICE_INTERFACE_MIGRATION, .description = "migration", .major_version = SPICE_INTERFACE_MIGRATION_MAJOR, .minor_version = SPICE_INTERFACE_MIGRATION_MINOR, }, .migrate_connect_complete = migrate_cb, .migrate_end_complete = migrate_cb, }; static void migration_leaks(void) { SpiceCoreInterface *core; SpiceServer *server = spice_server_new(); SpiceMigrateInstance migrate; g_assert_nonnull(server); core = basic_event_loop_init(); g_assert_nonnull(core); g_assert_cmpint(spice_server_init(server, core), ==, 0); migrate.base.sif = &migrate_interface.base; spice_server_add_interface(server, &migrate.base); spice_server_destroy(server); basic_event_loop_destroy(); } static void tablet_set_logical_size(SpiceTabletInstance* sin, int width, int height) { } static void tablet_position(SpiceTabletInstance* sin, int x, int y, uint32_t buttons_state) { } static void tablet_wheel(SpiceTabletInstance* sin, int wheel, uint32_t buttons_state) { } static void tablet_buttons(SpiceTabletInstance *sin, uint32_t buttons_state) { } static const SpiceTabletInterface tablet_interface = { .base = { .type = SPICE_INTERFACE_TABLET, .description = "tablet", .major_version = SPICE_INTERFACE_TABLET_MAJOR, .minor_version = SPICE_INTERFACE_TABLET_MINOR, }, .set_logical_size = tablet_set_logical_size, .position = tablet_position, .wheel = tablet_wheel, .buttons = tablet_buttons, }; static void tablet_leaks(void) { SpiceCoreInterface *core; SpiceServer *server; SpiceTabletInstance tablet; core = basic_event_loop_init(); g_assert_nonnull(core); // test if leaks without spice_server_remove_interface server = spice_server_new(); g_assert_nonnull(server); g_assert_cmpint(spice_server_init(server, core), ==, 0); tablet.base.sif = &tablet_interface.base; spice_server_add_interface(server, &tablet.base); spice_server_destroy(server); // test if leaks with spice_server_remove_interface server = spice_server_new(); g_assert_nonnull(server); g_assert_cmpint(spice_server_init(server, core), ==, 0); tablet.base.sif = &tablet_interface.base; spice_server_add_interface(server, &tablet.base); spice_server_remove_interface(&tablet.base); spice_server_destroy(server); basic_event_loop_destroy(); } int main(int argc, char *argv[]) { g_test_init(&argc, &argv, NULL); g_test_add_func("/server/server leaks", server_leaks); g_test_add_func("/server/vmc leaks", vmc_leaks); g_test_add_func("/server/migration leaks", migration_leaks); g_test_add_func("/server/tablet leaks", tablet_leaks); return g_test_run(); }