From nobody Mon Jun 8 15:36:59 2026 Received: from mail-pl1-f178.google.com (mail-pl1-f178.google.com [209.85.214.178]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D00A93EE1E5 for ; Thu, 28 May 2026 13:01:52 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.178 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779973314; cv=none; b=uvI03x5jxJfd+Nu6+ZhKR6stMtTlqoI75cbqHigm73OPNPNjCzOSNseQgi6g8Qxtz9kTmyHgQwPBbnySzqfhjVzXTo0EFT0hI6QsdGJPa6rORjrdazDdCFZDNfeHxBhuhAV3EjKlmoFeh56G0SKVRAhJ5H4v+E6a9fiqVV0DQE0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779973314; c=relaxed/simple; bh=lcDSJ87bV6vJQfy6PinO61G0LDBvkB6EAIMbHfc6P9E=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=K5GRmo46S1anC9208vjjM28ihJtDCZGHkzMqfpfXMUchAw/9/RHPK6HuR6y1eWJuiAVnQObz0sJK2RWrdy3xNfMlXhSDUaZltZ+0iRwoP6xKs4I0Bb24dZNahXpoYLRc6ng2uIEUDKQt6gD14lNVTe+V5bHu9iQFyMn585zKhgI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=CRkN5RdC; arc=none smtp.client-ip=209.85.214.178 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="CRkN5RdC" Received: by mail-pl1-f178.google.com with SMTP id d9443c01a7336-2b9f8c2c950so9269445ad.2 for ; Thu, 28 May 2026 06:01:52 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1779973312; x=1780578112; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=r4XT7mBcrAQilbyNGQo5M9t2//fft6f/hb3lp/Dz9ek=; b=CRkN5RdCNdBHumB54hS4MbGcndzBsIWtQ/lhFuRvTnylUU1Qzb26uxBYqaNLQdrYxK tcZA+MrB999zXJrkk7PfCO9RMu9s0uRMqDlyThW11GDO4wB/3KE3w40C7r5I7JJqcEiq XJUFFNDkgczInZ4b5F4BmGXdcooKw2cScDkust8jS+2MEZcE9wMRfErUvHst6eVUOSJ4 KCTneZ2L+7RTx4LvW9rWJw9ZxcDTuZ9cw6G85l4vFGj9nF+vqfMY2nF8B9asVErmPUG9 AN1jpix/ESiPmVrEKNA/L3oRzGnJ9S9182ka7u7faq3MteArhbKX3s2JwvCHDo92jSGm 18zw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1779973312; x=1780578112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=r4XT7mBcrAQilbyNGQo5M9t2//fft6f/hb3lp/Dz9ek=; b=WRBwhGLOa1Y8gn7EcnQg/5vHH0nHXrNVP/U3L3yHGbW84d2VfVVG/xS58ZIlYMz8uw dHsKfJlRGHlKjtpKnVwlegkd6mP4r9Yj7BgjNBzN75rXGqBy4Wf8ARp5kg9NLv6y4UKN Zqi1/kZPn7gJeod2U63JMxqyF7jRJmIiXq/C6DU6FWRn4pzIaaO62kdLUjiSKCLYGCk0 4U4MGhU8arqY6osaS+CmdGDJ7aFXzICw9TCLL6jJOt3BumiI6FpjWHQGHz4TiVqTXNdH oIysoBoogjBs1TvbY14qiDNPzGHNas64bVpNgy0U5dre8JF+R/h3+jGQjnGqXj+tEfp1 0DNg== X-Forwarded-Encrypted: i=1; AFNElJ8cYQPHrhmCYFBmCzeoDhjz0cFUe4L2rAx1xMh6EuqqoIx/Y75DcipwWajJCv87sOYaw3VY9Tg9DjoPu7A=@vger.kernel.org X-Gm-Message-State: AOJu0YylcMSVJyD68QkFVvIrw0BWQEZXMm0j0SDyu6fnIa87GmriKY0g vggw4M0rIY1OSmyDuE8JK0qM0WNtZ317rASzRP6aKQf1/fIJtTGLu6Cjh2U7iiBm X-Gm-Gg: Acq92OHGyY+dXQrVNUm/2o8IlWfOVbjYcdSyZcSvHbcVRJo3InD94C+Q3bySYYtZ/zg vMatg6o4GBz9yYcTw2bTJl73ZoB0GQK/SNNHuitHNfHMCDz0KzDF2crs5KCFmwloSrsceE5YfCC cbXkRku/P9v2XEF06rJtqMM5f1PwHfg3vjy2Ib1iT7lu7a7tKq0H0tmYETtvSBSQVjVGAJB2AX1 mQwffNvOoDpenCs39MFh5QK4blLtK//4HNfWRBbyBHKbAps8TeeDjO/8p5+QDmGmF5TIYKsYoz3 4O2Y251tLHhxzXfBVq++oWmbyPA4REhByKNNGIqHGbwVg1uO+sx/uEp5LdJp+X6HIfEtD4Ui5gk ChvNnbL6Dp93CSL47h3+T0TMyupFped40I6MK/9LUnYb1qeQcINHEzmM2el/JNc52VT6nU4NZRI JgV0DU83FCZXe3RF2cbALchuZqwvzLN+qp/hr8nOE= X-Received: by 2002:a17:903:2392:b0:2be:8d29:d5a7 with SMTP id d9443c01a7336-2bf02eff093mr19004395ad.2.1779973311622; Thu, 28 May 2026 06:01:51 -0700 (PDT) Received: from kali ([2402:e280:3d7c:a2:536a:b505:93f5:9d5d]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2bf0534e4c2sm23772905ad.5.2026.05.28.06.01.48 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 28 May 2026 06:01:50 -0700 (PDT) From: Pavitra Jha To: idryomov@gmail.com, Slava.Dubeyko@ibm.com Cc: ceph-devel@vger.kernel.org, linux-kernel@vger.kernel.org, stable@vger.kernel.org, Pavitra Jha Subject: [PATCH v2] ceph: fix multiple unsafe decodes in decode_locker() Date: Thu, 28 May 2026 09:01:13 -0400 Message-ID: <20260528130114.830041-1-jhapavitra98@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" decode_locker() in cls_lock_client.c contains three unsafe decode operations that allow a malicious or compromised OSD to trigger slab-out-of-bounds reads: 1. ceph_decode_copy() at the locker_id_t name field has no preceding bounds check. With p =3D=3D end after ceph_start_decoding() accepts struct_len=3D0, this reads sizeof(ceph_entity_name) =3D 9 bytes past the validated buffer boundary. 2. *p +=3D sizeof(struct ceph_timespec) after the locker_info_t header is an unchecked pointer advance. A malicious OSD can position p past end, causing all subsequent _safe checks to pass against a bogus boundary. 3. len =3D ceph_decode_32(p) has no preceding bounds check, and the immediately following *p +=3D len is uncapped. A malicious OSD can send len=3D0xffffffff, advancing p gigabytes past end and escaping the decode window entirely. Fix all three by replacing bare operations with their safe variants: ceph_decode_copy -> ceph_decode_copy_safe *p +=3D sizeof(...) -> ceph_decode_skip_n ceph_decode_32(p) -> ceph_decode_32_safe *p +=3D len -> ceph_decode_skip_n A new out_bad: label is added to return -EINVAL on any bounds violation. -EINVAL is appropriate here: the data received from the OSD is structurally malformed, which is an invalid argument to the decode contract regardless of whether the caller or the wire is at fault. KASAN report (kernel 7.0.0-rc7, QEMU/x86_64, KASLR disabled): [ 26.183969] ceph_oob4_poc: buf=3Dffff888009e31000 end=3Dffff888009e31f= a0 [ 26.186087] ceph_oob4_poc: struct_v=3D1 struct_len=3D0 p=3D=3Dend: 1 [ 26.186738] ceph_oob4_poc: triggering bare ceph_decode_32 past slab bo= undary... [ 26.187679] =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D [ 26.188236] BUG: KASAN: slab-out-of-bounds in ceph_oob4_init+0x22b/0xf= f0 [ceph_oob4_poc] [ 26.188236] Read of size 4 at addr ffff888009e31fa0 by task insmod/59 [ 26.188236] CPU: 0 UID: 0 PID: 59 Comm: insmod Tainted: G O = 7.0.0-rc7-g9c2abf69da83-dirty #15 PREEMPT(lazy) [ 26.188236] Call Trace: [ 26.188236] [ 26.188236] dump_stack_lvl+0x4d/0x70 [ 26.188236] print_report+0x170/0x4f3 [ 26.188236] kasan_report+0xda/0x110 [ 26.188236] ceph_oob4_init+0x22b/0xff0 [ceph_oob4_poc] [ 26.188236] do_one_initcall+0x9a/0x3a0 [ 26.188236] do_init_module+0x27c/0x790 [ 26.188236] load_module+0x4a9a/0x6350 [ 26.188236] init_module_from_file+0x15c/0x180 [ 26.188236] idempotent_init_module+0x21f/0x750 [ 26.188236] __x64_sys_finit_module+0xba/0x120 [ 26.188236] do_syscall_64+0xe2/0x570 [ 26.188236] entry_SYSCALL_64_after_hwframe+0x77/0x7f [ 26.188236] [ 26.188236] The buggy address belongs to the object at ffff888009e31000 [ 26.188236] which belongs to the cache kmalloc-4k of size 4096 [ 26.188236] The buggy address is located 0 bytes to the right of [ 26.188236] allocated 4000-byte region [ffff888009e31000, ffff888009e= 31fa0) [ 26.188236] ffff888009e31f80: 00 00 00 00 fc fc fc fc fc fc fc fc fc = fc fc fc [ 26.188236] ^ [ 26.188236] ffff888009e32000: fc fc fc fc fc fc fc fc fc fc fc fc fc = fc fc fc [ 26.188236] =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D [ 26.255513] ceph_oob4_poc: len=3D0xcccccccc (OOB garbage from KASAN re= dzone) 0xCCCCCCCC is KASAN redzone poison, confirming the read landed in the slab redzone immediately past the 4000-byte allocation. 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) without any further privileges beyond OSD session establishment. Fixes: d4ed4a530562 ("libceph: support for lock.lock_info") Cc: stable@vger.kernel.org Signed-off-by: Pavitra Jha --- v2: Move inline comments above ceph_decode_skip_n calls to stay within the 80-column limit, and rename label bad -> out_bad, per Viacheslav Dubeyko's review. --- net/ceph/cls_lock_client.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/net/ceph/cls_lock_client.c b/net/ceph/cls_lock_client.c index 78276273c..4f27b3d15 100644 --- a/net/ceph/cls_lock_client.c +++ b/net/ceph/cls_lock_client.c @@ -259,7 +259,7 @@ static int decode_locker(void **p, void *end, struct ce= ph_locker *locker) if (ret) return ret; =20 - ceph_decode_copy(p, &locker->id.name, sizeof(locker->id.name)); + ceph_decode_copy_safe(p, end, &locker->id.name, sizeof(locker->id.name), = out_bad); s =3D ceph_extract_encoded_string(p, end, NULL, GFP_NOIO); if (IS_ERR(s)) return PTR_ERR(s); @@ -270,19 +270,23 @@ static int decode_locker(void **p, void *end, struct = ceph_locker *locker) if (ret) return ret; =20 - *p +=3D sizeof(struct ceph_timespec); /* skip expiration */ + /* skip expiration */ + ceph_decode_skip_n(p, end, sizeof(struct ceph_timespec), out_bad); =20 ret =3D ceph_decode_entity_addr(p, end, &locker->info.addr); if (ret) return ret; =20 - len =3D ceph_decode_32(p); - *p +=3D len; /* skip description */ + ceph_decode_32_safe(p, end, len, out_bad); + /* skip description */ + ceph_decode_skip_n(p, end, len, out_bad); =20 dout("%s %s%llu cookie %s addr %s\n", __func__, ENTITY_NAME(locker->id.name), locker->id.cookie, ceph_pr_addr(&locker->info.addr)); return 0; +out_bad: + return -EINVAL; } =20 static int decode_lockers(void **p, void *end, u8 *type, char **tag, --=20 2.53.0