⬢ DragonFlyBSD Kernel Audit
DF-4791 / fix.diff
← back to finding ↓ download raw
--- sys/dev/drm/ttm/ttm_memory.c.orig
+++ sys/dev/drm/ttm/ttm_memory.c
@@ -38,10 +38,31 @@
 #include <linux/slab.h>
 
 #define TTM_MEMORY_ALLOC_RETRIES 4
 
+/*
+ * Recursion guard for ttm_shrink().
+ *
+ * ttm_shrink() -> ttm_bo_swapout() may have to move a buffer object to
+ * system memory, which lazily populates its ttm pages through
+ * ttm_mem_global_alloc_page() -> ttm_mem_global_alloc_zone(), which
+ * calls ttm_shrink() again while the zone is still over its limit.
+ * Nothing bounds this recursion except the number of swappable buffer
+ * objects on ttm_bo_global::swap_lru, which any unprivileged /dev/dri
+ * client controls, so the kernel stack (16 KiB) can be exhausted.
+ *
+ * Allow a small fixed nesting depth so eviction still functions, but
+ * cap it far below what the stack can hold (4 levels x ~2 KiB).
+ *
+ * (Upstream ultimately removed the accounting shrinker entirely in
+ *  commit b12dacf5fa64 "drm/ttm: nuke memory accounting shrinker".)
+ */
+#define TTM_SHRINK_MAX_NESTING 4
+static atomic_t ttm_shrink_nesting;
+
 struct ttm_mem_zone {
 	struct kobject kobj;
 	struct ttm_mem_global *glob;
@@ -211,10 +232,20 @@ static struct kobj_type ttm_mem_glob_kobj_type = {
 static void ttm_shrink(struct ttm_mem_global *glob, bool from_wq,
 			uint64_t extra, struct ttm_operation_ctx *ctx)
 {
 	int ret;
 
+	if (atomic_inc_return(&ttm_shrink_nesting) > TTM_SHRINK_MAX_NESTING) {
+		atomic_dec(&ttm_shrink_nesting);
+		return;
+	}
+
 	lockmgr(&glob->lock, LK_EXCLUSIVE);
 
 	while (ttm_zones_above_swap_target(glob, from_wq, extra)) {
 		lockmgr(&glob->lock, LK_RELEASE);
 		ret = ttm_bo_swapout(glob->bo_glob, ctx);
 		lockmgr(&glob->lock, LK_EXCLUSIVE);
 		if (unlikely(ret != 0))
 			break;
 	}
 
 	lockmgr(&glob->lock, LK_RELEASE);
+	atomic_dec(&ttm_shrink_nesting);
 }