Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

drm/bridge: add drm_bridge_clear_and_put()

Drivers having a struct drm_bridge pointer pointing to a bridge in many
cases hold that reference until the owning device is removed. In those
cases the reference to the bridge can be put in the .remove callback
(possibly using devm actions) or in the .destroy func (possibly with the
help of struct drm_bridge::next_bridge). At those moments the driver should
not be operating anymore and won't dereference the bridge pointer after it
is put.

However there are cases when drivers need to stop holding a reference to a
bridge even when their device is not being removed. This is the case for
bridge hot-unplug, when a bridge is removed but the previous entity (bridge
or encoder) is staying. In such case the "previous entity" needs to put it
but cannot do it via devm or .destroy, because it is not being removed.

The easy way to dispose of such pointer is:

drm_bridge_put(my_priv->some_bridge);
my_priv->some_bridge = NULL;

However this is risky because there is a time window between the two lines
where the reference is put, and thus the bridge could be deallocated, but
the pointer is still assigned. If other functions of the same driver were
invoked concurrently they might dereference my_priv->some_bridge during
that window, resulting in use-after-free.

A correct solution is to clear the pointer before putting the reference,
but that needs a temporary variable:

struct drm_bridge *temp = my_priv->some_bridge;
my_priv->some_bridge = NULL;
drm_bridge_put(temp);

This solution is however annoying to write, so the incorrect version might
still sneak in.

Add a simple, easy to use function to put a bridge after setting its
pointer to NULL in the correct way.

Acked-by: Maxime Ripard <mripard@kernel.org>
Link: https://patch.msgid.link/20260310-drm-bridge-atomic-vs-remove-clear_and_put-v2-1-51fe222f3cf0@bootlin.com
Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>

+35
+34
drivers/gpu/drm/drm_bridge.c
··· 304 304 * 305 305 * This function decrements the bridge's reference count and frees the 306 306 * object if the reference count drops to zero. 307 + * 308 + * See also drm_bridge_clear_and_put() if you also need to set the pointer 309 + * to NULL 307 310 */ 308 311 void drm_bridge_put(struct drm_bridge *bridge) 309 312 { ··· 314 311 kref_put(&bridge->refcount, __drm_bridge_free); 315 312 } 316 313 EXPORT_SYMBOL(drm_bridge_put); 314 + 315 + /** 316 + * drm_bridge_clear_and_put - Given a bridge pointer, clear the pointer 317 + * then put the bridge 318 + * @bridge_pp: pointer to pointer to a struct drm_bridge; ``bridge_pp`` 319 + * must be non-NULL; if ``*bridge_pp`` is NULL this function 320 + * does nothing 321 + * 322 + * Helper to put a DRM bridge, but only after setting its pointer to 323 + * NULL. Useful when a struct drm_bridge reference must be dropped without 324 + * leaving a use-after-free window where the pointed bridge might have been 325 + * freed while still holding a pointer to it. 326 + * 327 + * For struct ``drm_bridge *some_bridge``, this code:: 328 + * 329 + * drm_bridge_clear_and_put(&some_bridge); 330 + * 331 + * is equivalent to the more complex:: 332 + * 333 + * struct drm_bridge *temp = some_bridge; 334 + * some_bridge = NULL; 335 + * drm_bridge_put(temp); 336 + */ 337 + void drm_bridge_clear_and_put(struct drm_bridge **bridge_pp) 338 + { 339 + struct drm_bridge *bridge = *bridge_pp; 340 + 341 + *bridge_pp = NULL; 342 + drm_bridge_put(bridge); 343 + } 344 + EXPORT_SYMBOL(drm_bridge_clear_and_put); 317 345 318 346 /** 319 347 * drm_bridge_put_void - wrapper to drm_bridge_put() taking a void pointer
+1
include/drm/drm_bridge.h
··· 1290 1290 1291 1291 struct drm_bridge *drm_bridge_get(struct drm_bridge *bridge); 1292 1292 void drm_bridge_put(struct drm_bridge *bridge); 1293 + void drm_bridge_clear_and_put(struct drm_bridge **bridge_pp); 1293 1294 1294 1295 /* Cleanup action for use with __free() */ 1295 1296 DEFINE_FREE(drm_bridge_put, struct drm_bridge *, if (_T) drm_bridge_put(_T))