CVE-2026-31648 (GCVE-0-2026-31648)
Vulnerability from cvelistv5
Published
2026-04-24 14:45
Modified
2026-08-05 12:24
Summary
In the Linux kernel, the following vulnerability has been resolved: mm: filemap: fix nr_pages calculation overflow in filemap_map_pages() When running stress-ng on my Arm64 machine with v7.0-rc3 kernel, I encountered some very strange crash issues showing up as "Bad page state": " [ 734.496287] BUG: Bad page state in process stress-ng-env pfn:415735fb [ 734.496427] page: refcount:0 mapcount:1 mapping:0000000000000000 index:0x4cf316 pfn:0x415735fb [ 734.496434] flags: 0x57fffe000000800(owner_2|node=1|zone=2|lastcpupid=0x3ffff) [ 734.496439] raw: 057fffe000000800 0000000000000000 dead000000000122 0000000000000000 [ 734.496440] raw: 00000000004cf316 0000000000000000 0000000000000000 0000000000000000 [ 734.496442] page dumped because: nonzero mapcount " After analyzing this page’s state, it is hard to understand why the mapcount is not 0 while the refcount is 0, since this page is not where the issue first occurred. By enabling the CONFIG_DEBUG_VM config, I can reproduce the crash as well and captured the first warning where the issue appears: " [ 734.469226] page: refcount:33 mapcount:0 mapping:00000000bef2d187 index:0x81a0 pfn:0x415735c0 [ 734.469304] head: order:5 mapcount:0 entire_mapcount:0 nr_pages_mapped:0 pincount:0 [ 734.469315] memcg:ffff000807a8ec00 [ 734.469320] aops:ext4_da_aops ino:100b6f dentry name(?):"stress-ng-mmaptorture-9397-0-2736200540" [ 734.469335] flags: 0x57fffe400000069(locked|uptodate|lru|head|node=1|zone=2|lastcpupid=0x3ffff) ...... [ 734.469364] page dumped because: VM_WARN_ON_FOLIO((_Generic((page + nr_pages - 1), const struct page *: (const struct folio *)_compound_head(page + nr_pages - 1), struct page *: (struct folio *)_compound_head(page + nr_pages - 1))) != folio) [ 734.469390] ------------[ cut here ]------------ [ 734.469393] WARNING: ./include/linux/rmap.h:351 at folio_add_file_rmap_ptes+0x3b8/0x468, CPU#90: stress-ng-mlock/9430 [ 734.469551] folio_add_file_rmap_ptes+0x3b8/0x468 (P) [ 734.469555] set_pte_range+0xd8/0x2f8 [ 734.469566] filemap_map_folio_range+0x190/0x400 [ 734.469579] filemap_map_pages+0x348/0x638 [ 734.469583] do_fault_around+0x140/0x198 ...... [ 734.469640] el0t_64_sync+0x184/0x188 " The code that triggers the warning is: "VM_WARN_ON_FOLIO(page_folio(page + nr_pages - 1) != folio, folio)", which indicates that set_pte_range() tried to map beyond the large folio’s size. By adding more debug information, I found that 'nr_pages' had overflowed in filemap_map_pages(), causing set_pte_range() to establish mappings for a range exceeding the folio size, potentially corrupting fields of pages that do not belong to this folio (e.g., page->_mapcount). After above analysis, I think the possible race is as follows: CPU 0 CPU 1 filemap_map_pages() ext4_setattr() //get and lock folio with old inode->i_size next_uptodate_folio() ....... //shrink the inode->i_size i_size_write(inode, attr->ia_size); //calculate the end_pgoff with the new inode->i_size file_end = DIV_ROUND_UP(i_size_read(mapping->host), PAGE_SIZE) - 1; end_pgoff = min(end_pgoff, file_end); ...... //nr_pages can be overflowed, cause xas.xa_index > end_pgoff end = folio_next_index(folio) - 1; nr_pages = min(end, end_pgoff) - xas.xa_index + 1; ...... //map large folio filemap_map_folio_range() ...... //truncate folios truncate_pagecache(inode, inode->i_size); To fix this issue, move the 'end_pgoff' calculation before next_uptodate_folio(), so the retrieved folio stays consistent with the file end to avoid ---truncated---
Impacted products
Vendor Product Version
Linux Linux Version: fe601b70eac6cd266e8d7d55030e90a73ed0e339
Version: 743a2753a02e805347969f6f89f38b736850d808
Version: 743a2753a02e805347969f6f89f38b736850d808
Version: 743a2753a02e805347969f6f89f38b736850d808
Version: 743a2753a02e805347969f6f89f38b736850d808
Version: 84ede15f27c06b111d1398dfa80b6fac4b135e34
Version: 6.6.117   
Version: 6.1.159   
Create a notification for this product.
Show details on NVD website


{
  "containers": {
    "cna": {
      "affected": [
        {
          "defaultStatus": "unaffected",
          "product": "Linux",
          "programFiles": [
            "mm/filemap.c"
          ],
          "repo": "https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git",
          "vendor": "Linux",
          "versions": [
            {
              "lessThan": "88591194df736a508dd5461ab2167a61e98caac1",
              "status": "affected",
              "version": "fe601b70eac6cd266e8d7d55030e90a73ed0e339",
              "versionType": "git"
            },
            {
              "lessThan": "633ab680c405ac390e6bec5b74aaf46197c837b6",
              "status": "affected",
              "version": "743a2753a02e805347969f6f89f38b736850d808",
              "versionType": "git"
            },
            {
              "lessThan": "576543bedd616254032d4ebe54a90076f9e31740",
              "status": "affected",
              "version": "743a2753a02e805347969f6f89f38b736850d808",
              "versionType": "git"
            },
            {
              "lessThan": "9316a820b9aae07d44469d6485376dad824c5b3f",
              "status": "affected",
              "version": "743a2753a02e805347969f6f89f38b736850d808",
              "versionType": "git"
            },
            {
              "lessThan": "f58df566524ebcdfa394329c64f47e3c9257516e",
              "status": "affected",
              "version": "743a2753a02e805347969f6f89f38b736850d808",
              "versionType": "git"
            },
            {
              "status": "affected",
              "version": "84ede15f27c06b111d1398dfa80b6fac4b135e34",
              "versionType": "git"
            },
            {
              "lessThan": "6.6.135",
              "status": "affected",
              "version": "6.6.117",
              "versionType": "semver"
            },
            {
              "lessThan": "6.2",
              "status": "affected",
              "version": "6.1.159",
              "versionType": "semver"
            }
          ]
        },
        {
          "defaultStatus": "affected",
          "product": "Linux",
          "programFiles": [
            "mm/filemap.c"
          ],
          "repo": "https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git",
          "vendor": "Linux",
          "versions": [
            {
              "status": "affected",
              "version": "6.12"
            },
            {
              "lessThan": "6.12",
              "status": "unaffected",
              "version": "0",
              "versionType": "semver"
            },
            {
              "lessThanOrEqual": "6.6.*",
              "status": "unaffected",
              "version": "6.6.135",
              "versionType": "semver"
            },
            {
              "lessThanOrEqual": "6.12.*",
              "status": "unaffected",
              "version": "6.12.82",
              "versionType": "semver"
            },
            {
              "lessThanOrEqual": "6.18.*",
              "status": "unaffected",
              "version": "6.18.23",
              "versionType": "semver"
            },
            {
              "lessThanOrEqual": "6.19.*",
              "status": "unaffected",
              "version": "6.19.13",
              "versionType": "semver"
            },
            {
              "lessThanOrEqual": "*",
              "status": "unaffected",
              "version": "7.0",
              "versionType": "original_commit_for_fix"
            }
          ]
        }
      ],
      "cpeApplicability": [
        {
          "nodes": [
            {
              "cpeMatch": [
                {
                  "criteria": "cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:*",
                  "versionEndExcluding": "6.6.135",
                  "versionStartIncluding": "6.6.117",
                  "vulnerable": true
                },
                {
                  "criteria": "cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:*",
                  "versionEndExcluding": "6.12.82",
                  "versionStartIncluding": "6.12",
                  "vulnerable": true
                },
                {
                  "criteria": "cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:*",
                  "versionEndExcluding": "6.18.23",
                  "versionStartIncluding": "6.12",
                  "vulnerable": true
                },
                {
                  "criteria": "cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:*",
                  "versionEndExcluding": "6.19.13",
                  "versionStartIncluding": "6.12",
                  "vulnerable": true
                },
                {
                  "criteria": "cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:*",
                  "versionEndExcluding": "7.0",
                  "versionStartIncluding": "6.12",
                  "vulnerable": true
                },
                {
                  "criteria": "cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:*",
                  "versionStartIncluding": "6.1.159",
                  "vulnerable": true
                }
              ],
              "negate": false,
              "operator": "OR"
            }
          ]
        }
      ],
      "descriptions": [
        {
          "lang": "en",
          "value": "In the Linux kernel, the following vulnerability has been resolved:\n\nmm: filemap: fix nr_pages calculation overflow in filemap_map_pages()\n\nWhen running stress-ng on my Arm64 machine with v7.0-rc3 kernel, I\nencountered some very strange crash issues showing up as \"Bad page state\":\n\n\"\n[  734.496287] BUG: Bad page state in process stress-ng-env  pfn:415735fb\n[  734.496427] page: refcount:0 mapcount:1 mapping:0000000000000000 index:0x4cf316 pfn:0x415735fb\n[  734.496434] flags: 0x57fffe000000800(owner_2|node=1|zone=2|lastcpupid=0x3ffff)\n[  734.496439] raw: 057fffe000000800 0000000000000000 dead000000000122 0000000000000000\n[  734.496440] raw: 00000000004cf316 0000000000000000 0000000000000000 0000000000000000\n[  734.496442] page dumped because: nonzero mapcount\n\"\n\nAfter analyzing this page\u2019s state, it is hard to understand why the\nmapcount is not 0 while the refcount is 0, since this page is not where\nthe issue first occurred.  By enabling the CONFIG_DEBUG_VM config, I can\nreproduce the crash as well and captured the first warning where the issue\nappears:\n\n\"\n[  734.469226] page: refcount:33 mapcount:0 mapping:00000000bef2d187 index:0x81a0 pfn:0x415735c0\n[  734.469304] head: order:5 mapcount:0 entire_mapcount:0 nr_pages_mapped:0 pincount:0\n[  734.469315] memcg:ffff000807a8ec00\n[  734.469320] aops:ext4_da_aops ino:100b6f dentry name(?):\"stress-ng-mmaptorture-9397-0-2736200540\"\n[  734.469335] flags: 0x57fffe400000069(locked|uptodate|lru|head|node=1|zone=2|lastcpupid=0x3ffff)\n......\n[  734.469364] page dumped because: VM_WARN_ON_FOLIO((_Generic((page + nr_pages - 1),\nconst struct page *: (const struct folio *)_compound_head(page + nr_pages - 1), struct page *:\n(struct folio *)_compound_head(page + nr_pages - 1))) != folio)\n[  734.469390] ------------[ cut here ]------------\n[  734.469393] WARNING: ./include/linux/rmap.h:351 at folio_add_file_rmap_ptes+0x3b8/0x468,\nCPU#90: stress-ng-mlock/9430\n[  734.469551]  folio_add_file_rmap_ptes+0x3b8/0x468 (P)\n[  734.469555]  set_pte_range+0xd8/0x2f8\n[  734.469566]  filemap_map_folio_range+0x190/0x400\n[  734.469579]  filemap_map_pages+0x348/0x638\n[  734.469583]  do_fault_around+0x140/0x198\n......\n[  734.469640]  el0t_64_sync+0x184/0x188\n\"\n\nThe code that triggers the warning is: \"VM_WARN_ON_FOLIO(page_folio(page +\nnr_pages - 1) != folio, folio)\", which indicates that set_pte_range()\ntried to map beyond the large folio\u2019s size.\n\nBy adding more debug information, I found that \u0027nr_pages\u0027 had overflowed\nin filemap_map_pages(), causing set_pte_range() to establish mappings for\na range exceeding the folio size, potentially corrupting fields of pages\nthat do not belong to this folio (e.g., page-\u003e_mapcount).\n\nAfter above analysis, I think the possible race is as follows:\n\nCPU 0                                                  CPU 1\nfilemap_map_pages()                                   ext4_setattr()\n   //get and lock folio with old inode-\u003ei_size\n   next_uptodate_folio()\n\n                                                          .......\n                                                          //shrink the inode-\u003ei_size\n                                                          i_size_write(inode, attr-\u003eia_size);\n\n   //calculate the end_pgoff with the new inode-\u003ei_size\n   file_end = DIV_ROUND_UP(i_size_read(mapping-\u003ehost), PAGE_SIZE) - 1;\n   end_pgoff = min(end_pgoff, file_end);\n\n   ......\n   //nr_pages can be overflowed, cause xas.xa_index \u003e end_pgoff\n   end = folio_next_index(folio) - 1;\n   nr_pages = min(end, end_pgoff) - xas.xa_index + 1;\n\n   ......\n   //map large folio\n   filemap_map_folio_range()\n                                                          ......\n                                                          //truncate folios\n                                                          truncate_pagecache(inode, inode-\u003ei_size);\n\nTo fix this issue, move the \u0027end_pgoff\u0027 calculation before\nnext_uptodate_folio(), so the retrieved folio stays consistent with the\nfile end to avoid \n---truncated---"
        }
      ],
      "metrics": [
        {
          "cvssV3_1": {
            "baseScore": 7.8,
            "baseSeverity": "HIGH",
            "vectorString": "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H",
            "version": "3.1"
          },
          "scenarios": [
            {
              "lang": "en",
              "value": "AV:L - The vulnerable code is reached through a local userspace page fault on an mmaped regular file, with the race driven by local truncate/ftruncate activity. I found no complete unauthenticated network path directly invoking this filemap fault-around path.\nAC:L - The bug is a race, but an attacker can control both sides by faulting an mmaped file while concurrently shrinking it, and the reproducer-style stress path can be repeated. Large folios/THP are common enough for the affected path and not a rare attacker-independent condition.\nPR:L - An unprivileged local user can create or open a writable regular file, mmap it, and truncate it; no capability or real root privilege is required. Normal file permissions are sufficient.\nUI:N - No separate victim action is required for the local attack path. The attacker triggers the mmap fault and truncate operations directly.\nS:U - The corruption occurs within the kernel memory-management/file-cache security authority. This is standard kernel memory corruption/possible privilege escalation, not a VM escape or cross-authority boundary by itself.\nC:H - The overflow can cause PTEs to be installed for pages beyond the intended folio, potentially exposing unrelated physical memory to userspace. As kernel memory corruption, it is also reasonably capable of enabling broader information disclosure.\nI:H - The overlarge range corrupts page metadata such as mapcounts for pages outside the folio and creates incorrect mappings. This is out-of-bounds kernel memory corruption and is high-integrity impact under the required scoring guidance.\nA:H - The fix commit documents reproducible Bad page state crashes and DEBUG_VM warnings from mapping past the folio. Kernel crash/oops availability impact is High."
            }
          ]
        }
      ],
      "providerMetadata": {
        "dateUpdated": "2026-08-05T12:24:07.515Z",
        "orgId": "416baaa9-dc9f-4396-8d5f-8c081fb06d67",
        "shortName": "Linux"
      },
      "references": [
        {
          "url": "https://git.kernel.org/stable/c/88591194df736a508dd5461ab2167a61e98caac1"
        },
        {
          "url": "https://git.kernel.org/stable/c/633ab680c405ac390e6bec5b74aaf46197c837b6"
        },
        {
          "url": "https://git.kernel.org/stable/c/576543bedd616254032d4ebe54a90076f9e31740"
        },
        {
          "url": "https://git.kernel.org/stable/c/9316a820b9aae07d44469d6485376dad824c5b3f"
        },
        {
          "url": "https://git.kernel.org/stable/c/f58df566524ebcdfa394329c64f47e3c9257516e"
        }
      ],
      "title": "mm: filemap: fix nr_pages calculation overflow in filemap_map_pages()",
      "x_generator": {
        "engine": "bippy-1.2.0"
      }
    }
  },
  "cveMetadata": {
    "assignerOrgId": "416baaa9-dc9f-4396-8d5f-8c081fb06d67",
    "assignerShortName": "Linux",
    "cveId": "CVE-2026-31648",
    "datePublished": "2026-04-24T14:45:01.728Z",
    "dateReserved": "2026-03-09T15:48:24.128Z",
    "dateUpdated": "2026-08-05T12:24:07.515Z",
    "state": "PUBLISHED"
  },
  "dataType": "CVE_RECORD",
  "dataVersion": "5.2"
}


Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Sightings

Author Source Type Date

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or seen somewhere by the user.
  • Confirmed: The vulnerability is confirmed from an analyst perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: This vulnerability was exploited and seen by the user reporting the sighting.
  • Patched: This vulnerability was successfully patched by the user reporting the sighting.
  • Not exploited: This vulnerability was not exploited or seen by the user reporting the sighting.
  • Not confirmed: The user expresses doubt about the veracity of the vulnerability.
  • Not patched: This vulnerability was not successfully patched by the user reporting the sighting.


Loading…

Loading…