keyboard LED bug + patch

pacman at kosh.dhis.org pacman at kosh.dhis.org
Fri Feb 27 21:53:10 PST 2009


The component: xf86-input-keyboard

The source file: src/lnx_kbd.c

The function: GetKbdLeds

The bug:
    int real_leds, leds = 0;

    ioctl(pInfo->fd, KDGETLED, &real_leds);

    if (real_leds & LED_CAP) leds |= XLED1;
    if (real_leds & LED_NUM) leds |= XLED2;
    if (real_leds & LED_SCR) leds |= XLED3;

The reason:
KDGETLED actually only stores a single byte at the address indicated by
&real_leds, which on big-endian systems means the kernel's led state is put
into the most-significant byte of real_leds. The LED_CAP LED_NUM LED_SCR
macros then extract some bits from the least-significant byte, which still
contains stack garbage since real_leds hasn't been initialized.

Don't believe what the console_ioctl(4) man page says. It's wrong. Go read
drivers/char/vt_ioctl.c in the kernel source, or run this demo on a
big-endian machine:

#include <stdio.h>
#include <sys/ioctl.h>
#include <linux/kd.h>

int main(void)
{
  int ioctlret;
  long longval;

  longval = -1;

  ioctlret = ioctl(0, KDGETLED, &longval);
  printf("ioctlret=%d longval=%08lx\n", ioctlret, longval);

  return 0;
}

The fix is very simple:

diff --git a/src/lnx_kbd.c b/src/lnx_kbd.c
index cfe35a3..9144464 100644
--- a/src/lnx_kbd.c
+++ b/src/lnx_kbd.c
@@ -73,7 +73,8 @@ SetKbdLeds(InputInfoPtr pInfo, int leds)
 static int
 GetKbdLeds(InputInfoPtr pInfo)
 {
-    int real_leds, leds = 0;
+    char real_leds;
+    int leds = 0;
 
     ioctl(pInfo->fd, KDGETLED, &real_leds);
 

-- 
Alan Curry


More information about the xorg-devel mailing list