GCVE Workshop - 22 September 2026 (14:00-18:00), Luxembourg Before The Vulnopticon Conference - Registration

FKIE_CVE-2026-89767

Vulnerability from fkie_nvd - Published: 2026-09-11 20:20 - Updated: 2026-09-13 07:17
Summary
In the Linux kernel, the following vulnerability has been resolved: ovl: fix double end_creating() on the casefold-mismatch path ovl_create_real() releases the new dentry twice when the casefold consistency check fails. The S_IFDIR branch calls end_creating() and sets err, then falls through to the common out: label which calls end_creating() on the same dentry again: case S_IFDIR: newdentry = ovl_do_mkdir(ofs, dir, newdentry, attr->mode); err = PTR_ERR_OR_ZERO(newdentry); if (!err && ofs->casefold != ovl_dentry_casefolded(newdentry)) { pr_warn_ratelimited(...); end_creating(newdentry); /* first */ err = -EINVAL; } break; ... if (err) goto out; ... out: if (err) { end_creating(newdentry); /* second, same dentry */ return ERR_PTR(err); } end_creating() is end_dirop(), which does inode_unlock() on the parent and dput() on the dentry, so the parent directory's i_rwsem is unlocked twice and the dentry is put twice. The second unlock releases a lock that is not held, which is what wedges every later creation under that parent, and the second dput() drops a reference that was never taken. The branch was added by commit dfc7da402ccc ("ovl: Check for casefold consistency when creating new dentries") as a bare dput(), which already released the reference twice; commit fe497f0759e0 ("VFS: change vfs_mkdir() to unlock on failure.") converted both sites to end_creating(), adding the double unlock. This is reachable by an unprivileged user. The casefold consistency of the layers is validated at mount time in ovl_parse_layer(), and again on every lookup in ovl_lookup_single(), but ofs->workdir is the internal "work" subdirectory created inside the user-supplied workdir, and that subdirectory is not re-checked. Marking it casefolded after the mount therefore makes every ovl_create_temp() inherit the wrong state - and that path reaches ovl_create_real() through ovl_start_creating_temp(), which uses start_creating() with a generated name and so never runs the lookup-time check. unshare -Urm mount -t tmpfs -o casefold=utf8-12.1.0 tmpfs mnt mkdir -p mnt/lower/d mnt/upper mnt/work mnt/merged mount -t overlay ovl -o lowerdir=mnt/lower,\ upperdir=mnt/upper,workdir=mnt/work mnt/merged chattr +F mnt/work/work mkdir mnt/merged/d/sub # directory copy-up overlayfs: wrong inherited casefold (work/#5) and the next copy-up blocks forever on the parent's i_rwsem: mkdir D start_creating+0x65/0xb0 ovl_start_creating_temp+0xb0/0xe0 [overlay] ovl_create_temp+0xa3/0x1d0 [overlay] ovl_copy_up_one+0x1f1c/0x21c0 [overlay] ovl_copy_up_flags+0xf5/0x140 [overlay] ovl_create_object+0xb7/0x220 [overlay] ovl_mkdir+0x23/0x40 [overlay] Drop the end_creating() from the branch and let out: own the cleanup, which is what every other error path in this function already does.
Impacted products
Vendor Product Version

{
  "affected": [
    {
      "affectedData": [
        {
          "defaultStatus": "unaffected",
          "product": "Linux",
          "programFiles": [
            "fs/overlayfs/dir.c"
          ],
          "repo": "https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git",
          "vendor": "Linux",
          "versions": [
            {
              "lessThan": "2fa220bc0f84597cb6de665e5b5021c5901ddf00",
              "status": "affected",
              "version": "dfc7da402ccc92d6e4b01a4778a3f15f2496b9af",
              "versionType": "git"
            },
            {
              "lessThan": "b1aa8ab78a8e87aee9e306971465408e059f839a",
              "status": "affected",
              "version": "dfc7da402ccc92d6e4b01a4778a3f15f2496b9af",
              "versionType": "git"
            },
            {
              "lessThan": "077ab8985ee278c3d8618182d335b0f0cd919e16",
              "status": "affected",
              "version": "dfc7da402ccc92d6e4b01a4778a3f15f2496b9af",
              "versionType": "git"
            }
          ]
        },
        {
          "defaultStatus": "affected",
          "product": "Linux",
          "programFiles": [
            "fs/overlayfs/dir.c"
          ],
          "repo": "https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git",
          "vendor": "Linux",
          "versions": [
            {
              "status": "affected",
              "version": "6.18"
            },
            {
              "lessThan": "6.18",
              "status": "unaffected",
              "version": "0",
              "versionType": "semver"
            },
            {
              "lessThanOrEqual": "6.18.*",
              "status": "unaffected",
              "version": "6.18.51",
              "versionType": "semver"
            },
            {
              "lessThanOrEqual": "7.2.*",
              "status": "unaffected",
              "version": "7.2.4",
              "versionType": "semver"
            },
            {
              "lessThanOrEqual": "*",
              "status": "unaffected",
              "version": "7.3-rc1",
              "versionType": "original_commit_for_fix"
            }
          ]
        }
      ],
      "source": "416baaa9-dc9f-4396-8d5f-8c081fb06d67"
    }
  ],
  "cveTags": [],
  "descriptions": [
    {
      "lang": "en",
      "value": "In the Linux kernel, the following vulnerability has been resolved:\n\novl: fix double end_creating() on the casefold-mismatch path\n\novl_create_real() releases the new dentry twice when the casefold\nconsistency check fails.  The S_IFDIR branch calls end_creating() and\nsets err, then falls through to the common out: label which calls\nend_creating() on the same dentry again:\n\n\tcase S_IFDIR:\n\t\tnewdentry = ovl_do_mkdir(ofs, dir, newdentry, attr-\u003emode);\n\t\terr = PTR_ERR_OR_ZERO(newdentry);\n\t\tif (!err \u0026\u0026 ofs-\u003ecasefold != ovl_dentry_casefolded(newdentry)) {\n\t\t\tpr_warn_ratelimited(...);\n\t\t\tend_creating(newdentry);\t/* first */\n\t\t\terr = -EINVAL;\n\t\t}\n\t\tbreak;\n\t...\n\tif (err)\n\t\tgoto out;\n\t...\n out:\n\tif (err) {\n\t\tend_creating(newdentry);\t/* second, same dentry */\n\t\treturn ERR_PTR(err);\n\t}\n\nend_creating() is end_dirop(), which does inode_unlock() on the parent\nand dput() on the dentry, so the parent directory\u0027s i_rwsem is unlocked\ntwice and the dentry is put twice.  The second unlock releases a lock\nthat is not held, which is what wedges every later creation under that\nparent, and the second dput() drops a reference that was never taken.\n\nThe branch was added by commit dfc7da402ccc (\"ovl: Check for casefold\nconsistency when creating new dentries\") as a bare dput(), which already\nreleased the reference twice; commit fe497f0759e0 (\"VFS: change\nvfs_mkdir() to unlock on failure.\") converted both sites to\nend_creating(), adding the double unlock.\n\nThis is reachable by an unprivileged user.  The casefold consistency of\nthe layers is validated at mount time in ovl_parse_layer(), and again on\nevery lookup in ovl_lookup_single(), but ofs-\u003eworkdir is the internal\n\"work\" subdirectory created inside the user-supplied workdir, and that\nsubdirectory is not re-checked.  Marking it casefolded after the mount\ntherefore makes every ovl_create_temp() inherit the wrong state - and\nthat path reaches ovl_create_real() through ovl_start_creating_temp(),\nwhich uses start_creating() with a generated name and so never runs the\nlookup-time check.\n\n  unshare -Urm\n  mount -t tmpfs -o casefold=utf8-12.1.0 tmpfs mnt\n  mkdir -p mnt/lower/d mnt/upper mnt/work mnt/merged\n  mount -t overlay ovl -o lowerdir=mnt/lower,\\\n        upperdir=mnt/upper,workdir=mnt/work mnt/merged\n  chattr +F mnt/work/work\n  mkdir mnt/merged/d/sub\t\t# directory copy-up\n\n  overlayfs: wrong inherited casefold (work/#5)\n\nand the next copy-up blocks forever on the parent\u0027s i_rwsem:\n\n  mkdir           D  start_creating+0x65/0xb0\n                     ovl_start_creating_temp+0xb0/0xe0 [overlay]\n                     ovl_create_temp+0xa3/0x1d0 [overlay]\n                     ovl_copy_up_one+0x1f1c/0x21c0 [overlay]\n                     ovl_copy_up_flags+0xf5/0x140 [overlay]\n                     ovl_create_object+0xb7/0x220 [overlay]\n                     ovl_mkdir+0x23/0x40 [overlay]\n\nDrop the end_creating() from the branch and let out: own the cleanup,\nwhich is what every other error path in this function already does."
    }
  ],
  "id": "CVE-2026-89767",
  "lastModified": "2026-09-13T07:17:41.050",
  "metrics": {
    "cvssMetricV31": [
      {
        "cvssData": {
          "attackComplexity": "LOW",
          "attackVector": "LOCAL",
          "availabilityImpact": "HIGH",
          "baseScore": 7.8,
          "baseSeverity": "HIGH",
          "confidentialityImpact": "HIGH",
          "integrityImpact": "HIGH",
          "privilegesRequired": "LOW",
          "scope": "UNCHANGED",
          "userInteraction": "NONE",
          "vectorString": "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H",
          "version": "3.1"
        },
        "exploitabilityScore": 1.8,
        "impactScore": 5.9,
        "source": "416baaa9-dc9f-4396-8d5f-8c081fb06d67",
        "type": "Secondary"
      }
    ]
  },
  "published": "2026-09-11T20:20:07.960",
  "references": [
    {
      "source": "416baaa9-dc9f-4396-8d5f-8c081fb06d67",
      "url": "https://git.kernel.org/stable/c/077ab8985ee278c3d8618182d335b0f0cd919e16"
    },
    {
      "source": "416baaa9-dc9f-4396-8d5f-8c081fb06d67",
      "url": "https://git.kernel.org/stable/c/2fa220bc0f84597cb6de665e5b5021c5901ddf00"
    },
    {
      "source": "416baaa9-dc9f-4396-8d5f-8c081fb06d67",
      "url": "https://git.kernel.org/stable/c/b1aa8ab78a8e87aee9e306971465408e059f839a"
    }
  ],
  "sourceIdentifier": "416baaa9-dc9f-4396-8d5f-8c081fb06d67",
  "vulnStatus": "Received"
}



Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.

Sightings

Author Source Type Date Other

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or observed by the user.
  • Confirmed: The vulnerability has been validated from an analyst's perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
  • Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
  • Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
  • Not confirmed: The user expressed doubt about the validity of the vulnerability.
  • Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.

Loading…

Detection rules are retrieved from Rulezet.

Loading…

Loading…

Related by attack behaviour

Vulnerabilities whose description is nearest to this one in the vector space of the CIRCL/vulnerability-attack-technique-biencoder model. This is a similarity search over the bi-encoder space (plain cosine), not a classification, and it has no measured accuracy.


Loading…