Skip to content

keycodes argument to virDomainSendKey() is architecture specific

Hi,

There seems to be an issue related to (d *Domain) SendKey. I'm trying to send a keycode combination to a VM, but the combination is not sent as expected. For my program to work, all three keys need to be pressed simultaneously.

Basic info:

  • Using libvirt-go v7.3.0+incompatible
  • with Go 1.14.15
  • on Linux 5.11 x86_64

Here is my call that does not work:

var keys = []uint{KEY_LEFTCTRL, KEY_LEFTALT, KEY_F1}
dom.SendKey(uint(rawLibvirt.KEYCODE_SET_LINUX), 100, keys, 0)

In contrast, a manual call with virsh does work:

virsh send-key vm KEY_LEFTCTRL KEY_LEFTALT KEY_F1

I checked how the elements of keys are layed out in memory:

log.Printf("%p\n", unsafe.Pointer(&keys[0]))
log.Printf("%p\n", unsafe.Pointer(&keys[1]))

This gives me 0xb8efa0 and 0xb8efa8, meaning one uint is 8 bytes, fair so far.

Now, the Go bindings pass keys to the C library as follows:

func (d *Domain) SendKey(codeset, holdtime uint, keycodes []uint, flags uint32) error {
	...
	result := C.virDomainSendKeyWrapper(d.ptr, C.uint(codeset), C.uint(holdtime), (*C.uint)(unsafe.Pointer(&keycodes[0])), C.int(len(keycodes)), C.uint(flags), &err)
	...
}

In short, it (correctly) passes the pointer 0xb8efa0 and a length of 3, with the intention to include all three elements at the addresses 0xb8efa0, 0xb8efa8, and 0xb8efb0.

However, the C library expects an unsigned int * as pointer. On my system, an unsigned int is 4 bytes, not 8. I used this little C program to confirm that:

#include <stdio.h>

int main() {
	unsigned int foo = 0;
	printf("%d\n", sizeof(foo));
}

Let's compile and run it:

gcc main.c && ./a.out

This outputs 4. This means the C library will read the three keycodes from 0xb8efa0, 0xb8efa4, and 0xb8efa8.

Simply extending my keys to include trailing dummy elements "fixes" the issue.

var keys = []uint{KEY_LEFTCTRL, KEY_LEFTALT, KEY_F1, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED}
dom.SendKey(uint(rawLibvirt.KEYCODE_SET_LINUX), 100, keys, 0)

This way, the Go bindings will pass the C library a length of 6, meaning it will read 0xb8efa0, 0xb8efa4, 0xb8efa8, 0xb8efac, 0xb8efb0, 0xb8efb4. This includes all addresses the Go bindings intended to pass: 0xb8efa0, 0xb8efa8, 0xb8efb0.

Edited by eikendev