Re: [PATCH v2] libceph: Assign requests to homeless osd if calculated osd exceeds max_osd

Ilya Dryomov <[email protected]> Mon, 27 Jul 2026 19:41:51 +0200
Newsgroups org.kernel.vger.ceph-devel
Message-ID <CAOi1vP8k02Xe0MX-+uYGOsSPP2MGijUHcVEa1WGs0yrqZGFWmQ@mail.gmail.com>
On Mon, Jul 27, 2026 at 5:32=E2=80=AFPM Raphael Zimmer
<[email protected]> wrote:
>
> On 27.07.26 12:35 PM, Ilya Dryomov wrote:
> > On Wed, Jul 1, 2026 at 5:56=E2=80=AFPM Raphael Zimmer
> > <[email protected]> wrote:
> >>
> >> A corrupted osdmap received from a Ceph monitor or OSD may contain
> >> placement groups with osd indices that don't exist, i.e., that are
> >> greater than max_osd or smaller than CEPH_HOMELESS_OSD (-1).
> >
> > Hi Raphael,
> >
> > The osdmap doesn't contain all placement group mappings -- they are
> > produced on demand by CRUSH based off of the crushmap that is embedded
> > in the osdmap but doesn't contain any OSD indices.  There are a few
> > (usually small or empty) exception tables in the form of pg_temp,
> > primary_temp, pg_upmap and pg_upmap_items where the "override" OSD
> > indices are stored.  Are those what you have in mind wrt. osdmap
> > corruption?
> >
>
> Hi Ilya,
>
> Yes, it's about these. I looked into it again after submitting this
> patch and concluded that the problematic part is specifically
> primary_temp. In the functions called from ceph_pg_to_up_acting_osds(),
> non-existent osds are removed from the osd sets. Therefore, the up set
> will not contain any invalid osds afterwards and will also have a valid
> primary, as it is always one of the osds in the set. Similarly,
> non-existent osds are removed from the acting set. However, the primary
> osd is directly assigned from pg->primary_temp.osd in get_temp_osds(),
> without checking for its existence. This way, an invalid osd index can
> be returned as target osd from calc_target().
>
> Therefore, it would suffice to add the check at this position (in
> get_temp_osds()) and set the primary osd to the homeless osd in this
> case (or perhaps to another osd from the acting set, but this doesn't
> seem like a good solution to me, as the osdmap may be corrupted even
> more and other osd mappings are also incorrect).

Setting the primary to the homeless OSD in get_temp_osds() would make
the OSD set invalid as far as osds_valid() is concerned.  I don't see
a problem with "another OSD" solution because corruptions of this kind
shouldn't ever happen in real deployments.

>
> > If so, I'd suggest adding map->max_osd checks to the corresponding
> > decode routines.  Munging ct_res to CALC_TARGET_NEED_RESEND as done in
> > this patch doesn't make much sense -- you are asking the OSD client to
> > arrange for the OSD request to be resent when there is no valid OSD to
> > send it to (and no expectation whatsoever that the resend's invocation
> > of calc_target() would produce a different result).
> >
>
> I don't completely agree on this matter. I specifically reassign the
> request to the homeless osd here and don't ask for resending it to an
> invalid one. This will result in not sending it out. __submit_request()
> only sends it if !osd_homeless(osd). The same holds true for
> kick_requests(). For linger requests, send_linger() also ultimately
> calls __submit_request(), leading to the same behavior. Finally,
> recalc_linger_target() only re-links the request to the homeless osd if
> calc_target() returns CALC_TARGET_NEED_RESEND.
> If applying the solution described above (checking in get_temp_osds()),
> this would furthermore only happen once.
>
> Adding map->max_osd checks to the corresponding decode routines would
> also be possible. I initially decided not to do this because the
> proposed solution needs fewer code changes and still resolves the issue
> of a potential out-of-bounds access. If we wanted to add checks to the
> decode functions, this should be done in all of them for consistency.
> That would mean we need to change multiple functions, including their
> signatures, to pass the map->max_osd into them.
>
> Do you still think that checks should be added to the decode functions,
> or would a check for either ceph_osd_is_down(pg->primary_temp.osd) or
> !ceph_osd_exists(pg->primary_temp.osd) in get_temp_osds() be an
> acceptable option too?

If there is confidence that the potential out-of-bounds read issue is
limited to primary_temp, I'd go with ceph_osd_is_down() check in
get_temp_osds() and ignore the primary_temp entry if the check fires on
the OSD in question -- the primary derived from pg_temp or the up set
primary (if no pg_temp) might very well be actionable in case of legit
mishap on the monitors.  I'd also warn though since this isn't supposed
to happen normally.  Something along the lines of

    /* primary_temp? (shouldn't ever be a nonexistent or down OSD) */
    pg =3D lookup_pg_mapping(&osdmap->primary_temp, pgid);
    if (pg && !WARN_ON_ONCE(ceph_osd_is_down(osdmap,
                                             pg->primary_temp.osd)))
            temp->primary =3D pg->primary_temp.osd;

What do you think?

Thanks,

                Ilya