[Spice-devel] [PATCH spice-server v2 7/7] test-smardcard: Improve test coverage
Victor Toso
victortoso at redhat.com
Wed Oct 9 09:20:30 UTC 2019
Hi,
On Tue, Oct 08, 2019 at 06:39:24PM +0100, Frediano Ziglio wrote:
> Using coverage utility exercise more code paths:
> - message from channel with wrong type;
> - remove message from channel with already removed reader;
> - init message from channel (ignored);
> - data from devices, ADPU;
> - error from device;
> - messages split in different ways;
> - invalid reader_id values.
>
> Signed-off-by: Frediano Ziglio <fziglio at redhat.com>
Acked-by: Victor Toso <victortoso at redhat.com>
Nothing important to say, if we need to improve, we can wait till
bugs are hit first. Happy to have this tests, many thanks!
> ---
> server/tests/test-smartcard.c | 207 ++++++++++++++++++++++++++++++----
> server/tests/vmc-emu.c | 3 +
> server/tests/vmc-emu.h | 3 +
> 3 files changed, 193 insertions(+), 20 deletions(-)
>
> diff --git a/server/tests/test-smartcard.c b/server/tests/test-smartcard.c
> index aac2e7941..a3a7a1597 100644
> --- a/server/tests/test-smartcard.c
> +++ b/server/tests/test-smartcard.c
> @@ -41,6 +41,15 @@ static SpiceCoreInterface *core;
> static Test *test;
> static VmcEmu *vmc;
> typedef int TestFixture;
> +static int client_socket = -1;
> +// buffer when data from channel are stored
> +static SpiceBuffer channel_buf;
> +// expected buffer in channel
> +static SpiceBuffer channel_expected;
> +// expected buffer in device
> +static SpiceBuffer device_expected;
> +
> +static void next_test(void);
>
> static void test_smartcard_setup(TestFixture *fixture, gconstpointer user_data)
> {
> @@ -101,7 +110,7 @@ static void send_ack_sync(int socket, uint32_t generation)
> g_assert_cmpint(socket_write(socket, &msg.type, 10), ==, 10);
> }
>
> -static void send_data(int socket, uint32_t type)
> +static void send_data(int socket, uint32_t type, uint32_t reader_id)
> {
> struct {
> uint16_t dummy;
> @@ -114,24 +123,183 @@ static void send_data(int socket, uint32_t type)
> msg.type = GUINT16_TO_LE(SPICE_MSGC_SMARTCARD_DATA);
> msg.len = GUINT32_TO_LE(sizeof(VSCMsgHeader)+6);
> msg.vheader.type = GUINT32_TO_LE(type);
> - msg.vheader.reader_id = 0;
> + msg.vheader.reader_id = GUINT32_TO_LE(reader_id);
> msg.vheader.length = GUINT32_TO_LE(6);
> strcpy(msg.data, "hello");
>
> g_assert_cmpint(socket_write(socket, &msg.type, sizeof(msg)-4), ==, sizeof(msg)-4);
> }
>
> -static void check_data(void *opaque)
> +static void check_data(VmcEmu *vmc)
> +{
> + g_assert_cmpint(device_expected.offset, !=, 0);
> + if (vmc->write_pos < device_expected.offset) {
> + return;
> + }
> + g_assert_cmpint(vmc->write_pos, ==, device_expected.offset);
> + g_assert_true(memcmp(vmc->write_buf, device_expected.buffer, device_expected.offset) == 0);
> +
> + next_test();
> +}
> +
> +static void data_from_channel(int fd, int event, void *opaque)
> +{
> + uint8_t buf[128];
> + ssize_t ret = socket_read(fd, buf, sizeof(buf));
> + if (ret <= 0) {
> + return;
> + }
> + spice_buffer_append(&channel_buf, buf, ret);
> +
> + g_assert_cmpint(channel_expected.offset, !=, 0);
> + if (channel_buf.offset < channel_expected.offset) {
> + return;
> + }
> + g_assert_true(memcmp(channel_buf.buffer, channel_expected.buffer, channel_expected.offset) == 0);
> + spice_buffer_remove(&channel_buf, channel_expected.offset);
> +
> + next_test();
> +}
> +
> +static void next_test(void)
> {
> - static const char expected_buf[] =
> - // forwarded ReaderAdd message, note that payload is stripped
> - "\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00"
> - // forwarded APDU message
> - "\x00\x00\x00\x07\x00\x00\x00\x00\x00\x00\x00\x06\x68\x65\x6c\x6c\x6f\x00";
> - const size_t expected_buf_len = sizeof(expected_buf) - 1;
> - g_assert_cmpint(vmc->write_pos, ==, expected_buf_len);
> - g_assert_true(memcmp(vmc->write_buf, expected_buf, expected_buf_len) == 0);
> - basic_event_loop_quit();
> + static int test_num;
> +
> + test_num++;
> + printf("Executing subtest %d\n", test_num);
> +
> + spice_buffer_reset(&channel_expected);
> + spice_buffer_reset(&device_expected);
> +
> + switch (test_num) {
> + // First test, send some message to channel expecting a reply
> + // for each message we are sending
> + case 1: {
> + static const char expected_buf[] =
> + // forwarded ReaderAdd message, note that payload is stripped
> + "\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00"
> + // forwarded APDU message
> + "\x00\x00\x00\x07\x00\x00\x00\x00\x00\x00\x00\x06\x68\x65\x6c\x6c\x6f\x00"
> + "\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00";
> + spice_buffer_append(&device_expected, expected_buf, sizeof(expected_buf) - 1);
> +
> + send_data(client_socket, VSC_ReaderAdd, 0);
> + send_data(client_socket, VSC_APDU, 0);
> + send_data(client_socket, VSC_ReaderRemove, 0);
> + } break;
> + // Second test, send an init and remove a reader that is not present,
> + // we expect an error for the removal (the Init is ignored)
> + case 2: {
> + static const char expected_buf[] =
> + // forwarded Error message
> + "\x65\x00\x10\x00\x00\x00"
> + "\x02\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x01\x00\x00\x00";
> + spice_buffer_append(&channel_expected, expected_buf, sizeof(expected_buf) - 1);
> +
> + // Init message, ignored
> + send_data(client_socket, VSC_Init, 0);
> + // remove again, this will trigger an error
> + send_data(client_socket, VSC_ReaderRemove, 0);
> + } break;
> + // Third test, APDU messages from device are forwarded to the channel.
> + // We split the header and payload of the first message to check device code can handle it.
> + // The second message is send inside a block with the end of the first to trigger
> + // an hard path in the device code
> + case 3: {
> + static const char expected_buf[] =
> + // forwarded APDU message
> + "\x65\x00\x12\x00\x00\x00"
> + "\x07\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00" "foobaz"
> + "\x65\x00\x12\x00\x00\x00"
> + "\x07\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00" "foobar";
> + spice_buffer_append(&channel_expected, expected_buf, sizeof(expected_buf) - 1);
> +
> + vmc_emu_reset(vmc);
> + // data from device
> + uint8_t *p = vmc->message;
> +
> + // add VSC_APDU message
> + memcpy(p, "\x00\x00\x00\x07\x00\x00\x00\x00\x00\x00\x00\x06" "foobaz", 18);
> + p += 18;
> + memcpy(p, "\x00\x00\x00\x07\x00\x00\x00\x00\x00\x00\x00\x06" "foobar", 18);
> + p += 18;
> + vmc_emu_add_read_till(vmc, vmc->message + 8);
> + vmc_emu_add_read_till(vmc, vmc->message + 14);
> + vmc_emu_add_read_till(vmc, p);
> +
> + spice_server_char_device_wakeup(&vmc->instance);
> + } break;
> + // Fourth test, we should get back an error if client tried to remove
> + // a not existing reader
> + case 4: {
> + static const char expected_buf[] =
> + // forwarded Error message
> + "\x65\x00\x10\x00\x00\x00"
> + "\x02\x00\x00\x00\x05\x00\x00\x00\x04\x00\x00\x00\x01\x00\x00\x00";
> + spice_buffer_append(&channel_expected, expected_buf, sizeof(expected_buf) - 1);
> +
> + // remove invalid, this will trigger an error
> + send_data(client_socket, VSC_ReaderRemove, 5);
> + } break;
> + // Fifth test, similar to previous but using an huge reader_id field to trigger
> + // possible buffer overflow
> + case 5: {
> + static const char expected_buf[] =
> + // forwarded Error message
> + "\x65\x00\x10\x00\x00\x00"
> + "\x02\x00\x00\x00\x05\x01\x00\x00\x04\x00\x00\x00\x01\x00\x00\x00";
> + spice_buffer_append(&channel_expected, expected_buf, sizeof(expected_buf) - 1);
> +
> + // remove invalid and huge, this will trigger an error, should not crash
> + send_data(client_socket, VSC_ReaderRemove, 261);
> + } break;
> + // Sixth test, send an invalid message from client, a log is triggered
> + // but channel continues to work
> + case 6: {
> + static const char expected_buf[] =
> + // forwarded APDU message
> + "\x00\x00\x00\x07\x00\x00\x00\x00\x00\x00\x00\x06\x68\x65\x6c\x6c\x6f\x00";
> + spice_buffer_append(&device_expected, expected_buf, sizeof(expected_buf) - 1);
> +
> + g_test_expect_message(G_LOG_DOMAIN, G_LOG_LEVEL_WARNING,
> + "*ERROR: unexpected message on smartcard channel*");
> +
> + // invalid message type, should log a warning
> + send_data(client_socket, 0xabcd, 0);
> + // APDU just to get an event
> + send_data(client_socket, VSC_APDU, 0);
> + } break;
> + // Seventh test, an Error message from device are forwarded to the channel.
> + // Note that the header is in big endian order while the error from device
> + // is in little endian order. This seems weird but it's correct with the
> + // current libcacard implementation which just send error as host order
> + case 7: {
> + g_test_assert_expected_messages();
> +
> + static const char expected_buf[] =
> + // forwarded Error message
> + "\x65\x00\x10\x00\x00\x00"
> + "\x02\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x0a\x0b\x0c\x0d";
> + spice_buffer_append(&channel_expected, expected_buf, sizeof(expected_buf) - 1);
> +
> + vmc_emu_reset(vmc);
> + // data from device
> + uint8_t *p = vmc->message;
> +
> + // add Error message
> + memcpy(p, "\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x04\x0a\x0b\x0c\x0d", 16);
> + p += 16;
> + vmc_emu_add_read_till(vmc, p);
> +
> + spice_server_char_device_wakeup(&vmc->instance);
> + } break;
> + case 8:
> + g_test_assert_expected_messages();
> + basic_event_loop_quit();
> + break;
> + default:
> + abort();
> + }
> }
>
> static void test_smartcard(TestFixture *fixture, gconstpointer user_data)
> @@ -144,6 +312,7 @@ static void test_smartcard(TestFixture *fixture, gconstpointer user_data)
> // add VSC_Init message
> memcpy(p, "\x00\x00\x00\x01\x0a\x0b\x0c\x0d\x00\x00\x00\x00", 12);
> p += 12;
> + vmc_emu_add_read_till(vmc, vmc->message + 2); // check header is decoded correctly when split
> vmc_emu_add_read_till(vmc, p);
>
> // find Smartcard channel to connect to
> @@ -170,7 +339,6 @@ static void test_smartcard(TestFixture *fixture, gconstpointer user_data)
> red_client_set_main(client, mcc);
>
> // create our testing RedChannelClient
> - int client_socket;
> red_channel_connect(channel, client, create_dummy_stream(server, &client_socket),
> FALSE, &caps);
> red_channel_capabilities_reset(&caps);
> @@ -180,21 +348,20 @@ static void test_smartcard(TestFixture *fixture, gconstpointer user_data)
>
> // push data into channel
> send_ack_sync(client_socket, 1);
> - send_data(client_socket, VSC_ReaderAdd);
> - send_data(client_socket, VSC_APDU);
>
> - // check data are processed after a short time
> - SpiceTimer *watch_timer;
> - watch_timer = core->timer_add(check_data, core);
> - core->timer_start(watch_timer, 100);
> + // check data are processed
> + SpiceWatch *watch = core->watch_add(client_socket, SPICE_WATCH_EVENT_READ,
> + data_from_channel, NULL);
> + vmc->data_written_cb = check_data;
>
> // start all test
> alarm(10);
> + next_test();
> basic_event_loop_mainloop();
> alarm(0);
> // cleanup
> - core->timer_remove(watch_timer);
> + core->watch_remove(watch);
> red_client_destroy(client);
> g_object_unref(main_channel);
> g_object_unref(channel);
> diff --git a/server/tests/vmc-emu.c b/server/tests/vmc-emu.c
> index ddac5269c..9ef05061a 100644
> --- a/server/tests/vmc-emu.c
> +++ b/server/tests/vmc-emu.c
> @@ -31,6 +31,9 @@ static int vmc_write(SpiceCharDeviceInstance *sin,
> unsigned copy = MIN(sizeof(vmc->write_buf) - vmc->write_pos, len);
> memcpy(vmc->write_buf+vmc->write_pos, buf, copy);
> vmc->write_pos += copy;
> + if (copy && vmc->data_written_cb) {
> + vmc->data_written_cb(vmc);
> + }
> return len;
> }
>
> diff --git a/server/tests/vmc-emu.h b/server/tests/vmc-emu.h
> index 6fcea69d2..a6b77f585 100644
> --- a/server/tests/vmc-emu.h
> +++ b/server/tests/vmc-emu.h
> @@ -40,6 +40,9 @@ struct VmcEmu {
>
> unsigned write_pos;
> uint8_t write_buf[2048];
> +
> + // this callback will be called when new data arrive to the device
> + void (*data_written_cb)(VmcEmu *vmc);
> };
>
> VmcEmu *vmc_emu_new(const char *subtype, const char *portname);
> --
> 2.21.0
>
> _______________________________________________
> Spice-devel mailing list
> Spice-devel at lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/spice-devel
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 833 bytes
Desc: not available
URL: <https://lists.freedesktop.org/archives/spice-devel/attachments/20191009/34283686/attachment.sig>
More information about the Spice-devel
mailing list