[Spice-devel] [vdagent-linux v8 4/4] vdagentd: Do endian swapping.
Victor Toso
victortoso at redhat.com
Wed Feb 15 10:31:02 UTC 2017
On Wed, Feb 15, 2017 at 10:48:07AM +0100, Victor Toso wrote:
> From: Michal Suchanek <msuchanek at suse.de>
>
> This allows running big endian and little endian guest side by side using
> cut & paste between them.
>
> There is a general design idea that swapping should come as close to
> virtio_read/virtio_write as possible. In particular, the protocol
> between vdagent and vdagentd is guest-specific and in native endian.
> With muliple layers of headers this is a bit tricky. A few message types
> have to be swapped fully before passing through vdagentd.
>
> Signed-off-by: Michal Suchanek <msuchanek at suse.de>
> Signed-off-by: Victor Toso <me at victortoso.com>
> ---
> src/vdagentd/vdagentd.c | 109 ++++++++++++++++++++++++++++++++++++++++-----
> src/vdagentd/virtio-port.c | 36 +++++++++------
> 2 files changed, 120 insertions(+), 25 deletions(-)
>
> diff --git a/src/vdagentd/vdagentd.c b/src/vdagentd/vdagentd.c
> index 2329489..954bba1 100644
> --- a/src/vdagentd/vdagentd.c
> +++ b/src/vdagentd/vdagentd.c
> @@ -78,6 +78,34 @@ static int client_connected = 0;
> static int max_clipboard = -1;
>
> /* utility functions */
> +static void virtio_msg_uint32_to_le(uint8_t *_msg, uint32_t size, uint32_t offset)
> +{
> + uint32_t i, *msg = (uint32_t *)(_msg + offset);
> +
> + /* offset - size % 4 should be 0 - extra bytes are ignored */
> + for (i = 0; i < (size - offset) / 4; i++)
> + msg[i] = GUINT32_TO_LE(msg[i]);
> +}
> +
> +static void virtio_msg_uint32_from_le(uint8_t *_msg, uint32_t size, uint32_t offset)
> +{
> + uint32_t i, *msg = (uint32_t *)(_msg + offset);
> +
> + /* offset - size % 4 should be 0 - extra bytes are ignored */
> + for (i = 0; i < (size - offset) / 4; i++)
> + msg[i] = GUINT32_FROM_LE(msg[i]);
> +}
> +
> +static void virtio_msg_uint16_from_le(uint8_t *_msg, uint32_t size, uint32_t offset)
> +{
> + uint32_t i;
> + uint16_t *msg = (uint16_t *)(_msg + offset);
> +
> + /* offset - size % 2 should be 0 - extra bytes are ignored */
> + for (i = 0; i < (size - offset) / 2; i++)
> + msg[i] = GUINT16_FROM_LE(msg[i]);
> +}
> +
> /* vdagentd <-> spice-client communication handling */
> static void send_capabilities(struct vdagent_virtio_port *vport,
> uint32_t request)
> @@ -102,6 +130,7 @@ static void send_capabilities(struct vdagent_virtio_port *vport,
> VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_GUEST_LINEEND_LF);
> VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_MAX_CLIPBOARD);
> VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_AUDIO_VOLUME_SYNC);
> + virtio_msg_uint32_to_le((uint8_t *)caps, size, 0);
>
> vdagent_virtio_port_write(vport, VDP_CLIENT_PORT,
> VD_AGENT_ANNOUNCE_CAPABILITIES, 0,
> @@ -174,8 +203,8 @@ static void do_client_monitors(struct vdagent_virtio_port *vport, int port_nr,
> (uint8_t *)mon_config, size);
>
> /* Acknowledge reception of monitors config to spice server / client */
> - reply.type = VD_AGENT_MONITORS_CONFIG;
> - reply.error = VD_AGENT_SUCCESS;
> + reply.type = GUINT32_TO_LE(VD_AGENT_MONITORS_CONFIG);
> + reply.error = GUINT32_TO_LE(VD_AGENT_SUCCESS);
> vdagent_virtio_port_write(vport, port_nr, VD_AGENT_REPLY, 0,
> (uint8_t *)&reply, sizeof(reply));
> }
> @@ -278,8 +307,8 @@ static void send_file_xfer_status(struct vdagent_virtio_port *vport,
> const char *msg, uint32_t id, uint32_t xfer_status)
> {
> VDAgentFileXferStatusMessage status = {
> - .id = id,
> - .result = xfer_status,
> + .id = GUINT32_TO_LE(id),
> + .result = GUINT32_TO_LE(xfer_status),
> };
> syslog(LOG_WARNING, msg, id);
> if (vport)
> @@ -361,6 +390,50 @@ static gsize vdagent_message_min_size[] =
> sizeof(VDAgentAudioVolumeSync), /* VD_AGENT_AUDIO_VOLUME_SYNC */
> };
>
> +static void vdagent_message_clipboard_from_le(VDAgentMessage *message_header,
> + uint8_t *data)
> +{
> + gsize min_size = vdagent_message_min_size[message_header->type];
> + uint32_t *data_type = (uint32_t *) data;
> +
> + if (VD_AGENT_HAS_CAPABILITY(capabilities, capabilities_size,
> + VD_AGENT_CAP_CLIPBOARD_SELECTION)) {
> + min_size += 4;
> + data_type++;
> + }
> +
> + switch (message_header->type) {
> + case VD_AGENT_CLIPBOARD_REQUEST:
> + case VD_AGENT_CLIPBOARD:
> + *data_type = GUINT32_FROM_LE(*data_type);
> + break;
> + case VD_AGENT_CLIPBOARD_GRAB:
> + virtio_msg_uint32_from_le(data, message_header->size, min_size);
> + break;
> + default:
> + g_warn_if_reached();
> + }
> +}
> +
> +static void vdagent_message_file_xfer_from_le(VDAgentMessage *message_header,
> + uint8_t *data)
> +{
> + uint32_t *id = (uint32_t *)data;
> + *id = GUINT32_FROM_LE(*id);
> + id++; /* status */
> +
> + switch (message_header->type) {
> + case VD_AGENT_FILE_XFER_DATA: {
> + VDAgentFileXferDataMessage *msg = (VDAgentFileXferDataMessage *)data;
> + msg->size = GUINT64_FROM_LE(msg->size);
> + break;
> + }
> + case VD_AGENT_FILE_XFER_STATUS:
> + *id = GUINT32_FROM_LE(*id); /* status */
> + break;
> + }
> +}
> +
> static gboolean vdagent_message_check_size(const VDAgentMessage *message_header)
> {
> uint32_t min_size = 0;
> @@ -435,13 +508,16 @@ static int virtio_port_read_complete(
>
> switch (message_header->type) {
> case VD_AGENT_MOUSE_STATE:
> + virtio_msg_uint32_from_le(data, message_header->size, 0);
> do_client_mouse(&uinput, (VDAgentMouseState *)data);
> break;
> case VD_AGENT_MONITORS_CONFIG:
> + virtio_msg_uint32_from_le(data, message_header->size, 0);
> do_client_monitors(vport, port_nr, message_header,
> (VDAgentMonitorsConfig *)data);
> break;
> case VD_AGENT_ANNOUNCE_CAPABILITIES:
> + virtio_msg_uint32_from_le(data, message_header->size, 0);
> do_client_capabilities(vport, message_header,
> (VDAgentAnnounceCapabilities *)data);
> break;
> @@ -449,11 +525,13 @@ static int virtio_port_read_complete(
> case VD_AGENT_CLIPBOARD_REQUEST:
> case VD_AGENT_CLIPBOARD:
> case VD_AGENT_CLIPBOARD_RELEASE:
> + vdagent_message_clipboard_from_le(message_header, data);
> do_client_clipboard(vport, message_header, data);
> break;
> case VD_AGENT_FILE_XFER_START:
> case VD_AGENT_FILE_XFER_STATUS:
> case VD_AGENT_FILE_XFER_DATA:
> + vdagent_message_file_xfer_from_le(message_header, data);
> do_client_file_xfer(vport, message_header, data);
> break;
> case VD_AGENT_CLIENT_DISCONNECTED:
> @@ -461,14 +539,18 @@ static int virtio_port_read_complete(
> do_client_disconnect();
> break;
> case VD_AGENT_MAX_CLIPBOARD: {
> - max_clipboard = ((VDAgentMaxClipboard *)data)->max;
> + max_clipboard = GUINT32_FROM_LE(((VDAgentMaxClipboard *)data)->max);
> syslog(LOG_DEBUG, "Set max clipboard: %d", max_clipboard);
> break;
> }
> - case VD_AGENT_AUDIO_VOLUME_SYNC:
> - do_client_volume_sync(vport, port_nr, message_header,
> - (VDAgentAudioVolumeSync *)data);
> + case VD_AGENT_AUDIO_VOLUME_SYNC: {
> + VDAgentAudioVolumeSync *vdata = (VDAgentAudioVolumeSync *)data;
> + virtio_msg_uint16_from_le((uint8_t *)vdata, message_header->size,
> + offsetof(VDAgentAudioVolumeSync, volume));
> +
> + do_client_volume_sync(vport, port_nr, message_header, vdata);
> break;
> + }
> default:
> g_warn_if_reached();
> }
> @@ -477,7 +559,7 @@ static int virtio_port_read_complete(
> }
>
> static void virtio_write_clipboard(uint8_t selection, uint32_t msg_type,
> - uint32_t data_type, const uint8_t *data, uint32_t data_size)
> + uint32_t data_type, uint8_t *data, uint32_t data_size)
> {
> uint32_t size = data_size;
>
> @@ -498,15 +580,18 @@ static void virtio_write_clipboard(uint8_t selection, uint32_t msg_type,
> vdagent_virtio_port_write_append(virtio_port, sel, 4);
> }
> if (data_type != -1) {
Ah, good catch from cddd [0] about signed x unsigned here. I also agree
that you should not fix this in this patch but maybe add a FIXME?
[0] https://lists.freedesktop.org/archives/spice-devel/2017-January/035099.html
I wonder why gcc/clang does not catch this by default. We can see it
with -Wextra, see:
src/vdagentd/vdagentd.c:570:19: warning: comparison of integers of
different signs: 'uint32_t' (aka 'unsigned int') and 'int'
[-Wsign-compare]
if (data_type != -1) {
~~~~~~~~~ ^ ~~
src/vdagentd/vdagentd.c:582:19: warning: comparison of integers of
different signs: 'uint32_t' (aka 'unsigned int') and 'int'
[-Wsign-compare]
if (data_type != -1) {
~~~~~~~~~ ^ ~~
src/vdagentd/vdagentd.c:635:41: warning: comparison of integers of
different signs: 'uint32_t' (aka 'unsigned int') and 'int'
[-Wsign-compare]
if (max_clipboard != -1 && size > max_clipboard) {
~~~~ ^ ~~~~~~~~~~~~~
As suggested in another thread (but for spice-gtk) we should consider
enabling a few extra warnings by default.
> + data_type = GUINT32_TO_LE(data_type);
> vdagent_virtio_port_write_append(virtio_port, (uint8_t*)&data_type, 4);
> }
>
> + if (msg_type == VD_AGENT_CLIPBOARD_GRAB)
> + virtio_msg_uint32_to_le(data, data_size, 0);
> vdagent_virtio_port_write_append(virtio_port, data, data_size);
> }
>
> /* vdagentd <-> vdagent communication handling */
> static int do_agent_clipboard(struct udscs_connection *conn,
> - struct udscs_message_header *header, const uint8_t *data)
> + struct udscs_message_header *header, uint8_t *data)
> {
> uint8_t selection = header->arg1;
> uint32_t msg_type = 0, data_type = -1, size = header->size;
> @@ -817,8 +902,8 @@ static void agent_read_complete(struct udscs_connection **connp,
> break;
> case VDAGENTD_FILE_XFER_STATUS:{
> VDAgentFileXferStatusMessage status;
> - status.id = header->arg1;
> - status.result = header->arg2;
> + status.id = GUINT32_TO_LE(header->arg1);
> + status.result = GUINT32_TO_LE(header->arg2);
> vdagent_virtio_port_write(virtio_port, VDP_CLIENT_PORT,
> VD_AGENT_FILE_XFER_STATUS, 0,
> (uint8_t *)&status, sizeof(status));
> diff --git a/src/vdagentd/virtio-port.c b/src/vdagentd/virtio-port.c
> index cedda4d..ef3de90 100644
> --- a/src/vdagentd/virtio-port.c
> +++ b/src/vdagentd/virtio-port.c
> @@ -28,6 +28,7 @@
> #include <sys/select.h>
> #include <sys/socket.h>
> #include <sys/un.h>
> +#include <glib.h>
>
> #include "virtio-port.h"
>
> @@ -216,16 +217,16 @@ int vdagent_virtio_port_write_start(
> return -1;
> }
>
> - chunk_header.port = port_nr;
> - chunk_header.size = sizeof(message_header) + data_size;
> + chunk_header.port = GUINT32_TO_LE(port_nr);
> + chunk_header.size = GUINT32_TO_LE(sizeof(message_header) + data_size);
> memcpy(new_wbuf->buf + new_wbuf->write_pos, &chunk_header,
> sizeof(chunk_header));
> new_wbuf->write_pos += sizeof(chunk_header);
>
> - message_header.protocol = VD_AGENT_PROTOCOL;
> - message_header.type = message_type;
> - message_header.opaque = message_opaque;
> - message_header.size = data_size;
> + message_header.protocol = GUINT32_TO_LE(VD_AGENT_PROTOCOL);
> + message_header.type = GUINT32_TO_LE(message_type);
> + message_header.opaque = GUINT64_TO_LE(message_opaque);
> + message_header.size = GUINT32_TO_LE(data_size);
> memcpy(new_wbuf->buf + new_wbuf->write_pos, &message_header,
> sizeof(message_header));
> new_wbuf->write_pos += sizeof(message_header);
> @@ -309,13 +310,20 @@ static void vdagent_virtio_port_do_chunk(struct vdagent_virtio_port **vportp)
> memcpy((uint8_t *)&port->message_header + port->message_header_read,
> vport->chunk_data, read);
> port->message_header_read += read;
> - if (port->message_header_read == sizeof(port->message_header) &&
> - port->message_header.size) {
> - port->message_data = malloc(port->message_header.size);
> - if (!port->message_data) {
> - syslog(LOG_ERR, "out of memory, disconnecting virtio");
> - vdagent_virtio_port_destroy(vportp);
> - return;
> + if (port->message_header_read == sizeof(port->message_header)) {
> +
> + port->message_header.protocol = GUINT32_FROM_LE(port->message_header.protocol);
> + port->message_header.type = GUINT32_FROM_LE(port->message_header.type);
> + port->message_header.opaque = GUINT64_FROM_LE(port->message_header.opaque);
> + port->message_header.size = GUINT32_FROM_LE(port->message_header.size);
> +
> + if (port->message_header.size) {
> + port->message_data = malloc(port->message_header.size);
> + if (!port->message_data) {
> + syslog(LOG_ERR, "out of memory, disconnecting virtio");
> + vdagent_virtio_port_destroy(vportp);
> + return;
> + }
> }
> }
> pos = read;
> @@ -420,6 +428,8 @@ static void vdagent_virtio_port_do_read(struct vdagent_virtio_port **vportp)
> if (vport->chunk_header_read < sizeof(vport->chunk_header)) {
> vport->chunk_header_read += n;
> if (vport->chunk_header_read == sizeof(vport->chunk_header)) {
> + vport->chunk_header.size = GUINT32_FROM_LE(vport->chunk_header.size);
> + vport->chunk_header.port = GUINT32_FROM_LE(vport->chunk_header.port);
> if (vport->chunk_header.size > VD_AGENT_MAX_DATA_SIZE) {
> syslog(LOG_ERR, "chunk size %u too large",
> vport->chunk_header.size);
I don't see any issue in this patch so,
Acked-by: Victor Toso <victortoso at redhat.com>
And thanks again for the patch!
Cheers,
toso
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: not available
URL: <https://lists.freedesktop.org/archives/spice-devel/attachments/20170215/8c592ea4/attachment-0001.sig>
More information about the Spice-devel
mailing list