Hello,

This is my first time posting here so please forgive me if
something is not correct.

When running cwm you can destroy the client window while
you are moving or resizing it, leading to crash of the WM
when the prop window is accessed (the one at the top-left that
indicates the position/scale).

I include a demonstration for the crash and also a patch that
fixes it. Hopefully someone with more experience can implement
a better fix.

Also, should this go into the tech mailing list instead of misc?

Demonstration code for the crash:

#include <X11/Xlib.h>

#include <err.h>
#include <unistd.h>

int
main(void)
{
        Display *dpy;
        Window win;
        int scr;
        XEvent ev;

        dpy = XOpenDisplay(NULL);
        if (!dpy)
                err(1, "XOpenDisplay");

        scr = DefaultScreen(dpy);

        win = XCreateSimpleWindow(dpy, RootWindow(dpy, scr), 0, 0, 500, 500, 0, 
0, WhitePixel(dpy, scr));
        if (!win)
                err(1, "XCreateSimpleWindow");

        XSelectInput(dpy, win, StructureNotifyMask | SubstructureNotifyMask | 
ExposureMask);
        XMapRaised(dpy, win);

        while (1) {
                XNextEvent(dpy, &ev);
                switch (ev.type) {
                case CreateNotify:
                        goto end;
                case Expose:
                        XClearWindow(dpy, win);
                        break;
                default:
                        break;
                }
        }
end:
        XDestroyWindow(dpy, win);

        XCloseDisplay(dpy);

        return 0;
}

Here is the patch:

Index: calmwm.h
===================================================================
RCS file: /cvs/xenocara/app/cwm/calmwm.h,v
retrieving revision 1.379
diff -u -p -r1.379 calmwm.h
--- calmwm.h    20 Jul 2023 14:39:34 -0000      1.379
+++ calmwm.h    6 Feb 2024 20:20:22 -0000
@@ -481,7 +481,7 @@ struct geom          screen_area(struct screen_
 struct screen_ctx      *screen_find(Window);
 void                    screen_init(int);
 void                    screen_prop_win_create(struct screen_ctx *, Window);
-void                    screen_prop_win_destroy(struct screen_ctx *);
+void                    screen_prop_win_destroy(struct screen_ctx *, int);
 void                    screen_prop_win_draw(struct screen_ctx *,
                             const char *, ...)
                            __attribute__((__format__ (printf, 2, 3)))
Index: kbfunc.c
===================================================================
RCS file: /cvs/xenocara/app/cwm/kbfunc.c,v
retrieving revision 1.174
diff -u -p -r1.174 kbfunc.c
--- kbfunc.c    20 Jul 2023 14:39:34 -0000      1.174
+++ kbfunc.c    6 Feb 2024 20:20:22 -0000
@@ -169,9 +169,23 @@ kbfunc_client_move_mb(void *ctx, struct 
 
        screen_prop_win_create(sc, cc->win);
        screen_prop_win_draw(sc, "%+5d%+5d", cc->geom.x, cc->geom.y);
-       while (move) {
-               XMaskEvent(X_Dpy, MOUSEMASK, &ev);
+       while (move > 0) {
+               XMaskEvent(X_Dpy, MOUSEMASK | SubstructureNotifyMask, &ev);
                switch (ev.type) {
+               /* check for destroy events, in case the client window
+                * gets destroyed, which forcefully closes the prop window.
+                */
+               case DestroyNotify:
+                       /* set move to -1 to specify abrupt exit */
+                       if (ev.xdestroywindow.window == cc->win) {
+                               screen_prop_win_destroy(sc, 1);
+                               client_remove(cc);
+                               move = -1; 
+                       } else if (ev.xdestroywindow.window == sc->prop.win) {
+                               screen_prop_win_destroy(sc, 0);
+                               move = -1; 
+                       }
+                       break;
                case MotionNotify:
                        /* not more than 60 times / second */
                        if ((ev.xmotion.time - ltime) <= (1000 / 60))
@@ -199,9 +213,11 @@ kbfunc_client_move_mb(void *ctx, struct 
                        break;
                }
        }
-       if (ltime)
-               client_move(cc);
-       screen_prop_win_destroy(sc);
+       if (move != -1) {
+               if (ltime)
+                       client_move(cc);
+               screen_prop_win_destroy(sc, 1);
+       }
        XUngrabPointer(X_Dpy, CurrentTime);
 }
 
@@ -258,9 +274,22 @@ kbfunc_client_resize_mb(void *ctx, struc
 
        screen_prop_win_create(sc, cc->win);
        screen_prop_win_draw(sc, "%4d x %-4d", cc->dim.w, cc->dim.h);
-       while (resize) {
+       while (resize > 0) {
                XMaskEvent(X_Dpy, MOUSEMASK, &ev);
                switch (ev.type) {
+               /* check for destroy events, in case the client window
+                * gets destroyed, which forcefully closes the prop window.
+                */
+               case DestroyNotify:
+                       if (ev.xdestroywindow.window == cc->win) {
+                               screen_prop_win_destroy(sc, 1);
+                               client_remove(cc);
+                               resize = -1; 
+                       } else if (ev.xdestroywindow.window == sc->prop.win) {
+                               screen_prop_win_destroy(sc, 0);
+                               resize = -1; 
+                       }
+                       break;
                case MotionNotify:
                        /* not more than 60 times / second */
                        if ((ev.xmotion.time - ltime) <= (1000 / 60))
@@ -279,9 +308,11 @@ kbfunc_client_resize_mb(void *ctx, struc
                        break;
                }
        }
-       if (ltime)
-               client_resize(cc, 1);
-       screen_prop_win_destroy(sc);
+       if (resize != -1) {
+               if (ltime)
+                       client_resize(cc, 1);
+               screen_prop_win_destroy(sc, 1);
+       }
        XUngrabPointer(X_Dpy, CurrentTime);
 
        /* Make sure the pointer stays within the window. */
Index: screen.c
===================================================================
RCS file: /cvs/xenocara/app/cwm/screen.c,v
retrieving revision 1.98
diff -u -p -r1.98 screen.c
--- screen.c    27 Jan 2022 18:45:10 -0000      1.98
+++ screen.c    6 Feb 2024 20:20:22 -0000
@@ -278,10 +278,15 @@ screen_prop_win_create(struct screen_ctx
 }
 
 void
-screen_prop_win_destroy(struct screen_ctx *sc)
+screen_prop_win_destroy(struct screen_ctx *sc, int destroy_window)
 {
        XftDrawDestroy(sc->prop.xftdraw);
-       XDestroyWindow(X_Dpy, sc->prop.win);
+       /*
+        * In case the window was already destroyed
+        * (i.e. DestroyNotify event)
+        */
+       if (destroy_window)
+               XDestroyWindow(X_Dpy, sc->prop.win);
 }
 
 void

Reply via email to