From nobody Mon Jul 27 10:41:04 2026 Received: from mail-qk1-f178.google.com (mail-qk1-f178.google.com [209.85.222.178]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 358B82F39AB for ; Mon, 15 Jun 2026 23:50:02 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.222.178 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781567404; cv=none; b=JDMa+tKj3zVtDgI7bbyoIPlvQuQtT+1k946Tqav4lWmSNESQh+17MGXify98350FOj85+GGmMNP8wfzmlKzS7ZlXopZKlKFl1rpg/vpdr5lPhJC9L4O/qJA52qEy3PKcpD9vH60ZpBZVm6dk6VelMM6XjXx4Z+s9tUte4PWqv68= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781567404; c=relaxed/simple; bh=1KAwpuaqunao85edd0BfgLzDtsviQonKpCBP9gPML0U=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=UoHUv2hg4lsmTS5Z3maPNXA4nyGUUzHuYQOB2rZ/16t04zN0OaCLPVFXYbfsFlC7/9E1w1yixYddsZqRTQHZGebjcPPGlQnuqdB3q3hUJ0XtJuZcvpA9kecUoC7RnwpBlfy2p5vIOUYKJHWFTAIoi9JhBnrd0cML+CUvOKI+U9c= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=PidoONYI; arc=none smtp.client-ip=209.85.222.178 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="PidoONYI" Received: by mail-qk1-f178.google.com with SMTP id af79cd13be357-9157b895c57so361947185a.3 for ; Mon, 15 Jun 2026 16:50:01 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1781567401; x=1782172201; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=aD59fWP6U+2oV2sPLiRI6B5viEMhkSgKAPCznTndvPM=; b=PidoONYIQpUK5QCw8Il/XFS3GJwJAWJHvgW4Yt9EOsR3iNEdVE0uZiQz9xxD/75jBh O691Fr117H8nz6TIFaKCoUav1Uqtxbf9TVZHQc74r8AkSPTNFze80i45EmNdahvSRoQE n7Supc+Y9VOaqxIRCRxtKDskgznO5TONOSI0mkEY32oC/dXKR5UEV2eF1747pA0m4DDJ KHt3e46UXSDAKHpIie1bo5DXdin6Sd1bzr4NJGbA4QCzGDEOl4Cexma12N4tM7G3grA1 oj4vwxiExHVkBdl3eFEkJ9q8rqoO223bD33165nbBNJ9OfoAsN5yJO5Gjo6NGcURdRbE ZcWw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1781567401; x=1782172201; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=aD59fWP6U+2oV2sPLiRI6B5viEMhkSgKAPCznTndvPM=; b=EAY9qwoIbZUq+5NcrhD7sj0Rw2oHFAy3PhrnI6+o2ISWm8OOsDXTRV2fo8BhqsmOUG 7jw02s/nGcjfm1vkygkLZGROuajW20SUbxNC1LHTtN/A2sqVXHm1BE/g24kp+yJjrCf/ XW/in4JiLqXXUzqTL/g8sdZ164uY6nKIVnXwg3aW16drJKLGvahpTWbuGFdTqi8nGRiS DYQNsPI+BI0rPK814v0d/Aya/dJvBx+JNN/RZXvSoFB9r9ll28NCsCq+LSgq6nLCscXW HvXXIUyXVhSgAENwjku2EZkzme80Ni4o5TC2yZC69JBqNM6rU4ftd/dPBcAwZbqNuB+0 MazA== X-Forwarded-Encrypted: i=1; AFNElJ/ihHg948JxooMdj0OoV6e3TZWvzO6+LYzbVeTeM9CtV8VSxpESVAXqKijdyvn7DfD71JgN7+ozm6EKAjM=@vger.kernel.org X-Gm-Message-State: AOJu0YwyMO2H4Jdg/7WBhvwO4z7DvQGwZ4REmCY5367PmCB/ipnzwm6m owts93ufVUrvQP57syAYc13fSuA6au99bPEPm2Flp/diuVMMClZx0TmWrK0a8tAx X-Gm-Gg: Acq92OFh1EkI73VRy/wdrjIDi0YZrmivM6kkREO51L6Bx76w5eA9NaSOHmMX7qQdND3 NbS8E2CnOR7fQg1c9pzyXap4dBIVPpDnZ6N8FjKngQoct3E3+r174wzXM1o6lgcciXv2wkrDkXN TeHmtSJPlyQLMQnpv1F6T5qMo6UgDan8k+b2d150ScrulSYTipifVKd6rxn9F84/vpGh+yRpXBx lExubqG77LjlkgiW9bti86A0wfAWwB5Pc02ll1n+cbm3V7LfbOGGIQ7FxhyRi9G6IVyYC1S14Tw M/SyyTZCD1VpXdYIw0Y2NGoQJzCjpicUjqQwlm1Bn/5doYejKCehmm4sRKNOqW6bsOGkWSK7/NL urijuYtx/XMRPEQw0skisZzzxx8awA30EwzZnxQr+z5gDPIhrYjAKyduLREC7Hmv8BfPoR8bSpP Q/fAyhnReV3v/XW8U07mEmP0xMoVFhpN7+iv+Vy49xKwIsXYHdHGdQttPdxJsy1oTKKluUZK9qR LrozECCdUZE X-Received: by 2002:a05:620a:4720:b0:915:8f76:7ffa with SMTP id af79cd13be357-917f1474c76mr2073103685a.45.1781567400807; Mon, 15 Jun 2026 16:50:00 -0700 (PDT) Received: from tropical-turnip.tail32462.ts.net ([216.132.43.94]) by smtp.gmail.com with ESMTPSA id af79cd13be357-9161a00af50sm1387942285a.30.2026.06.15.16.49.59 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 15 Jun 2026 16:50:00 -0700 (PDT) From: Samuel Ainsworth To: =?UTF-8?q?Christian=20K=C3=B6nig?= , Huang Rui Cc: =?UTF-8?q?Thomas=20Hellstr=C3=B6m?= , Matthew Auld , Matthew Brost , dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, Samuel Ainsworth , stable@vger.kernel.org Subject: [PATCH v1 1/2] drm/ttm: don't leave bulk_move cursor dangling for unevictable resources Date: Mon, 15 Jun 2026 19:49:21 -0400 Message-ID: <20260615234922.151263-2-skainsworth@gmail.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260615234922.151263-1-skainsworth@gmail.com> References: <20260615234922.151263-1-skainsworth@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" ttm_resource_add_bulk_move() and ttm_resource_del_bulk_move() both act only when the resource is evictable (!ttm_resource_unevictable()). A resource is added to its bo's bulk_move cursor (pos->first / pos->last) while evictable, but it can become unevictable -- pinned or swapped -- after it has been added. ttm_resource_del_bulk_move() is reached both when the resource is freed (ttm_resource_free()) and when the bo's bulk_move is cleared on teardown (ttm_bo_set_bulk_move()). If the resource has become unevictable by then, the del is skipped, so pos->first / pos->last are left pointing at it. Once the resource is freed the cursor dangles, and the next ttm_resource_add_bulk_move() / ttm_resource_move_to_lru_tail() on that bulk_move dereferences it: a use-after-free read of pos->first->bo->base.resv (the WARN_ON in ttm_lru_bulk_move_add()) followed by a list_move() through freed memory that corrupts the LRU list. With CONFIG_DEBUG_LIST this manifests as a fatal "list_del corruption" BUG. On a Framework 13 (AMD Ryzen 7040, gfx1103) this is hit via hibernation: a buffer object swapped out during hibernate (its resource becomes unevictable) is later closed after resume (amdgpu_gem_object_close -> amdgpu_vm_bo_del -> ttm_bo_set_bulk_move()), which skips removing its resource from the VM's bulk_move cursor; a later GEM allocation on that cursor then faults. KASAN reports a slab-use-after-free in ttm_resource_add_bulk_move(). Track whether a resource is actually on the bulk_move cursor with a new ttm_resource::bulk_move flag, set when it is added, and remove based on that flag rather than on the resource's current evictability. The del then always undoes what the add did, regardless of any pin/swap transition in between. Fixes: fc5d96670eb2 ("drm/ttm: Move swapped objects off the manager's LRU l= ist") Cc: stable@vger.kernel.org Closes: https://gitlab.freedesktop.org/drm/amd/-/issues/5387 Signed-off-by: Samuel Ainsworth --- drivers/gpu/drm/ttm/ttm_resource.c | 18 +++++++++++++++--- include/drm/ttm/ttm_resource.h | 9 +++++++++ 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/ttm/ttm_resource.c b/drivers/gpu/drm/ttm/ttm_r= esource.c index 192fca24f37e..1a031ef151a7 100644 --- a/drivers/gpu/drm/ttm/ttm_resource.c +++ b/drivers/gpu/drm/ttm/ttm_resource.c @@ -280,16 +280,27 @@ static bool ttm_resource_unevictable(struct ttm_resou= rce *res, struct ttm_buffer void ttm_resource_add_bulk_move(struct ttm_resource *res, struct ttm_buffer_object *bo) { - if (bo->bulk_move && !ttm_resource_unevictable(res, bo)) + if (bo->bulk_move && !ttm_resource_unevictable(res, bo)) { ttm_lru_bulk_move_add(bo->bulk_move, res); + res->bulk_move =3D true; + } } =20 /* Remove the resource from a bulk move if the BO is configured for it */ void ttm_resource_del_bulk_move(struct ttm_resource *res, struct ttm_buffer_object *bo) { - if (bo->bulk_move && !ttm_resource_unevictable(res, bo)) + /* + * Remove based on whether the resource was actually added, not on its + * current evictability: a resource can become unevictable (pinned or + * swapped) after being added, and must still be taken off the bulk_move + * cursor before it is freed -- otherwise pos->first/last are left + * dangling at freed memory. + */ + if (res->bulk_move) { ttm_lru_bulk_move_del(bo->bulk_move, res); + res->bulk_move =3D false; + } } =20 /* Move a resource to the LRU or bulk tail */ @@ -303,7 +314,7 @@ void ttm_resource_move_to_lru_tail(struct ttm_resource = *res) if (ttm_resource_unevictable(res, bo)) { list_move_tail(&res->lru.link, &bdev->unevictable); =20 - } else if (bo->bulk_move) { + } else if (res->bulk_move) { struct ttm_lru_bulk_move_pos *pos =3D ttm_lru_bulk_move_pos(bo->bulk_move, res); =20 @@ -339,6 +350,7 @@ void ttm_resource_init(struct ttm_buffer_object *bo, res->bus.is_iomem =3D false; res->bus.caching =3D ttm_cached; res->bo =3D bo; + res->bulk_move =3D false; =20 man =3D ttm_manager_type(bo->bdev, place->mem_type); spin_lock(&bo->bdev->lru_lock); diff --git a/include/drm/ttm/ttm_resource.h b/include/drm/ttm/ttm_resource.h index 33e80f30b8b8..1fedf75bab96 100644 --- a/include/drm/ttm/ttm_resource.h +++ b/include/drm/ttm/ttm_resource.h @@ -274,6 +274,15 @@ struct ttm_resource { * @lru: Least recently used list, see &ttm_resource_manager.lru */ struct ttm_lru_item lru; + + /** + * @bulk_move: Whether this resource is currently tracked by its bo's + * &ttm_buffer_object.bulk_move cursor. Recorded when the resource is + * added so the matching del removes it even if the resource has since + * become unevictable (pinned or swapped) -- otherwise the cursor would + * be left pointing at this resource after it is freed. + */ + bool bulk_move; }; =20 /** --=20 2.54.0 From nobody Mon Jul 27 10:41:04 2026 Received: from mail-qk1-f179.google.com (mail-qk1-f179.google.com [209.85.222.179]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7FF0A3290C7 for ; Mon, 15 Jun 2026 23:50:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.222.179 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781567405; cv=none; b=ZkKum3bDXf7MxKMFE9F4ubunJDxPKPuvQ41XY/Gc3KwNAnNX0Vn4daTj/6VhQHSQWqf2oH6MoVhZPcCjX26q6R+CMBNVE+/3qOMCMvVWVBbZini63oC5tiF5fMkH5yBTbrbcPJfirS9obkO1EtchEUn+i1A56++qCFU2+nVQxSY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781567405; c=relaxed/simple; bh=y9BehPLSpoMGhJv/ElIdSNj/5ox3Roh1VLY2QLdTmYw=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Kay+BWLUUT48wyiOiBEPiD2kUZJh2w2voEo7QCahrkaT1wyl2hWNEhJ7Mi8Bf8uSH8J0+88ASf7zKlV7epiFMF3LgA8gk3t0yRK2UWTMxRtpEZziLibGZ1wyaCzM7KBCPMgttsezZqaD4sfVtcznN3m9e44Qk8JSPXrL2IUp/Tw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=jcDTP7nW; arc=none smtp.client-ip=209.85.222.179 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="jcDTP7nW" Received: by mail-qk1-f179.google.com with SMTP id af79cd13be357-915d64fead9so504925585a.0 for ; Mon, 15 Jun 2026 16:50:03 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1781567402; x=1782172202; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=gMomLA5yc+OAXvOs3SpKTXvia9aXXpiO5BxVYuDjyFA=; b=jcDTP7nWBafylig6+J7+BzSNN3BDKEfI7fh2lVUZ9vswEy6WEOfk20xJHRnRuKnvUQ 9wwnckSoEXMTP0m5nxLIcStTDs37X1zgp5xX8ROh5zTXIWejvBZctVtSddm8CjzA+uEX zf/RQRVZgLu0G0OudhK5tgtMAsLaM2U0bIxOAHItAOOfnroFKVETM2Q8HD/E7XeeEYnI gnfpRpG7hNEu3dSHqjPDntFFg4JGfsUfDWU450gCuGG6bHaYPMhLAmOUuHXpz8OYIUHY dnwQpQXrU2giJK35SjSVtxpG/sXU0nrW9x28vQT0SKBULniENx+18HngwHscjvQJhbub k+pA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1781567402; x=1782172202; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=gMomLA5yc+OAXvOs3SpKTXvia9aXXpiO5BxVYuDjyFA=; b=WHC+3qIl/CQTEVs6eAfUKp7yObtrqCPjMNDbzvC9i/eUv5Ms4zuyuaxPnbPh/jUMZj ylYc8QTCCv8fvH3MaPFyS9FiTQrkonJQWhTMxrZnUTN+Eb22vE4srF51PcGPaA4Q7hl3 /86cG2P2D1rA6PubQzYlECHO8RncEXzR13ENSyO+1iH0oeZv+CgYASVQ9Pn1KGjSp5e5 QEqN2k4EeAzf6s9rtH7T9x8pYC25VVihc+f213u0EamgvMaRKKwVxGEI9w/CCWoIHrmk bqj2ljAVSrO5J42LUtqQaXIlu5tgGCOifpKBR67bePuwKr7hsQmbbzAueAStDt6cydA+ swfg== X-Forwarded-Encrypted: i=1; AFNElJ9O/UJQMBZ/GNgX9rSSpkOagk+tPtVKfhuTli7s8FN6r8JXfEpPUAPpp7sCV3kFC8FonOc3GoHt1vvgegQ=@vger.kernel.org X-Gm-Message-State: AOJu0YyQYuTa5c0FrbS/RgkyCoN5/FnCMAEY0ctWgXJA930WeUxurrSg WbMYv+qRGhLATxaNydYm78G0uVRPGMIDnFEmFHryg1lGKDSnrr8unnba X-Gm-Gg: Acq92OH0nNC25ttabhVRFMgftXfy0cHlZIwdy0BC2Os8fP52AzB7AOQ9w9ICTBeG4Sv nuGZBl606MRZwHFK6FN67/dI7YlPn6uJ998PAuiF2ZR747BXMy8/kunEiuHa7zlXpbb+ZKoBc3o fDB3+kGivWo7iRcwANreEy4oDA8Bx99uxNCRuEkBLl2etvqj8EPTo62UM5e52r9aOlp7PLiARcy +ftREHRqm4wCEzASPOFS7bfi/CYRCi3PzHjR1dK4BSoP25LZRHTmXAYRXpeq+LfMjToO0Z3nf0R nOIWidvYZhHyzKYwm9qEKTxlN6dei9lUjnLiyqB50LsZxoCTyiEK5MhIfUyTct0LGbK1te0v552 pZZsTfxfkOT/VELRiK04MZvO6Wru1hdm2PKtrWfc3tAMeNyazJuzGHE0ezij+1MePQa4zoiZQAa 9EyqxBzcbJMKf8pyfXW3ucN0+zwKfqcL0X6iD9xeRxnhfFAfCpWgdNJ39F5Bp0H+mvAGSzSC9x7 8mGvN6Zo7vh X-Received: by 2002:a05:620a:4008:b0:8f2:1ccf:46d2 with SMTP id af79cd13be357-91c2f1caa82mr230132685a.32.1781567402022; Mon, 15 Jun 2026 16:50:02 -0700 (PDT) Received: from tropical-turnip.tail32462.ts.net ([216.132.43.94]) by smtp.gmail.com with ESMTPSA id af79cd13be357-9161a00af50sm1387942285a.30.2026.06.15.16.50.01 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 15 Jun 2026 16:50:01 -0700 (PDT) From: Samuel Ainsworth To: =?UTF-8?q?Christian=20K=C3=B6nig?= , Huang Rui Cc: =?UTF-8?q?Thomas=20Hellstr=C3=B6m?= , Matthew Auld , Matthew Brost , dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, Samuel Ainsworth Subject: [PATCH v1 2/2] drm/ttm/tests: add bulk_move cursor regression tests Date: Mon, 15 Jun 2026 19:49:22 -0400 Message-ID: <20260615234922.151263-3-skainsworth@gmail.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260615234922.151263-1-skainsworth@gmail.com> References: <20260615234922.151263-1-skainsworth@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add two kunit tests for the bulk_move LRU cursor accounting fixed in the previous commit: - ttm_bo_bulk_move_swapped_free_dangles frees a swapped (unevictable) resource that is still referenced by the bulk_move cursor and asserts the cursor no longer points at the freed resource. - ttm_bo_bulk_move_dangling_corrupts then allocates on the same bulk_move, which without the fix dereferences the dangling cursor -- reported by KASAN as a slab-use-after-free in ttm_resource_add_bulk_move(). Both run with no GPU under the existing TTM mock device. Without the fix the first fails its expectation and the second triggers the use-after-free; with it the whole TTM kunit suite passes. Signed-off-by: Samuel Ainsworth --- drivers/gpu/drm/ttm/tests/ttm_bo_test.c | 163 ++++++++++++++++++++++++ 1 file changed, 163 insertions(+) diff --git a/drivers/gpu/drm/ttm/tests/ttm_bo_test.c b/drivers/gpu/drm/ttm/= tests/ttm_bo_test.c index d468f8322072..07ad1a7821fd 100644 --- a/drivers/gpu/drm/ttm/tests/ttm_bo_test.c +++ b/drivers/gpu/drm/ttm/tests/ttm_bo_test.c @@ -603,7 +603,170 @@ static void ttm_bo_multiple_pin_one_unpin(struct kuni= t *test) ttm_resource_free(bo, &res); } =20 +/* + * Regression tests for bulk_move LRU cursor accounting: a resource added = to a + * bo's bulk_move cursor can become unevictable (pinned or swapped) before= it is + * freed. ttm_resource_del_bulk_move() must still take it off the cursor, = or + * pos->first/last are left dangling at the freed resource. + */ + +/* + * Free a swapped (unevictable) resource that is still referenced by the + * bulk_move cursor and check that the cursor no longer points at it. + */ +static void ttm_bo_bulk_move_swapped_free_dangles(struct kunit *test) +{ + struct ttm_test_devices *priv =3D test->priv; + struct ttm_lru_bulk_move lru_bulk_move; + struct ttm_lru_bulk_move_pos *pos; + struct ttm_operation_ctx ctx =3D { }; + struct ttm_resource *res1, *res1_saved; + struct ttm_buffer_object *bo1; + struct ttm_device *ttm_dev; + struct ttm_place *place; + struct dma_resv *resv; + struct ttm_tt *tt; + int err; + + ttm_lru_bulk_move_init(&lru_bulk_move); + place =3D ttm_place_kunit_init(test, TTM_PL_SYSTEM, 0); + + ttm_dev =3D kunit_kzalloc(test, sizeof(*ttm_dev), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ttm_dev); + err =3D ttm_device_kunit_init(priv, ttm_dev, 0); + KUNIT_ASSERT_EQ(test, err, 0); + priv->ttm_dev =3D ttm_dev; + + resv =3D kunit_kzalloc(test, sizeof(*resv), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, resv); + dma_resv_init(resv); + + bo1 =3D ttm_bo_kunit_init(test, priv, BO_SIZE, resv); + + /* bo1: put a SYSTEM resource on the bulk_move pos */ + dma_resv_lock(bo1->base.resv, NULL); + ttm_bo_set_bulk_move(bo1, &lru_bulk_move); + err =3D ttm_resource_alloc(bo1, place, &res1, NULL); + KUNIT_ASSERT_EQ(test, err, 0); + bo1->resource =3D res1; + + pos =3D &lru_bulk_move.pos[TTM_PL_SYSTEM][bo1->priority]; + /* res1 is now tracked by the bulk_move pos */ + KUNIT_EXPECT_PTR_EQ(test, pos->first, res1); + KUNIT_EXPECT_PTR_EQ(test, pos->last, res1); + + /* make bo1->ttm swapped so res1 is "unevictable" (ttm_resource_is_swappe= d) */ + tt =3D kunit_kzalloc(test, sizeof(*tt), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, tt); + err =3D ttm_tt_init(tt, bo1, 0, ttm_cached, 0); + KUNIT_ASSERT_EQ(test, err, 0); + bo1->ttm =3D tt; + err =3D ttm_tt_populate(ttm_dev, tt, &ctx); + KUNIT_ASSERT_EQ(test, err, 0); + ttm_tt_swapout(ttm_dev, tt, GFP_KERNEL); + KUNIT_ASSERT_TRUE(test, tt->page_flags & TTM_TT_FLAG_SWAPPED); + + /* res1 is still tracked by the pos right before the free */ + KUNIT_EXPECT_PTR_EQ(test, pos->first, res1); + + /* + * Free res1 while it is swapped (unevictable). ttm_resource_free() calls + * ttm_resource_del_bulk_move(), which is a no-op for an unevictable + * resource -- so res1 is NOT removed from the bulk_move pos before the + * underlying memory is freed. + */ + res1_saved =3D res1; + ttm_resource_free(bo1, &res1); + dma_resv_unlock(bo1->base.resv); + + /* + * A correct implementation must not leave the bulk_move pos pointing at + * the freed resource. On the buggy code these still equal the freed + * res1_saved (a dangling pointer that the next add/move dereferences). + */ + KUNIT_EXPECT_PTR_NE(test, pos->first, res1_saved); + KUNIT_EXPECT_PTR_NE(test, pos->last, res1_saved); + + dma_resv_fini(resv); +} + +/* + * After a swapped-free leaves the bulk_move cursor dangling, a later + * ttm_resource_alloc() on the same bulk_move dereferences the freed curso= r in + * ttm_lru_bulk_move_add() (use-after-free, catchable with KASAN) and corr= upts + * the LRU list. + */ +static void ttm_bo_bulk_move_dangling_corrupts(struct kunit *test) +{ + struct ttm_test_devices *priv =3D test->priv; + struct ttm_lru_bulk_move lru_bulk_move; + struct ttm_operation_ctx ctx =3D { }; + struct ttm_buffer_object *bo1, *bo2; + struct ttm_resource *res1, *res2; + struct ttm_device *ttm_dev; + struct ttm_place *place; + struct dma_resv *resv1, *resv2; + struct ttm_tt *tt; + int err; + + ttm_lru_bulk_move_init(&lru_bulk_move); + place =3D ttm_place_kunit_init(test, TTM_PL_SYSTEM, 0); + + ttm_dev =3D kunit_kzalloc(test, sizeof(*ttm_dev), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ttm_dev); + err =3D ttm_device_kunit_init(priv, ttm_dev, 0); + KUNIT_ASSERT_EQ(test, err, 0); + priv->ttm_dev =3D ttm_dev; + + resv1 =3D kunit_kzalloc(test, sizeof(*resv1), GFP_KERNEL); + resv2 =3D kunit_kzalloc(test, sizeof(*resv2), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, resv1); + KUNIT_ASSERT_NOT_NULL(test, resv2); + dma_resv_init(resv1); + dma_resv_init(resv2); + + bo1 =3D ttm_bo_kunit_init(test, priv, BO_SIZE, resv1); + bo2 =3D ttm_bo_kunit_init(test, priv, BO_SIZE, resv2); + + /* bo1: resource on the bulk_move pos, then swap + free -> dangling pos */ + dma_resv_lock(bo1->base.resv, NULL); + ttm_bo_set_bulk_move(bo1, &lru_bulk_move); + err =3D ttm_resource_alloc(bo1, place, &res1, NULL); + KUNIT_ASSERT_EQ(test, err, 0); + bo1->resource =3D res1; + + tt =3D kunit_kzalloc(test, sizeof(*tt), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, tt); + err =3D ttm_tt_init(tt, bo1, 0, ttm_cached, 0); + KUNIT_ASSERT_EQ(test, err, 0); + bo1->ttm =3D tt; + err =3D ttm_tt_populate(ttm_dev, tt, &ctx); + KUNIT_ASSERT_EQ(test, err, 0); + ttm_tt_swapout(ttm_dev, tt, GFP_KERNEL); + + ttm_resource_free(bo1, &res1); + dma_resv_unlock(bo1->base.resv); + + /* + * bo2 allocates on the SAME bulk_move. ttm_lru_bulk_move_add() reads the + * dangling pos->first (freed res1) and list_move()s relative to the freed + * pos->last -> use-after-free + list corruption. + */ + dma_resv_lock(bo2->base.resv, NULL); + ttm_bo_set_bulk_move(bo2, &lru_bulk_move); + err =3D ttm_resource_alloc(bo2, place, &res2, NULL); + KUNIT_ASSERT_EQ(test, err, 0); + bo2->resource =3D res2; + ttm_resource_free(bo2, &res2); + dma_resv_unlock(bo2->base.resv); + + dma_resv_fini(resv1); + dma_resv_fini(resv2); +} + static struct kunit_case ttm_bo_test_cases[] =3D { + KUNIT_CASE(ttm_bo_bulk_move_swapped_free_dangles), + KUNIT_CASE(ttm_bo_bulk_move_dangling_corrupts), KUNIT_CASE_PARAM(ttm_bo_reserve_optimistic_no_ticket, ttm_bo_reserve_gen_params), KUNIT_CASE(ttm_bo_reserve_locked_no_sleep), --=20 2.54.0