{ "dataType": "CVE_RECORD", "dataVersion": "5.2", "cveMetadata": { "cveId": "CVE-2026-98166", "assignerOrgId": "416baaa9-dc9f-4396-8d5f-8c081fb06d67", "state": "PUBLISHED", "assignerShortName": "Linux", "dateReserved": "2026-09-25T10:25:14.321Z", "datePublished": "2026-10-06T08:44:11.927Z", "dateUpdated": "2026-10-19T09:14:57.782Z" }, "containers": { "cna": { "providerMetadata": { "orgId": "416baaa9-dc9f-4396-8d5f-8c081fb06d67", "shortName": "Linux", "dateUpdated": "2026-10-19T09:14:57.782Z" }, "descriptions": [ { "lang": "en", "value": "In the Linux kernel, the following vulnerability has been resolved:\n\ndrm/ttm: fix swapped-out resources never leaving their bulk_move range\n\nttm_tt_swapout() returns the number of pages swapped out on success and\na negative error code on failure; for a populated ttm it never returns\nzero. Commit b2ed01e7ad3d (\"drm/ttm: Fix ttm_bo_swapout() infinite LRU\nwalk on swapout failure\") moved the bulk_move bookkeeping in\nttm_bo_swapout_cb() under \"if (!ret)\", so the\nttm_resource_del_bulk_move_unevictable() / ttm_resource_move_to_lru_tail()\npair is now skipped on every successful swapout. The equivalent change\nfor the shrinker in commit 1d59f36e95f7 (\"drm/ttm: Fix ttm_bo_shrink()\ninfinite LRU walk on backup failure\") tests \"lret > 0\", which is what\nwas intended here as well.\n\nBefore b2ed01e7ad3d the resource was taken off the bulk_move before the\nswapout; since then a swapped-out resource stays inside its BO's\nbulk_move range (and on the manager LRU) although it is unevictable.\nWhen it is later freed or the BO leaves the bulk_move\n(ttm_resource_free(), ttm_bo_set_bulk_move() via amdgpu_vm_bo_del()),\nttm_resource_del_bulk_move() skips it because of its\n!ttm_resource_unevictable() guard, so a range endpoint in pos->first /\npos->last is left pointing at freed memory. The next\nttm_lru_bulk_move_tail() or ttm_resource_add_bulk_move() on that cursor\nis a use-after-free, seen as the resv WARN in ttm_lru_bulk_move_add(),\n\"list_del corruption\" in ttm_resource_move_to_lru_tail() or a NULL\ndereference in ttm_resource_manager_next() -- minutes to hours after a\nhibernation, or at process exit / reboot following one. Samuel\nAinsworth's analysis of drm/amd issue 5387 (see Link) identified the\ndangling cursor; the missing removal at swapout time is the reason it\ndangles.\n\nTesting the condition for success restores the removal. On an AMD\nPhoenix APU (ASUS UM3406GA, gfx1103) running suspend-then-hibernate on\na 7.0.y stable kernel carrying the backport (Ubuntu 7.0.0-31) the bug\ncrashed 5 of 18 hibernation cycles; a function profile of one\nhibernation showed 336 ttm_tt_swapout() calls and zero\nttm_resource_del_bulk_move_unevictable() calls. With this change the\nremoval happens for every swapped-out resource and 12 further cycles\nwere clean." } ], "metrics": [ { "cvssV3_1": { "version": "3.1", "vectorString": "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H", "baseScore": 7.8, "baseSeverity": "HIGH" }, "scenarios": [ { "lang": "en", "value": "AV:L - The attacker drives ttm_bo_swapout_cb() through local DRM render-node ioctls (amdgpu GEM_CREATE with VM_ALWAYS_VALID, then CS), with ttm_tt_populate() calling ttm_global_swapout() once ttm_pages_limit is exceeded, or through hibernation. No remote protocol carries any input.\nAC:L - Hibernation is not needed: the attacker allocates per-VM BOs past ttm_pages_limit to force swapout, closes the swapped BO so ttm_resource_free() frees a resource still referenced by the bulk_move cursor, then submits CS to run ttm_lru_bulk_move_tail(). Every step is the attacker's own action.\nPR:L - AMDGPU_GEM_CREATE, GEM_CLOSE and AMDGPU_CS are DRM_RENDER_ALLOW ioctls on /dev/dri/renderD*, open to an ordinary render-group or seat user without root or capabilities.\nUI:N - The attacker runs the whole sequence in their own process with their own buffer objects. No other user has to act.\nS:U - The freed ttm_resource and the corrupted manager LRU lists are kernel memory, so the impact stays within the kernel's own security authority.\nC:H - After ttm_resource_free() frees the swapped resource, bulk_move pos->first/pos->last still point at it, so a reallocated object can be read and walked as a ttm_resource by ttm_lru_bulk_move_tail() and the ttm_lru_next_res()/prev_res() helpers.\nI:H - list_bulk_move_tail() and ttm_lru_bulk_move_pos_tail() write list pointers through the dangling cursor into freed memory. The commit message reports this showing up as list_del corruption, a write primitive usable against a sprayed object.\nA:H - The use-after-free crashes the kernel through list_del corruption, the dma_resv WARN in ttm_lru_bulk_move_add(), or a NULL dereference in ttm_resource_manager_next(). The reporter crashed 5 of 18 hibernation cycles." } ] } ], "affected": [ { "product": "Linux", "vendor": "Linux", "defaultStatus": "unaffected", "repo": "https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git", "programFiles": [ "drivers/gpu/drm/ttm/ttm_bo.c" ], "versions": [ { "version": "b2ed01e7ad3de80333e9b962a44024b094bc0b2b", "lessThan": "1169fe8c11ca45e3f91d59a73eb271d0ca8a7fb0", "status": "affected", "versionType": "git" }, { "version": "b2ed01e7ad3de80333e9b962a44024b094bc0b2b", "lessThan": "3db7d7d583419f7b1f2e141e36418802dbb25cf8", "status": "affected", "versionType": "git" }, { "version": "060f8f3020516d71bc4d6159d4d0beef85606a74", "status": "affected", "versionType": "git" }, { "version": "0124a09e3e5f5f6080efe9663b27af27933f8382", "status": "affected", "versionType": "git" }, { "version": "6.18.57", "lessThan": "6.19", "status": "affected", "versionType": "semver" }, { "version": "7.0.10", "lessThan": "7.1", "status": "affected", "versionType": "semver" } ] }, { "product": "Linux", "vendor": "Linux", "defaultStatus": "affected", "repo": "https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git", "programFiles": [ "drivers/gpu/drm/ttm/ttm_bo.c" ], "versions": [ { "version": "7.1", "status": "affected" }, { "version": "0", "lessThan": "7.1", "status": "unaffected", "versionType": "semver" }, { "version": "7.2.8", "lessThanOrEqual": "7.2.*", "status": "unaffected", "versionType": "semver" }, { "version": "7.3", "lessThanOrEqual": "*", "status": "unaffected", "versionType": "original_commit_for_fix" } ] } ], "cpeApplicability": [ { "nodes": [ { "operator": "OR", "negate": false, "cpeMatch": [ { "vulnerable": true, "criteria": "cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:*", "versionStartIncluding": "7.1", "versionEndExcluding": "7.2.8" }, { "vulnerable": true, "criteria": "cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:*", "versionStartIncluding": "7.1", "versionEndExcluding": "7.3" }, { "vulnerable": true, "criteria": "cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:*", "versionStartIncluding": "6.18.57" }, { "vulnerable": true, "criteria": "cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:*", "versionStartIncluding": "7.0.10" } ] } ] } ], "references": [ { "url": "https://git.kernel.org/stable/c/1169fe8c11ca45e3f91d59a73eb271d0ca8a7fb0" }, { "url": "https://git.kernel.org/stable/c/3db7d7d583419f7b1f2e141e36418802dbb25cf8" } ], "title": "drm/ttm: fix swapped-out resources never leaving their bulk_move range", "x_generator": { "engine": "bippy-1.2.0" } } } }