---
id: CVE-2026-68082
title: |-
  In the Linux kernel, the following vulnerability has been resolved:

  libceph: fix two unsafe bare decodes in decode_lockers()

  decode_lockers() in cls_lock_client.c contains two bare decode operations
  that allow a malicious or compromise…
summary: |-
  In the Linux kernel, the following vulnerability has been resolved:

  libceph: fix two unsafe bare decodes in decode_lockers()

  decode_lockers() in cls_lock_client.c contains two bare decode operations
  that allow a malicious or compromise…
severity: none
published: '2026-08-08'
updated: '2026-08-08'
source: NVD
sourceUrl: 'https://nvd.nist.gov/vuln/detail/CVE-2026-68082'
references:
  - url: 'https://git.kernel.org/stable/c/a109a556115271ca7896dcda7b4b7e45e156c227'
    label: 416baaa9-dc9f-4396-8d5f-8c081fb06d67
  - url: 'https://git.kernel.org/stable/c/a54be593d0b749161b08a1e56189b2cb9114267a'
    label: 416baaa9-dc9f-4396-8d5f-8c081fb06d67
tags:
  - nvd
epss: 0.00399
epssPercentile: 0.33951
ingestedAt: '2026-08-09T04:32:45.275Z'
---

## Overview

In the Linux kernel, the following vulnerability has been resolved:

libceph: fix two unsafe bare decodes in decode_lockers()

decode_lockers() in cls_lock_client.c contains two bare decode operations
that allow a malicious or compromised OSD to trigger slab-out-of-bounds
reads:

1. ceph_decode_32(p) at the num_lockers field has no preceding bounds
   check. ceph_start_decoding() accepts struct_len=0 as valid -- the
   internal ceph_decode_need(p, end, 0, bad) always passes -- so when an
   OSD sends struct_len=0, ceph_start_decoding() returns success with
   p == end. The immediately following bare ceph_decode_32(p) then reads
   4 bytes past the validated buffer boundary. The garbage value is
   passed directly to kzalloc_objs() as the locker count.

   The sibling function decode_watchers() in osd_client.c already uses
   ceph_decode_32_safe() after its own ceph_start_decoding() call.
   decode_lockers() was the only site using the bare variant.

2. ceph_decode_8(p) after the decode_locker() loop has no preceding
   bounds check. If an OSD crafts num_lockers such that the loop
   advances p exactly to end, the subsequent bare ceph_decode_8(p) reads
   one byte past the validated buffer boundary. The result is passed
   directly into *type, which is used as a lock type discriminator by
   callers, giving an OSD-controlled one-byte OOB read with direct
   influence over the lock type field.

Fix both by replacing bare operations with their safe variants:
  ceph_decode_32(p) -> ceph_decode_32_safe(p, end, *num_lockers,
                                           err_inval)
  ceph_decode_8(p)  -> ceph_decode_8_safe(p, end, *type,
                                          err_free_lockers)

The goto targets differ intentionally:
  err_inval: is a new label returning -EINVAL directly. It is used for
  the pre-allocation failure path where *lockers is not yet allocated
  and must not be passed to ceph_free_lockers().

  err_free_lockers: is the existing label. It is used for the
  post-allocation failure path where *lockers is allocated and must
  be freed.

ret is set to -EINVAL before ceph_decode_8_safe() so that
err_free_lockers returns the correct error code on bounds violation.
Without this, err_free_lockers would return a stale ret value (0 from
the successful decode_locker() loop), silently swallowing the error.

-EINVAL is correct for both failure paths. The data received from the
OSD is structurally malformed. -ENOMEM would misrepresent the failure
class to callers and to stable@ backporters triaging error paths.

Attacker model: a malicious or compromised OSD in a multi-tenant Ceph
deployment can trigger this against any kernel client that issues the
lock.get_info class method (e.g. during RBD exclusive lock acquisition).

[ idryomov: trim changelog, formatting ]

## Remediation

Refer to the linked advisories for vendor-supplied fixes and affected version ranges.
