All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Abdurrahman Hussain" <abdurrahman@nexthop.ai>
To: <sashiko-reviews@lists.linux.dev>,
	"Abdurrahman Hussain" <abdurrahman@nexthop.ai>
Cc: <robh@kernel.org>, <conor+dt@kernel.org>, <devicetree@vger.kernel.org>
Subject: Re: [PATCH v7 05/10] of/overlay: only treat a positive changeset id as registered
Date: Thu, 03 Sep 2026 16:11:35 -0700	[thread overview]
Message-ID: <DL627V92FSCZ.2Y9QLHRJJWO2@nexthop.ai> (raw)
In-Reply-To: <20260901014554.83A8D1F000E9@smtp.kernel.org>

On Mon Aug 31, 2026 at 6:45 PM PDT, sashiko-bot wrote:
> Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
>
> Pre-existing issues:
> - [High] In `of_overlay_remove_all()`, passing `&ovcs->id` to `of_overlay_remove()` causes a deterministic use-after-free and leaves freed memory in the global `ovcs_list` and `ovcs_idr`.
> - [High] Lockless iteration of `ovcs_list` in `of_overlay_remove_all()` allows concurrent modifications, leading to a Use-After-Free.
> --
>
> commit 5e44e19c9c02429165243b3c23378b30ba40fdd3
> Author: Abdurrahman Hussain <abdurrahman@nexthop.ai>
>
> of/overlay: only treat a positive changeset id as registered
>
> of_overlay_fdt_apply() stores the idr_alloc() return value in
> ovcs->id before checking it. On failure the stored id is negative,
> free_overlay_changeset()'s "if (ovcs->id)" check passes, idr_remove()
> is called with a negative id and list_del() runs on ovcs->ovcs_list,
> which is not initialized until after the id allocation. An allocation
> failure at that point dereferences NULL.
>
>> diff --git a/drivers/of/overlay.c b/drivers/of/overlay.c
>> index 08d5351746be6..9b9f198a1d70a 100644
>> --- a/drivers/of/overlay.c
>> +++ b/drivers/of/overlay.c
> [ ... ]
>> @@ -860,7 +860,8 @@ static void free_overlay_changeset(struct overlay_changeset *ovcs)
>>  	if (ovcs->cset.entries.next)
>>  		of_changeset_destroy(&ovcs->cset);
>>  
>> -	if (ovcs->id) {
>> +	/* a failed idr_alloc() leaves its negative error in ovcs->id */
>> +	if (ovcs->id > 0) {
>
> [Severity: High]
> This isn't a bug introduced by this patch, but does this check cause a 
> use-after-free and list corruption when called from of_overlay_remove_all()?
>
> In of_overlay_remove_all(), we iterate over the ovcs_list and pass the 
> address of the internal id field:
>
> drivers/of/overlay.c:of_overlay_remove_all() {
>     list_for_each_entry_safe_reverse(ovcs, ovcs_n, &ovcs_list, ovcs_list) {
>         ret = of_overlay_remove(&ovcs->id);
> ...
> }
>
> Inside of_overlay_remove(), it zeros out this ID directly through the pointer 
> before freeing the changeset:
>
> drivers/of/overlay.c:of_overlay_remove() {
> ...
>     *ovcs_id = 0;
> ...
>     free_overlay_changeset(ovcs);
> ...
> }
>
> When free_overlay_changeset() executes, ovcs->id is already 0, so the 
> if (ovcs->id > 0) condition will evaluate to false. This skips both 
> idr_remove() and list_del(&ovcs->ovcs_list) before the memory is freed, 
> leaving stale pointers in the active global list and IDR.
>

Confirmed. of_overlay_remove_all() is the one caller that passes
&ovcs->id, so the "*ovcs_id = 0" in of_overlay_remove() zeroes
ovcs->id itself before free_overlay_changeset() runs. The
"if (ovcs->id > 0)" guard (previously "if (ovcs->id)") then skips
idr_remove() and list_del(), and the ovcs is kfree()d while still
linked in ovcs_list and registered in ovcs_idr. The next ovcs_list or
ovcs_idr access after that walks freed memory.

Pre-existing — the ordering dates to 24789c5ce5a3 ("of: overlay:
detect cases where device tree may become corrupt") — and latent, since
of_overlay_remove_all() currently has no in-tree callers. Still worth
fixing for module users. The minimal fix is to stop aliasing the
changeset's own field:

      list_for_each_entry_safe_reverse(ovcs, ovcs_n, &ovcs_list, ovcs_list) {
-             ret = of_overlay_remove(&ovcs->id);
+             int id = ovcs->id;
+
+             ret = of_overlay_remove(&id);

Rob, want this as one more patch in this series (it sits right next to
patch 5), or as a separate fix?

>
> [Severity: High]
> This is also a pre-existing issue, but could the lockless iteration in 
> of_overlay_remove_all() lead to a use-after-free?
>
> The iteration of ovcs_list in of_overlay_remove_all() allows concurrent 
> modifications:
>
> drivers/of/overlay.c:of_overlay_remove_all() {
>     list_for_each_entry_safe_reverse(ovcs, ovcs_n, &ovcs_list, ovcs_list) {
> ...
> }
>
> This iterates the globally shared ovcs_list without holding of_mutex. If 
> another thread concurrently calls of_overlay_remove() on a different overlay, 
> it will acquire of_mutex and remove or free that overlay.
>
> If the freed overlay was concurrently cached as ovcs_n (the 'next' element in 
> the safe iteration), the loop in of_overlay_remove_all() will dereference 
> the freed memory when advancing to the next iteration.
>
>>  		idr_remove(&ovcs_idr, ovcs->id);
>>  		list_del(&ovcs->ovcs_list);
>>  		ovcs->id = 0;
>>  	}

Known — you raised the same walk on v6, and it was deliberately left
out of this series' scope. It also can't be fixed locally: wrapping
the loop in of_mutex deadlocks against of_overlay_remove() taking
of_mutex itself, so it needs a __of_overlay_remove() split. That is
follow-up material, and equally unreachable in-tree today for the same
no-callers reason.

Thanks,
Abdurrahman

  reply	other threads:[~2026-09-03 23:11 UTC|newest]

Thread overview: 16+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-01  1:29 [PATCH v7 00/10] of: teach overlay code to keep /aliases in sync Abdurrahman Hussain
2026-09-01  1:29 ` [PATCH v7 01/10] of: hold a reference on of_aliases during alias path resolution Abdurrahman Hussain
2026-09-01  1:55   ` sashiko-bot
2026-09-03 23:17     ` Abdurrahman Hussain
2026-09-01  1:29 ` [PATCH v7 02/10] of: update /aliases lookup on reconfig notifications Abdurrahman Hussain
2026-09-01  1:45   ` sashiko-bot
2026-09-01  1:29 ` [PATCH v7 03/10] of/overlay: look up absolute target-paths absolutely Abdurrahman Hussain
2026-09-01  1:29 ` [PATCH v7 04/10] of/overlay: put property on deadprops only after changeset add succeeds Abdurrahman Hussain
2026-09-01  1:29 ` [PATCH v7 05/10] of/overlay: only treat a positive changeset id as registered Abdurrahman Hussain
2026-09-01  1:45   ` sashiko-bot
2026-09-03 23:11     ` Abdurrahman Hussain [this message]
2026-09-01  1:29 ` [PATCH v7 06/10] of/overlay: don't leak fragment references when changeset init fails Abdurrahman Hussain
2026-09-01  1:29 ` [PATCH v7 07/10] of/overlay: don't create "//" paths for fragments targeting the root Abdurrahman Hussain
2026-09-01  1:29 ` [PATCH v7 08/10] of/overlay: return ERR_PTR from dup_and_fixup_symbol_prop() Abdurrahman Hussain
2026-09-01  1:29 ` [PATCH v7 09/10] of/overlay: rewrite /aliases path values to live-tree paths Abdurrahman Hussain
2026-09-01  1:29 ` [PATCH v7 10/10] of: unittest: cover /aliases updates from overlay apply/revert Abdurrahman Hussain

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=DL627V92FSCZ.2Y9QLHRJJWO2@nexthop.ai \
    --to=abdurrahman@nexthop.ai \
    --cc=conor+dt@kernel.org \
    --cc=devicetree@vger.kernel.org \
    --cc=robh@kernel.org \
    --cc=sashiko-reviews@lists.linux.dev \
    /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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.