xserver/dix/resource_priv.h
Kiyoshi Spreclerg 0a18bb02d9 glx: fix DrawableGone use-after-free without rejecting duplicate drawables
A window can have more than one __GLXdrawable registered under its X id.
On teardown DrawableGone() freed the partner with FreeResourceByType(),
which matches only id+type and so frees an arbitrary one, leaving the
survivor dangling -> later use-after-free (issue #1491).

Add FreeResourceByTypeValue() (internal, dix/resource_priv.h) that also
matches the value, and use it in DrawableGone() to free the entry for
this exact drawable. Fixes the UAF while keeping duplicate glXCreateWindow
working, unlike the previous fix that rejected it and broke clients.

Assisted-by: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Kiyoshi Spreclerg <kiyoshi_pip@protonmail.com>
(cherry picked from commit 36b8a3323c)
2026-08-24 12:24:29 +02:00

194 lines
6.3 KiB
C

/* SPDX-License-Identifier: MIT OR X11
*
* Copyright © 2024 Enrico Weigelt, metux IT consult <info@metux.net>
*/
#ifndef _XSERVER_DIX_RESOURCE_PRIV_H
#define _XSERVER_DIX_RESOURCE_PRIV_H
#include <X11/Xdefs.h>
#include "include/callback.h"
#include "include/dix.h"
#include "include/resource.h"
#define SameClient(obj,client) \
(CLIENT_BITS((obj)->resource) == (client)->clientAsMask)
/*
* Resource IDs having that bit set still belonging to some client,
* but are server-internal, thus invisible to clients.
*/
#define SERVER_BIT (Mask)0x40000000 /* use illegal bit */
/* client field */
#define RESOURCE_CLIENT_MASK ((((1u << ResourceClientBits())) - 1) << CLIENTOFFSET)
/* bits and fields within a resource id */
#define RESOURCE_AND_CLIENT_COUNT 29 /* 29 bits for XIDs */
#define CLIENTOFFSET (RESOURCE_AND_CLIENT_COUNT - ResourceClientBits())
/* extract the client mask from an XID */
#define CLIENT_BITS(id) ((id) & RESOURCE_CLIENT_MASK)
/* resource field */
#define RESOURCE_ID_MASK ((1u << CLIENTOFFSET) - 1)
/*
* @brief retrieve client that owns given window
*
* XIDs carry the ID of the client who created/owns the resource in upper bits.
* (every client so is assigned a range of XIDs it may use for resource creation)
*
* @param WindowPtr to the window whose client shall be retrieved
* @return pointer to ClientRec structure or NULL
*/
ClientPtr dixClientForWindow(WindowPtr pWin);
/*
* @brief retrieve client that owns given grab
*
* XIDs carry the ID of the client who created/owns the resource in upper bits.
* (every client so is assigned a range of XIDs it may use for resource creation)
*
* @param GrabPtr to the grab whose owning client shall be retrieved
* @return pointer to ClientRec structure or NULL
*/
ClientPtr dixClientForGrab(GrabPtr pGrab);
/*
* @brief retrieve client that owns InputClients
*
* XIDs carry the ID of the client who created/owns the resource in upper bits.
* (every client so is assigned a range of XIDs it may use for resource creation)
*
* @param GrabPtr to the InputClients whose owning client shall be retrieved
* @return pointer to ClientRec structure or NULL
*/
ClientPtr dixClientForInputClients(InputClientsPtr pInputClients);
/*
* @brief retrieve client that owns OtherClients
*
* XIDs carry the ID of the client who created/owns the resource in upper bits.
* (every client so is assigned a range of XIDs it may use for resource creation)
*
* @param GrabPtr to the OtherClients whose owning client shall be retrieved
* @return pointer to ClientRec structure or NULL
*/
ClientPtr dixClientForOtherClients(OtherClientsPtr pOtherClients);
/*
* @brief extract client ID from XID
*
* XIDs carry the ID of the client who created/owns the resource in upper bits.
* (every client so is assigned a range of XIDs it may use for resource creation)
*
* This ID is frequently used as table index, eg. for client or resource lookup.
*
* @param XID the ID of the resource whose client is retrieved
* @return index of the client (within client or resource table)
*/
static inline unsigned short dixClientIdForXID(XID xid) {
return (unsigned short)((CLIENT_BITS(xid) >> CLIENTOFFSET));
}
/*
* @brief retrieve client pointer from XID
*
* XIDs carry the ID of the client who created/owns the resource in upper bits.
* (every client so is assigned a range of XIDs it may use for resource creation)
*
* @param XID the ID of the resource whose client is retrieved
* @return pointer to ClientRec structure or NULL
*/
static inline ClientPtr dixClientForXID(XID xid) {
const int idx = dixClientIdForXID(xid);
if (idx < MAXCLIENTS)
return clients[idx];
return NULL;
}
/*
* @brief check whether resource is owned by server
*
* @param XID the ID of the resource to check
* @return TRUE if resource is server owned
*/
static inline Bool dixResouceIsServerOwned(XID xid) {
return (dixClientForXID(xid) == serverClient);
}
/*
* @brief hash a XID for using as hashtable index
*
* @param id the XID to hash
* @param numBits number of bits in the resulting hash (>=0)
* @result the computed hash value
*
* @note This function is really only for handling
* INITHASHSIZE..MAXHASHSIZE bit hashes, but will handle any number
* of bits by either masking numBits lower bits of the ID or by
* providing at most MAXHASHSIZE hashes.
*/
int HashResourceID(XID id, unsigned int numBits);
/*
* @brief scan for free XIDs for client
*
* @param pClient the client to scan
* @param count maximum size of items to return
* @param pids pointer to XID where to return found free XIDs
* @result number of free XIDs
*/
unsigned int GetXIDList(ClientPtr pClient,
unsigned int count,
XID *pids);
/*
* @brief retrieve a range of free XIDs for given client
*
* @param client the client to scan
* @param server TRUE if scanning for free server XIDs
* @param minp pointer to result buffer: minimum XID of found range
* @param maxp pointer to result buffer: maximum XID of found range
*/
void GetXIDRange(int client,
Bool server,
XID *minp,
XID *maxp);
/*
* @brief free a specific resource among several sharing an id and type
*
* Like FreeResourceByType(), but only frees the entry whose value matches the
* one supplied. Needed when more than one resource is registered under the same
* id and type (e.g. a GLX window drawable, registered under both its GLX id and
* the backing X window id): matching on id+type alone frees an arbitrary one.
*
* @param id the resource id to free
* @param type the resource type to match
* @param value the resource value to match
* @param skipFree if TRUE, unlink the entry without calling its delete function
*
* Exported (like FreeResourceByType) because loadable dix modules such as glx
* call it across the dlopen boundary, even though it stays out of the SDK.
*/
extern _X_EXPORT void FreeResourceByTypeValue(XID id, RESTYPE type,
void *value, Bool skipFree);
/* Resource state callback */
extern CallbackListPtr ResourceStateCallback;
typedef enum {
ResourceStateAdding,
ResourceStateFreeing
} ResourceState;
typedef struct {
ResourceState state;
XID id;
RESTYPE type;
void *value;
} ResourceStateInfoRec;
#endif /* _XSERVER_DIX_RESOURCE_PRIV_H */