Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

x11: Prevent memory corruption in XrmSetDatabase #1291

Merged
merged 1 commit into from
Mar 1, 2024

Conversation

cdown
Copy link
Contributor

@cdown cdown commented Feb 23, 2024

Despite what the XrmSetDatabase docs say, it may try to free the database, resulting in memory corruption. Prevent that by making sure it has no db to act on. If it's past the first run, we will have done XrmDestroyDatabase above anyway. This causes a segfault, reported in issue #1256.

From the XrmSetDatabase man page:

The database previously associated with the display (if any) is not
destroyed.

But this is patently false, from the source in Xlib's src/Xrm.c:

void XrmSetDatabase(
    Display *display,
    XrmDatabase database)
{
    LockDisplay(display);
    /* destroy database if set up implicitly by XGetDefault() */
    if (display->db && (display->flags & XlibDisplayDfltRMDB)) {
        XrmDestroyDatabase(display->db);
        display->flags &= ~XlibDisplayDfltRMDB;
    }
    display->db = database;
    UnlockDisplay(display);
}

This is broken in Xlib since at least 2004[0], so now it's unfortunately a backwards compatibility issue...

Fixes #1256.

0: https://lists.freedesktop.org/archives/xorg-commit-diffs/2004-March/000239.html

Despite what the XrmSetDatabase docs say, it may try to free the
database, resulting in memory corruption. Prevent that by making sure it
has no db to act on. If it's past the first run, we will have done
XrmDestroyDatabase above anyway. This causes a segfault, reported in
issue dunst-project#1256.

From the XrmSetDatabase man page:

> The database previously associated with the display (if any) is not
> destroyed.

But this is patently false, from the source in Xlib's src/Xrm.c:

    void XrmSetDatabase(
        Display *display,
        XrmDatabase database)
    {
        LockDisplay(display);
        /* destroy database if set up implicitly by XGetDefault() */
        if (display->db && (display->flags & XlibDisplayDfltRMDB)) {
            XrmDestroyDatabase(display->db);
            display->flags &= ~XlibDisplayDfltRMDB;
        }
        display->db = database;
        UnlockDisplay(display);
    }

This is broken in Xlib since at least 2004[0], so now it's unfortunately
a backwards compatibility issue...

Fixes dunst-project#1256.

0: https://lists.freedesktop.org/archives/xorg-commit-diffs/2004-March/000239.html
@cdown
Copy link
Contributor Author

cdown commented Feb 23, 2024

Conditions for repro are:

  1. Dunst starts before wm
  2. Dunst is displaying a notification
  3. WM starts and sends PropertyNotify

@cdown
Copy link
Contributor Author

cdown commented Feb 23, 2024

I was curious why others haven't run into this, and found out they have, for example this block in rxvt-unicode: https://github.com/exg/rxvt-unicode/blob/5ec6fb5ec7f0871f19814279575a2f7873e5556a/src/rxvttoolkit.C#L542-L543

@bynect
Copy link
Member

bynect commented Feb 29, 2024

Many thanks.

Do you know why in the rxvt code that line is protected by a #if XLIB_ILLEGAL_ACCESS ?

@cdown
Copy link
Contributor Author

cdown commented Mar 1, 2024

The flag is enabled by autotools if the following compiles:

Display *dpy;
dpy->xdefaults = (char *)0;

What's happened here is that the first illegal accesses they found were in xdefaults. From rxvt_display::get_resources():

  /* Get any Xserver defaults */
  if (refresh)
    {
      // fucking xlib keeps a copy of the rm string
      Atom actual_type;
      int actual_format;
      unsigned long nitems, nremaining;
      char *val = 0;

#if XLIB_ILLEGAL_ACCESS
      if (dpy->xdefaults)
        XFree (dpy->xdefaults);
#endif
[...]

They were concerned that since modifying this is not permitted by Xlib's formal API, it should be tested that it at least compiles.

Then further uses of XLIB_ILLEGAL_ACCESS were extended to other functions with broken behaviour, but the compile test was never updated to also test whether those compile (although they clearly always do, since nobody has ever complained about it).

Considering the compile test will always pass on any existing Xlib -- I looked back a long time and the relevant parts of these structs have not changed for at least 20 years) -- #if XLIB_ILLEGAL_ACCESS is always true. As such rxvt-unicode has always had this workaround enabled since its addition in January 2011.

@bynect
Copy link
Member

bynect commented Mar 1, 2024

Thanks for the explanation. Your solution seems reasonable to me so I'll merge 👍🏻

@bynect bynect merged commit de6fd27 into dunst-project:master Mar 1, 2024
18 checks passed
@cdown
Copy link
Contributor Author

cdown commented Mar 1, 2024

Thank you! Much appreciated.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants