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

CVE-2026-89708 (GCVE-0-2026-89708)

Vulnerability from cvelistv5 – Published: 2026-09-11 19:46 – Updated: 2026-09-13 06:33
VLAI
Title
nfsd: RCU-protect cl_cb_session to fix use-after-free on session teardown
Summary
In the Linux kernel, the following vulnerability has been resolved: nfsd: RCU-protect cl_cb_session to fix use-after-free on session teardown After a DESTROY_SESSION the per-session teardown path can free a session while rpciod still holds an inflight callback rpc_task that dereferences clp->cl_cb_session. nfsd4_probe_callback_sync() flushes cl_callback_wq, but once nfsd4_run_cb_work() has called rpc_call_async() the rpc_task lives on rpciod; flushing the workqueue does not wait for it. rpc_shutdown_client() does drain rpciod tasks, but uses a 1-second wait_event_timeout — tasks stuck in rpc_delay() (e.g. 2-second NFS4ERR_DELAY retries) can outlive the drain. destroy path rpciod ------------ ------ unhash_session(ses) nfsd4_probe_callback_sync(clp) flush_workqueue(cl_callback_wq) /* returns; rpc_task still live */ nfsd4_put_session_locked(ses) free_session(ses) -> kfree(ses) nfsd4_cb_sequence_done() reads cb_clp->cl_cb_session /* freed slab */ A second window exists in nfsd4_process_cb_update(). When __nfsd4_find_backchannel() returns NULL because unhash_session() has already removed the destroyed session from cl_sessions, setup_callback_client() takes the v4.1 early return so clp->cl_cb_session = ses never fires and the field retains a pointer to the about-to-be-freed session. Fix both by converting cl_cb_session to an RCU-protected pointer: - Move the cl_cb_session = ses assignment in setup_callback_client() to after rpc_create() succeeds, so it is only published when a working backchannel exists. Clear cl_cb_session on the error return in nfsd4_process_cb_update(). Both stores use rcu_assign_pointer(). - Annotate cl_cb_session with __rcu. All rpciod-side readers use rcu_read_lock()/rcu_dereference() and check for NULL, bailing to the appropriate error or requeue path: encode_cb_sequence4args(), decode_cb_sequence4resok(), nfsd41_cb_get_slot(), nfsd41_cb_release_slot(), nfsd4_cb_prepare(), and nfsd4_cb_sequence_done(). - Switch __free_session() from kfree() to kfree_rcu() so the session slab is not reclaimed until after an RCU grace period, guaranteeing that rpciod readers inside rcu_read_lock() never dereference freed memory. - Pass the session pointer to the nfsd_cb_seq_status and nfsd_cb_free_slot tracepoints instead of having them re-read cl_cb_session. - nfsd4_cb_prepare() calls rpc_exit() when the session is NULL, routing through the done/release path to requeue the callback.
Impacted products
Vendor Product Version CPE status
Linux Linux Affected: dcbeaa68dbbdacbbb330a86c7fc95a28473fc209 , < f164eb52b6f3cbf40f07fe379f9f421f88e02f76 (git)
Affected: dcbeaa68dbbdacbbb330a86c7fc95a28473fc209 , < 13bdd486c3aad4fc19e6d8b9c3556a4b4c190b25 (git)
Affected: dcbeaa68dbbdacbbb330a86c7fc95a28473fc209 , < 01c5d5f58a5db9b0ee5afba2e49d3157788687b2 (git)
guessed Create a notification for this product.
Linux Linux Affected: 2.6.38
Unaffected: 0 , < 2.6.38 (semver)
Unaffected: 6.18.50 , ≤ 6.18.* (semver)
Unaffected: 7.2.4 , ≤ 7.2.* (semver)
Unaffected: 7.3-rc1 , ≤ * (original_commit_for_fix)
guessed Create a notification for this product.
Show details on NVD website

{
  "containers": {
    "cna": {
      "affected": [
        {
          "defaultStatus": "unaffected",
          "product": "Linux",
          "programFiles": [
            "fs/nfsd/nfs4callback.c",
            "fs/nfsd/nfs4state.c",
            "fs/nfsd/state.h",
            "fs/nfsd/trace.h"
          ],
          "repo": "https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git",
          "vendor": "Linux",
          "versions": [
            {
              "lessThan": "f164eb52b6f3cbf40f07fe379f9f421f88e02f76",
              "status": "affected",
              "version": "dcbeaa68dbbdacbbb330a86c7fc95a28473fc209",
              "versionType": "git"
            },
            {
              "lessThan": "13bdd486c3aad4fc19e6d8b9c3556a4b4c190b25",
              "status": "affected",
              "version": "dcbeaa68dbbdacbbb330a86c7fc95a28473fc209",
              "versionType": "git"
            },
            {
              "lessThan": "01c5d5f58a5db9b0ee5afba2e49d3157788687b2",
              "status": "affected",
              "version": "dcbeaa68dbbdacbbb330a86c7fc95a28473fc209",
              "versionType": "git"
            }
          ]
        },
        {
          "defaultStatus": "affected",
          "product": "Linux",
          "programFiles": [
            "fs/nfsd/nfs4callback.c",
            "fs/nfsd/nfs4state.c",
            "fs/nfsd/state.h",
            "fs/nfsd/trace.h"
          ],
          "repo": "https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git",
          "vendor": "Linux",
          "versions": [
            {
              "status": "affected",
              "version": "2.6.38"
            },
            {
              "lessThan": "2.6.38",
              "status": "unaffected",
              "version": "0",
              "versionType": "semver"
            },
            {
              "lessThanOrEqual": "6.18.*",
              "status": "unaffected",
              "version": "6.18.50",
              "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"
            }
          ]
        }
      ],
      "cpeApplicability": [
        {
          "nodes": [
            {
              "cpeMatch": [
                {
                  "criteria": "cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:*",
                  "versionEndExcluding": "6.18.50",
                  "versionStartIncluding": "2.6.38",
                  "vulnerable": true
                },
                {
                  "criteria": "cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:*",
                  "versionEndExcluding": "7.2.4",
                  "versionStartIncluding": "2.6.38",
                  "vulnerable": true
                },
                {
                  "criteria": "cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:*",
                  "versionEndExcluding": "7.3-rc1",
                  "versionStartIncluding": "2.6.38",
                  "vulnerable": true
                }
              ],
              "negate": false,
              "operator": "OR"
            }
          ]
        }
      ],
      "descriptions": [
        {
          "lang": "en",
          "value": "In the Linux kernel, the following vulnerability has been resolved:\n\nnfsd: RCU-protect cl_cb_session to fix use-after-free on session teardown\n\nAfter a DESTROY_SESSION the per-session teardown path can free a\nsession while rpciod still holds an inflight callback rpc_task that\ndereferences clp-\u003ecl_cb_session.  nfsd4_probe_callback_sync() flushes\ncl_callback_wq, but once nfsd4_run_cb_work() has called\nrpc_call_async() the rpc_task lives on rpciod; flushing the workqueue\ndoes not wait for it.  rpc_shutdown_client() does drain rpciod tasks,\nbut uses a 1-second wait_event_timeout \u2014 tasks stuck in rpc_delay()\n(e.g. 2-second NFS4ERR_DELAY retries) can outlive the drain.\n\n    destroy path                       rpciod\n    ------------                       ------\n    unhash_session(ses)\n    nfsd4_probe_callback_sync(clp)\n      flush_workqueue(cl_callback_wq)\n      /* returns; rpc_task still live */\n    nfsd4_put_session_locked(ses)\n    free_session(ses) -\u003e kfree(ses)\n                                       nfsd4_cb_sequence_done()\n                                         reads cb_clp-\u003ecl_cb_session\n                                         /* freed slab */\n\nA second window exists in nfsd4_process_cb_update().  When\n__nfsd4_find_backchannel() returns NULL because unhash_session() has\nalready removed the destroyed session from cl_sessions,\nsetup_callback_client() takes the v4.1 early return so\nclp-\u003ecl_cb_session = ses never fires and the field retains a pointer\nto the about-to-be-freed session.\n\nFix both by converting cl_cb_session to an RCU-protected pointer:\n\n  - Move the cl_cb_session = ses assignment in setup_callback_client()\n    to after rpc_create() succeeds, so it is only published when a\n    working backchannel exists.  Clear cl_cb_session on the error\n    return in nfsd4_process_cb_update().  Both stores use\n    rcu_assign_pointer().\n\n  - Annotate cl_cb_session with __rcu.  All rpciod-side readers use\n    rcu_read_lock()/rcu_dereference() and check for NULL, bailing to\n    the appropriate error or requeue path:\n    encode_cb_sequence4args(), decode_cb_sequence4resok(),\n    nfsd41_cb_get_slot(), nfsd41_cb_release_slot(),\n    nfsd4_cb_prepare(), and nfsd4_cb_sequence_done().\n\n  - Switch __free_session() from kfree() to kfree_rcu() so the\n    session slab is not reclaimed until after an RCU grace period,\n    guaranteeing that rpciod readers inside rcu_read_lock() never\n    dereference freed memory.\n\n  - Pass the session pointer to the nfsd_cb_seq_status and\n    nfsd_cb_free_slot tracepoints instead of having them re-read\n    cl_cb_session.\n\n  - nfsd4_cb_prepare() calls rpc_exit() when the session is NULL,\n    routing through the done/release path to requeue the callback."
        }
      ],
      "metrics": [
        {
          "cvssV3_1": {
            "baseScore": 9.8,
            "baseSeverity": "CRITICAL",
            "vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
            "version": "3.1"
          },
          "scenarios": [
            {
              "lang": "en",
              "value": "AV:N - The UAF is in nfsd NFSv4.1 session teardown (nfsd4_destroy_session) and the rpciod callback path (nfsd4_cb_sequence_done). DESTROY_SESSION and the inflight CB_COMPOUND run over TCP/2049, so a remote NFS client reaches the bug with no local access.\nAC:L - The attacker controls both sides: they keep a callback rpc_task on rpciod (e.g. NFS4ERR_DELAY so rpc_delay is 2s) and send DESTROY_SESSION. flush_workqueue does not wait for rpciod, and rpc_shutdown_client\u0027s 1s drain can expire first, so the window is attacker-driven and retryable.\nPR:N - Typical nfsd exports use AUTH_SYS/AUTH_NULL, so RPC identities are self-asserted. EXCHANGE_ID, CREATE_SESSION with a backchannel, OPEN, and DESTROY_SESSION succeed for any host allowed by the export; nfsd4_mach_creds_match is a no-op under default SP4_NONE. No local account is required.\nUI:N - The attacking NFS client issues CREATE_SESSION, OPEN/CB_RECALL traffic, NFS4ERR_DELAY replies, and DESTROY_SESSION itself. No victim on the server must mount a filesystem, open a file, or take any other action.\nS:U - The use-after-free corrupts a kmalloc\u0027d nfsd4_session in the host nfsd kernel. Impact stays in that kernel security authority and does not cross a VM, IOMMU, or sandbox boundary.\nC:H - After free_session kfree\u0027s the session, rpciod still reads se_sessionid and se_cb_seq_nr in encode_cb_sequence4args and decode_cb_sequence4resok. Reclaim of that slab lets the attacker disclose kernel memory via the callback they receive. Per kernel guidance a UAF is C:H.\nI:H - The same dangling cl_cb_session is written by update_cb_slot_table (attacker-controlled target_highest_slotid), grab_slot/nfsd41_cb_release_slot (se_cb_slot_avail and se_lock), and nfsd4_cb_sequence_done (se_cb_seq_nr++). That is a UAF write primitive for heap corruption and control-flow hijack.\nA:H - Use-after-free of struct nfsd4_session from an rpciod callback oopses or panics the NFS server even without full exploitation, and the attacker can repeat DESTROY_SESSION against inflight callbacks to deny service."
            }
          ]
        }
      ],
      "providerMetadata": {
        "dateUpdated": "2026-09-13T06:33:31.107Z",
        "orgId": "416baaa9-dc9f-4396-8d5f-8c081fb06d67",
        "shortName": "Linux"
      },
      "references": [
        {
          "url": "https://git.kernel.org/stable/c/f164eb52b6f3cbf40f07fe379f9f421f88e02f76"
        },
        {
          "url": "https://git.kernel.org/stable/c/13bdd486c3aad4fc19e6d8b9c3556a4b4c190b25"
        },
        {
          "url": "https://git.kernel.org/stable/c/01c5d5f58a5db9b0ee5afba2e49d3157788687b2"
        }
      ],
      "title": "nfsd: RCU-protect cl_cb_session to fix use-after-free on session teardown",
      "x_generator": {
        "engine": "bippy-1.2.0"
      }
    }
  },
  "cveMetadata": {
    "assignerOrgId": "416baaa9-dc9f-4396-8d5f-8c081fb06d67",
    "assignerShortName": "Linux",
    "cveId": "CVE-2026-89708",
    "datePublished": "2026-09-11T19:46:24.061Z",
    "dateReserved": "2026-09-11T19:38:34.751Z",
    "dateUpdated": "2026-09-13T06:33:31.107Z",
    "state": "PUBLISHED"
  },
  "dataType": "CVE_RECORD",
  "dataVersion": "5.2",
  "vulnerability-lookup:meta": {
    "epss": {
      "cve": "CVE-2026-89708",
      "date": "2026-09-15",
      "epss": "0.00435",
      "percentile": "0.36905"
    },
    "microsoft_vex": {
      "current_release_date": "2026-09-14T14:49:36.000Z",
      "cve": "CVE-2026-89708",
      "id": "msrc_CVE-2026-89708",
      "initial_release_date": "2026-09-13T01:03:39.000Z",
      "product_status:known_affected": "1",
      "source": "Microsoft CSAF VEX",
      "status": "final",
      "title": "nfsd: RCU-protect cl_cb_session to fix use-after-free on session teardown",
      "url": "https://msrc.microsoft.com/csaf/vex/2026/msrc_cve-2026-89708.json",
      "version": "2"
    },
    "nvd": "{\"cve\":{\"id\":\"CVE-2026-89708\",\"sourceIdentifier\":\"416baaa9-dc9f-4396-8d5f-8c081fb06d67\",\"published\":\"2026-09-11T20:19:58.140\",\"lastModified\":\"2026-09-13T07:17:36.477\",\"vulnStatus\":\"Received\",\"cveTags\":[],\"descriptions\":[{\"lang\":\"en\",\"value\":\"In the Linux kernel, the following vulnerability has been resolved:\\n\\nnfsd: RCU-protect cl_cb_session to fix use-after-free on session teardown\\n\\nAfter a DESTROY_SESSION the per-session teardown path can free a\\nsession while rpciod still holds an inflight callback rpc_task that\\ndereferences clp-\u003ecl_cb_session.  nfsd4_probe_callback_sync() flushes\\ncl_callback_wq, but once nfsd4_run_cb_work() has called\\nrpc_call_async() the rpc_task lives on rpciod; flushing the workqueue\\ndoes not wait for it.  rpc_shutdown_client() does drain rpciod tasks,\\nbut uses a 1-second wait_event_timeout \u2014 tasks stuck in rpc_delay()\\n(e.g. 2-second NFS4ERR_DELAY retries) can outlive the drain.\\n\\n    destroy path                       rpciod\\n    ------------                       ------\\n    unhash_session(ses)\\n    nfsd4_probe_callback_sync(clp)\\n      flush_workqueue(cl_callback_wq)\\n      /* returns; rpc_task still live */\\n    nfsd4_put_session_locked(ses)\\n    free_session(ses) -\u003e kfree(ses)\\n                                       nfsd4_cb_sequence_done()\\n                                         reads cb_clp-\u003ecl_cb_session\\n                                         /* freed slab */\\n\\nA second window exists in nfsd4_process_cb_update().  When\\n__nfsd4_find_backchannel() returns NULL because unhash_session() has\\nalready removed the destroyed session from cl_sessions,\\nsetup_callback_client() takes the v4.1 early return so\\nclp-\u003ecl_cb_session = ses never fires and the field retains a pointer\\nto the about-to-be-freed session.\\n\\nFix both by converting cl_cb_session to an RCU-protected pointer:\\n\\n  - Move the cl_cb_session = ses assignment in setup_callback_client()\\n    to after rpc_create() succeeds, so it is only published when a\\n    working backchannel exists.  Clear cl_cb_session on the error\\n    return in nfsd4_process_cb_update().  Both stores use\\n    rcu_assign_pointer().\\n\\n  - Annotate cl_cb_session with __rcu.  All rpciod-side readers use\\n    rcu_read_lock()/rcu_dereference() and check for NULL, bailing to\\n    the appropriate error or requeue path:\\n    encode_cb_sequence4args(), decode_cb_sequence4resok(),\\n    nfsd41_cb_get_slot(), nfsd41_cb_release_slot(),\\n    nfsd4_cb_prepare(), and nfsd4_cb_sequence_done().\\n\\n  - Switch __free_session() from kfree() to kfree_rcu() so the\\n    session slab is not reclaimed until after an RCU grace period,\\n    guaranteeing that rpciod readers inside rcu_read_lock() never\\n    dereference freed memory.\\n\\n  - Pass the session pointer to the nfsd_cb_seq_status and\\n    nfsd_cb_free_slot tracepoints instead of having them re-read\\n    cl_cb_session.\\n\\n  - nfsd4_cb_prepare() calls rpc_exit() when the session is NULL,\\n    routing through the done/release path to requeue the callback.\"}],\"affected\":[{\"source\":\"416baaa9-dc9f-4396-8d5f-8c081fb06d67\",\"affectedData\":[{\"vendor\":\"Linux\",\"product\":\"Linux\",\"defaultStatus\":\"unaffected\",\"programFiles\":[\"fs/nfsd/nfs4callback.c\",\"fs/nfsd/nfs4state.c\",\"fs/nfsd/state.h\",\"fs/nfsd/trace.h\"],\"repo\":\"https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git\",\"versions\":[{\"version\":\"dcbeaa68dbbdacbbb330a86c7fc95a28473fc209\",\"lessThan\":\"f164eb52b6f3cbf40f07fe379f9f421f88e02f76\",\"versionType\":\"git\",\"status\":\"affected\"},{\"version\":\"dcbeaa68dbbdacbbb330a86c7fc95a28473fc209\",\"lessThan\":\"13bdd486c3aad4fc19e6d8b9c3556a4b4c190b25\",\"versionType\":\"git\",\"status\":\"affected\"},{\"version\":\"dcbeaa68dbbdacbbb330a86c7fc95a28473fc209\",\"lessThan\":\"01c5d5f58a5db9b0ee5afba2e49d3157788687b2\",\"versionType\":\"git\",\"status\":\"affected\"}]},{\"vendor\":\"Linux\",\"product\":\"Linux\",\"defaultStatus\":\"affected\",\"programFiles\":[\"fs/nfsd/nfs4callback.c\",\"fs/nfsd/nfs4state.c\",\"fs/nfsd/state.h\",\"fs/nfsd/trace.h\"],\"repo\":\"https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git\",\"versions\":[{\"version\":\"2.6.38\",\"status\":\"affected\"},{\"version\":\"0\",\"lessThan\":\"2.6.38\",\"versionType\":\"semver\",\"status\":\"unaffected\"},{\"version\":\"6.18.50\",\"lessThanOrEqual\":\"6.18.*\",\"versionType\":\"semver\",\"status\":\"unaffected\"},{\"version\":\"7.2.4\",\"lessThanOrEqual\":\"7.2.*\",\"versionType\":\"semver\",\"status\":\"unaffected\"},{\"version\":\"7.3-rc1\",\"lessThanOrEqual\":\"*\",\"versionType\":\"original_commit_for_fix\",\"status\":\"unaffected\"}]}]}],\"metrics\":{\"cvssMetricV31\":[{\"source\":\"416baaa9-dc9f-4396-8d5f-8c081fb06d67\",\"type\":\"Secondary\",\"cvssData\":{\"version\":\"3.1\",\"vectorString\":\"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H\",\"baseScore\":9.8,\"baseSeverity\":\"CRITICAL\",\"attackVector\":\"NETWORK\",\"attackComplexity\":\"LOW\",\"privilegesRequired\":\"NONE\",\"userInteraction\":\"NONE\",\"scope\":\"UNCHANGED\",\"confidentialityImpact\":\"HIGH\",\"integrityImpact\":\"HIGH\",\"availabilityImpact\":\"HIGH\"},\"exploitabilityScore\":3.9,\"impactScore\":5.9}]},\"references\":[{\"url\":\"https://git.kernel.org/stable/c/01c5d5f58a5db9b0ee5afba2e49d3157788687b2\",\"source\":\"416baaa9-dc9f-4396-8d5f-8c081fb06d67\"},{\"url\":\"https://git.kernel.org/stable/c/13bdd486c3aad4fc19e6d8b9c3556a4b4c190b25\",\"source\":\"416baaa9-dc9f-4396-8d5f-8c081fb06d67\"},{\"url\":\"https://git.kernel.org/stable/c/f164eb52b6f3cbf40f07fe379f9f421f88e02f76\",\"source\":\"416baaa9-dc9f-4396-8d5f-8c081fb06d67\"}]}}",
    "redhat_vex": {
      "aggregate_severity": "Important",
      "current_release_date": "2026-09-15T13:22:55+00:00",
      "cve": "CVE-2026-89708",
      "id": "CVE-2026-89708",
      "initial_release_date": "2026-09-11T00:00:00+00:00",
      "product_status:known_affected": "262",
      "product_status:under_investigation": "14",
      "source": "Red Hat CSAF VEX",
      "status": "final",
      "title": "kernel: nfsd: RCU-protect cl_cb_session to fix use-after-free on session teardown",
      "url": "https://security.access.redhat.com/data/csaf/v2/vex/2026/cve-2026-89708.json",
      "version": "3"
    },
    "suse_vex": {
      "aggregate_severity": "critical",
      "current_release_date": "2026-09-13T16:56:49Z",
      "cve": "CVE-2026-89708",
      "id": "CVE-2026-89708",
      "initial_release_date": "2026-09-12T16:23:15Z",
      "source": "SUSE CSAF VEX",
      "status": "interim",
      "title": "SUSE CVE CVE-2026-89708",
      "url": "https://ftp.suse.com/pub/projects/security/csaf-vex/cve-2026-89708.json",
      "version": "3"
    }
  }
}



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…

Loading…