From: Daehyeon Ko <4ncienth@gmail.com>
To: Ilya Dryomov <idryomov@gmail.com>
Cc: Alex Markuze <amarkuze@redhat.com>,
Viacheslav Dubeyko <slava@dubeyko.com>,
ceph-devel@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: [PATCH] libceph: validate CRUSH bucket references before use
Date: Tue, 15 Sep 2026 08:58:17 +0900 [thread overview]
Message-ID: <20260914235817.2201175-1-4ncienth@gmail.com> (raw)
crush_decode() copies signed bucket item values directly from an OSDMap.
Nonnegative values identify devices and negative values identify buckets.
The firstn and indep mappers reject devices beyond max_devices, but index
map->buckets[-1-item] before validating a negative item's bucket index. A
malformed map can therefore cause an out-of-bounds pointer read and
dereference.
Commit 3cde4a830230 ("libceph: reject buckets with mismatched CRUSH ids")
validates an in-range bucket's own id. It does not validate the item which
selects the bucket-pointer slot.
Add a common negative-item lookup which calculates the index without signed
overflow and checks its range and pointer before use. Reuse the validated
pointer for type checks, descent and recursive choose.
An in-kernel KASAN module linked with the exact mapper reports 8-byte slab
out-of-bounds reads in firstn and indep on 3/3 fresh boots each. The fixed
mapper rejects both malformed modes in 3/3 boots without a KASAN report.
Valid firstn and indep controls remain unchanged.
Fixes: 5ecc0a0f8128 ("ceph: CRUSH mapping algorithm")
Cc: stable@vger.kernel.org
Assisted-by: LLM
Signed-off-by: Daehyeon Ko <4ncienth@gmail.com>
---
net/ceph/crush/mapper.c | 51 ++++++++++++++++++++++++++++-------------
1 file changed, 35 insertions(+), 16 deletions(-)
diff --git a/net/ceph/crush/mapper.c b/net/ceph/crush/mapper.c
index 17b041779fb9..4815a1b243cf 100644
--- a/net/ceph/crush/mapper.c
+++ b/net/ceph/crush/mapper.c
@@ -406,6 +406,21 @@ static int crush_bucket_choose(const struct crush_bucket *in,
}
}
+static const struct crush_bucket *
+crush_get_bucket(const struct crush_map *map, int item)
+{
+ __u32 pos;
+
+ if (item >= 0 || map->max_buckets <= 0)
+ return NULL;
+
+ pos = -1U - (__u32)item;
+ if (pos >= (__u32)map->max_buckets)
+ return NULL;
+
+ return map->buckets[pos];
+}
+
/*
* true if device is marked "out" (failed, fully offloaded)
* of the cluster
@@ -476,6 +491,7 @@ static int crush_choose_firstn(const struct crush_map *map,
int i;
int item = 0;
int itemtype;
+ const struct crush_bucket *item_bucket;
int collide, reject;
int count = out_size;
@@ -520,28 +536,29 @@ static int crush_choose_firstn(const struct crush_map *map,
(choose_args ?
&choose_args[-1-in->id] : NULL),
outpos);
- if (item >= map->max_devices) {
+ item_bucket = crush_get_bucket(map, item);
+ if (item >= map->max_devices ||
+ (item < 0 && !item_bucket)) {
dprintk(" bad item %d\n", item);
skip_rep = 1;
break;
}
/* desired type? */
- if (item < 0)
- itemtype = map->buckets[-1-item]->type;
+ if (item_bucket)
+ itemtype = item_bucket->type;
else
itemtype = 0;
dprintk(" item %d type %d\n", item, itemtype);
/* keep going? */
if (itemtype != type) {
- if (item >= 0 ||
- (-1-item) >= map->max_buckets) {
+ if (!item_bucket) {
dprintk(" bad item type %d\n", type);
skip_rep = 1;
break;
}
- in = map->buckets[-1-item];
+ in = item_bucket;
retry_bucket = 1;
continue;
}
@@ -556,7 +573,7 @@ static int crush_choose_firstn(const struct crush_map *map,
reject = 0;
if (!collide && recurse_to_leaf) {
- if (item < 0) {
+ if (item_bucket) {
int sub_r;
if (vary_r)
sub_r = r >> (vary_r-1);
@@ -565,7 +582,7 @@ static int crush_choose_firstn(const struct crush_map *map,
if (crush_choose_firstn(
map,
work,
- map->buckets[-1-item],
+ item_bucket,
weight, weight_max,
x, stable ? 1 : outpos+1, 0,
out2, outpos, count,
@@ -664,6 +681,7 @@ static void crush_choose_indep(const struct crush_map *map,
int i;
int item = 0;
int itemtype;
+ const struct crush_bucket *item_bucket;
int collide;
dprintk("CHOOSE%s INDEP bucket %d x %d outpos %d numrep %d\n", recurse_to_leaf ? "_LEAF" : "",
@@ -730,7 +748,9 @@ static void crush_choose_indep(const struct crush_map *map,
(choose_args ?
&choose_args[-1-in->id] : NULL),
outpos);
- if (item >= map->max_devices) {
+ item_bucket = crush_get_bucket(map, item);
+ if (item >= map->max_devices ||
+ (item < 0 && !item_bucket)) {
dprintk(" bad item %d\n", item);
out[rep] = CRUSH_ITEM_NONE;
if (out2)
@@ -740,16 +760,15 @@ static void crush_choose_indep(const struct crush_map *map,
}
/* desired type? */
- if (item < 0)
- itemtype = map->buckets[-1-item]->type;
+ if (item_bucket)
+ itemtype = item_bucket->type;
else
itemtype = 0;
dprintk(" item %d type %d\n", item, itemtype);
/* keep going? */
if (itemtype != type) {
- if (item >= 0 ||
- (-1-item) >= map->max_buckets) {
+ if (!item_bucket) {
dprintk(" bad item type %d\n", type);
out[rep] = CRUSH_ITEM_NONE;
if (out2)
@@ -758,7 +777,7 @@ static void crush_choose_indep(const struct crush_map *map,
left--;
break;
}
- in = map->buckets[-1-item];
+ in = item_bucket;
continue;
}
@@ -774,11 +793,11 @@ static void crush_choose_indep(const struct crush_map *map,
break;
if (recurse_to_leaf) {
- if (item < 0) {
+ if (item_bucket) {
crush_choose_indep(
map,
work,
- map->buckets[-1-item],
+ item_bucket,
weight, weight_max,
x, 1, numrep, 0,
out2, rep,
base-commit: 142040037eba7ddd2de6f13067f33585451b8efe
--
2.55.0
reply other threads:[~2026-09-14 23:58 UTC|newest]
Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260914235817.2201175-1-4ncienth@gmail.com \
--to=4ncienth@gmail.com \
--cc=amarkuze@redhat.com \
--cc=ceph-devel@vger.kernel.org \
--cc=idryomov@gmail.com \
--cc=linux-kernel@vger.kernel.org \
--cc=slava@dubeyko.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®