{ "dataType": "CVE_RECORD", "dataVersion": "5.2", "cveMetadata": { "cveId": "CVE-2025-38276", "assignerOrgId": "416baaa9-dc9f-4396-8d5f-8c081fb06d67", "state": "PUBLISHED", "assignerShortName": "Linux", "dateReserved": "2025-04-16T04:51:23.998Z", "datePublished": "2025-07-10T07:41:56.288Z", "dateUpdated": "2026-08-05T12:00:55.718Z" }, "containers": { "cna": { "providerMetadata": { "orgId": "416baaa9-dc9f-4396-8d5f-8c081fb06d67", "shortName": "Linux", "dateUpdated": "2026-08-05T12:00:55.718Z" }, "descriptions": [ { "lang": "en", "value": "In the Linux kernel, the following vulnerability has been resolved:\n\nfs/dax: Fix \"don't skip locked entries when scanning entries\"\n\nCommit 6be3e21d25ca (\"fs/dax: don't skip locked entries when scanning\nentries\") introduced a new function, wait_entry_unlocked_exclusive(),\nwhich waits for the current entry to become unlocked without advancing\nthe XArray iterator state.\n\nWaiting for the entry to become unlocked requires dropping the XArray\nlock. This requires calling xas_pause() prior to dropping the lock\nwhich leaves the xas in a suitable state for the next iteration. However\nthis has the side-effect of advancing the xas state to the next index.\nNormally this isn't an issue because xas_for_each() contains code to\ndetect this state and thus avoid advancing the index a second time on\nthe next loop iteration.\n\nHowever both callers of and wait_entry_unlocked_exclusive() itself\nsubsequently use the xas state to reload the entry. As xas_pause()\nupdated the state to the next index this will cause the current entry\nwhich is being waited on to be skipped. This caused the following\nwarning to fire intermittently when running xftest generic/068 on an XFS\nfilesystem with FS DAX enabled:\n\n[ 35.067397] ------------[ cut here ]------------\n[ 35.068229] WARNING: CPU: 21 PID: 1640 at mm/truncate.c:89 truncate_folio_batch_exceptionals+0xd8/0x1e0\n[ 35.069717] Modules linked in: nd_pmem dax_pmem nd_btt nd_e820 libnvdimm\n[ 35.071006] CPU: 21 UID: 0 PID: 1640 Comm: fstest Not tainted 6.15.0-rc7+ #77 PREEMPT(voluntary)\n[ 35.072613] Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS rel-1.16.3-0-ga6ed6b701f0a-prebuilt.qemu.org 04/01/204\n[ 35.074845] RIP: 0010:truncate_folio_batch_exceptionals+0xd8/0x1e0\n[ 35.075962] Code: a1 00 00 00 f6 47 0d 20 0f 84 97 00 00 00 4c 63 e8 41 39 c4 7f 0b eb 61 49 83 c5 01 45 39 ec 7e 58 42 f68\n[ 35.079522] RSP: 0018:ffffb04e426c7850 EFLAGS: 00010202\n[ 35.080359] RAX: 0000000000000000 RBX: ffff9d21e3481908 RCX: ffffb04e426c77f4\n[ 35.081477] RDX: ffffb04e426c79e8 RSI: ffffb04e426c79e0 RDI: ffff9d21e34816e8\n[ 35.082590] RBP: ffffb04e426c79e0 R08: 0000000000000001 R09: 0000000000000003\n[ 35.083733] R10: 0000000000000000 R11: 822b53c0f7a49868 R12: 000000000000001f\n[ 35.084850] R13: 0000000000000000 R14: ffffb04e426c78e8 R15: fffffffffffffffe\n[ 35.085953] FS: 00007f9134c87740(0000) GS:ffff9d22abba0000(0000) knlGS:0000000000000000\n[ 35.087346] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033\n[ 35.088244] CR2: 00007f9134c86000 CR3: 000000040afff000 CR4: 00000000000006f0\n[ 35.089354] Call Trace:\n[ 35.089749] \n[ 35.090168] truncate_inode_pages_range+0xfc/0x4d0\n[ 35.091078] truncate_pagecache+0x47/0x60\n[ 35.091735] xfs_setattr_size+0xc7/0x3e0\n[ 35.092648] xfs_vn_setattr+0x1ea/0x270\n[ 35.093437] notify_change+0x1f4/0x510\n[ 35.094219] ? do_truncate+0x97/0xe0\n[ 35.094879] do_truncate+0x97/0xe0\n[ 35.095640] path_openat+0xabd/0xca0\n[ 35.096278] do_filp_open+0xd7/0x190\n[ 35.096860] do_sys_openat2+0x8a/0xe0\n[ 35.097459] __x64_sys_openat+0x6d/0xa0\n[ 35.098076] do_syscall_64+0xbb/0x1d0\n[ 35.098647] entry_SYSCALL_64_after_hwframe+0x77/0x7f\n[ 35.099444] RIP: 0033:0x7f9134d81fc1\n[ 35.100033] Code: 75 57 89 f0 25 00 00 41 00 3d 00 00 41 00 74 49 80 3d 2a 26 0e 00 00 74 6d 89 da 48 89 ee bf 9c ff ff ff5\n[ 35.102993] RSP: 002b:00007ffcd41e0d10 EFLAGS: 00000202 ORIG_RAX: 0000000000000101\n[ 35.104263] RAX: ffffffffffffffda RBX: 0000000000000242 RCX: 00007f9134d81fc1\n[ 35.105452] RDX: 0000000000000242 RSI: 00007ffcd41e1200 RDI: 00000000ffffff9c\n[ 35.106663] RBP: 00007ffcd41e1200 R08: 0000000000000000 R09: 0000000000000064\n[ 35.107923] R10: 00000000000001a4 R11: 0000000000000202 R12: 0000000000000066\n[ 35.109112] R13: 0000000000100000 R14: 0000000000100000 R15: 0000000000000400\n[ 35.110357] \n[ 35.110769] irq event stamp: 8415587\n[ 35.111486] hardirqs last enabled at (8415599): [] __up_console_se\n---truncated---" } ], "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 vulnerable code is reached purely through local filesystem syscalls (open(O_TRUNC)/ftruncate, fallocate PUNCH_HOLE, mmap faults) on a DAX-mounted filesystem, as shown by the reported do_sys_openat→do_truncate→xfs_setattr_size→truncate_pagecache trace. There is no network-facing consumer of fs/dax.c.\nAC:L - The attacker controls both sides of the race — one thread loops truncate/punch-hole while sibling threads fault pages or hold O_DIRECT/DMA pins to keep DAX entries locked, which is exactly the window that makes wait_entry_unlocked_exclusive() skip an entry. It reproduces intermittently under an ordinary fstests run, so a tight loop wins it readily, and the DAX deployment (pmem/CXL-backed XFS/ext4, virtiofs-DAX guests) is a normal configuration rather than a condition beyond the attacker's control.\nPR:L - Only an unprivileged local account with write access to a file on the DAX filesystem is required; no capability check, mount privilege, or admin gate exists anywhere along notify_change→xfs_setattr_size→dax_break_layout→dax_delete_mapping_r ange. In a virtiofs-DAX guest an ordinary user inside the VM suffices.\nUI:N - The attacker performs every operation itself — truncating/punching its own file while concurrently faulting or pinning its pages. No victim action of any kind is needed.\nS:U - The corruption stays within the kernel's own security authority — filesystem block allocation, page-cache/XArray state and inode structures. No hypervisor, IOMMU or sandbox boundary is crossed.\nC:H - Missing a DMA-busy page lets the filesystem free and reallocate blocks while reads are still being DMA'd from them, and the __dax_clear_dirty_range skip leaves a stale DAX entry mapping the pre-CoW shared extent, so an attacker can read data belonging to other files and users; the never-disassociated folio also leaves folio->mapping dangling into a freed inode for memory-failure code to dereference.\nI:H - The same missed-busy-page path is precisely what commit 6be3e21d25ca described as \"file systems to free blocks whilst DMA operations are ongoing which can lead to file system corruption\" — in-flight DMA writes land in blocks already reallocated to other files or metadata, and stale DAX entries let writes go to the wrong physical extent on reflinked files.\nA:H - Leftover DAX entries trip WARN_ON_ONCE at mm/truncate.c:89 and, because mapping->nrpages is decremented for the wrong entry (PTE vs PMD order, so it can also underflow), inode eviction hits BUG_ON(inode->i_data.nrpages) in clear_inode() and panics; the mis-targeted put_unlocked_entry() wakeup can additionally strand tasks in unkillable TASK_UNINTERRUPTIBLE waits." } ] } ], "affected": [ { "product": "Linux", "vendor": "Linux", "defaultStatus": "unaffected", "repo": "https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git", "programFiles": [ "fs/dax.c" ], "versions": [ { "version": "6be3e21d25ca2dbb7ca4f3f7db808a3e1a944bd1", "lessThan": "61009dd2252ab4391d44a240e891f1e04c00b9ca", "status": "affected", "versionType": "git" }, { "version": "6be3e21d25ca2dbb7ca4f3f7db808a3e1a944bd1", "lessThan": "dd59137bfe70cf3646021b4721e430213b9c71bd", "status": "affected", "versionType": "git" } ] }, { "product": "Linux", "vendor": "Linux", "defaultStatus": "affected", "repo": "https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git", "programFiles": [ "fs/dax.c" ], "versions": [ { "version": "6.15", "status": "affected" }, { "version": "0", "lessThan": "6.15", "status": "unaffected", "versionType": "semver" }, { "version": "6.15.3", "lessThanOrEqual": "6.15.*", "status": "unaffected", "versionType": "semver" }, { "version": "6.16", "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": "6.15", "versionEndExcluding": "6.15.3" }, { "vulnerable": true, "criteria": "cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:*", "versionStartIncluding": "6.15", "versionEndExcluding": "6.16" } ] } ] } ], "references": [ { "url": "https://git.kernel.org/stable/c/61009dd2252ab4391d44a240e891f1e04c00b9ca" }, { "url": "https://git.kernel.org/stable/c/dd59137bfe70cf3646021b4721e430213b9c71bd" } ], "title": "fs/dax: Fix \"don't skip locked entries when scanning entries\"", "x_generator": { "engine": "bippy-1.2.0" } } } }