Search code examples
csegmentation-faultxlib

segmentation fault in C, address points to zero page - XOpenDevice


I'm trying to use the X11 library to look at events from a specific device, but getting stuck at XOpenDevice.

The code:

#include "stdio.h"
#include <stdlib.h>
#include "string.h"
#include "X11/extensions/XInput.h"
#include "X11/Xlib.h"

int main(int argc, char *argv[])
{
    Display *dpy = XOpenDisplay(0);

    int *device_count;
    XDeviceInfo *devices = XListInputDevices(dpy, &device_count);

    int mouse_id;
    for (int i = 0; i < device_count; i++)
        if (0 == strcmp(devices[i].name, "SIGMACHIP SG 2.4G Wireless Mouse"))
            mouse_id = devices[i].id;
    XFreeDeviceList(devices);
    printf("mouse_id: %d\n", mouse_id);

    XDevice *mouse = XOpenDevice(&dpy, mouse_id); // <-- segmentation fault
    // do something
    XCloseDevice(&dpy, mouse);

    XCloseDisplay(dpy);
    return 0;
}

Compile and run:

gcc -g -fsanitize=address code.c -o prog.exe -lX11 -lXi
./prog.exe

Output:

> gcc -g -fsanitize=address code.c -o prog.exe -lX11 -lXi
code.c: In function ‘main’:
code.c:12:51: warning: passing argument 2 of ‘XListInputDevices’ from incompatible pointer type [-Wincompatible-pointer-types]
   12 |     XDeviceInfo *devices = XListInputDevices(dpy, &device_count);
      |                                                   ^~~~~~~~~~~~~
      |                                                   |
      |                                                   int **
In file included from code.c:4:
/usr/include/X11/extensions/XInput.h:1133:5: note: expected ‘int *’ but argument is of type ‘int **’
 1133 |     int*                /* ndevices */
      |     ^~~~
code.c:15:23: warning: comparison between pointer and integer
   15 |     for (int i = 0; i < device_count; i++)
      |                       ^
code.c:21:34: warning: passing argument 1 of ‘XOpenDevice’ from incompatible pointer type [-Wincompatible-pointer-types]
   21 |     XDevice *mouse = XOpenDevice(&dpy, mouse_id); // <-- segmentation fault
      |                                  ^~~~
      |                                  |
      |                                  Display **
/usr/include/X11/extensions/XInput.h:1141:5: note: expected ‘Display *’ but argument is of type ‘Display **’
 1141 |     Display*            /* display */,
      |     ^~~~~~~~
code.c:23:18: warning: passing argument 1 of ‘XCloseDevice’ from incompatible pointer type [-Wincompatible-pointer-types]
   23 |     XCloseDevice(&dpy, mouse);
      |                  ^~~~
      |                  |
      |                  Display **
/usr/include/X11/extensions/XInput.h:1146:5: note: expected ‘Display *’ but argument is of type ‘Display **’
 1146 |     Display*            /* display */,
      |     ^~~~~~~~

> ./prog.exe
mouse_id: 10
AddressSanitizer:DEADLYSIGNAL
=================================================================
==28208==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000048 (pc 0x7ffba8d03613 bp 0x000000000008 sp 0x7ffdf9cfb420 T0)
==28208==The signal is caused by a READ memory access.
==28208==Hint: address points to the zero page.
    #0 0x7ffba8d03613 in _XFlush (/lib64/libX11.so.6+0x49613) (BuildId: db539db0970427ed0cb836eae3d193c23fc94418)
    #1 0x7ffba8d038e4 in _XGetRequest (/lib64/libX11.so.6+0x498e4) (BuildId: db539db0970427ed0cb836eae3d193c23fc94418)
    #2 0x7ffba8cf565e in XQueryExtension (/lib64/libX11.so.6+0x3b65e) (BuildId: db539db0970427ed0cb836eae3d193c23fc94418)
    #3 0x7ffba951a800  (/lib64/libXi.so.6+0x3800) (BuildId: ee64e5f4d60239d9dcab6326d02bd5c2c6e31259)
    #4 0x7ffba9521d68 in XOpenDevice (/lib64/libXi.so.6+0xad68) (BuildId: ee64e5f4d60239d9dcab6326d02bd5c2c6e31259)
    #5 0x401430 in main /home/levi/development/mouse-chording/code.c:21
    #6 0x7ffba8ae4bef in __libc_start_call_main (/lib64/libc.so.6+0x27bef) (BuildId: 7ed2a863c9bc5e582a36387b1b1f26053e10ef16)
    #7 0x7ffba8ae4cb8 in __libc_start_main@GLIBC_2.2.5 (/lib64/libc.so.6+0x27cb8) (BuildId: 7ed2a863c9bc5e582a36387b1b1f26053e10ef16)
    #8 0x401154 in _start ../sysdeps/x86_64/start.S:115

AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV (/lib64/libX11.so.6+0x49613) (BuildId: db539db0970427ed0cb836eae3d193c23fc94418) in _XFlush
==28208==ABORTING

I tried this, but got the same error:

XDevice *mouse = malloc( sizeof(XDevice) );
mouse = XOpenDevice(&dpy, mouse_id);

I tried changing int mouse_id to XID mouse_id to more closely match the API, but same error.

I've looked up repos using the same API, trying to find differences, but it seems like I am doing the same as everybody else. Example: https://github.com/FreeRDP/FreeRDP/blob/10386e73bcebd450062a9ef5272260af5b621d05/client/X11/xf_client.c#L991C1-L991C59

Have barely used C before, so I am not sure where to go from here.

Relevant docs: https://x.org/releases/X11R7.7/doc/libXi/inputlib.html#XOpenDevice


Solution

  • as pointed out by @someprogrammerdude, the problem was a misunderstanding around pointers.

    since dpy was initialized as a pointer with Display *dpy it did not need the & to be passed as a pointer (the asterix makes it a pointer).

    device_count had the opposite problem.

    and this is what the fixed version looks like.

    #include "stdio.h"
    #include "string.h"
    #include "X11/extensions/XInput.h"
    #include "X11/Xlib.h"
    
    int main(int argc, char *argv[])
    {
        Display *dpy = XOpenDisplay(0);
    
        int device_count;
        XDeviceInfo *devices;
        devices = XListInputDevices(dpy, &device_count);
    
        int mouse_id;
        for (int i = 0; i < device_count; i++)
            if (0 == strcmp(devices[i].name, "SIGMACHIP SG 2.4G Wireless Mouse"))
                mouse_id = devices[i].id;
        XFreeDeviceList(devices);
        printf("mouse_id: %d\n", mouse_id);
    
        XDevice *mouse = XOpenDevice(dpy, mouse_id);
        // do something
        XCloseDevice(dpy, mouse);
    
        XCloseDisplay(dpy);
        return 0;
    }