public inbox for u-boot@lists.denx.de
 help / color / mirror / Atom feed
From: Marek Vasut <marek.vasut@mailbox.org>
To: Rasmus Villemoes <ravi@prevas.dk>,
	Marek Vasut <marek.vasut+renesas@mailbox.org>
Cc: u-boot@lists.denx.de, Aaron Williams <awilliams@marvell.com>,
	Anatolij Gustschin <agust@denx.de>,
	Angelo Dureghello <angelo@kernel-space.org>,
	Christian Marangi <ansuelsmth@gmail.com>,
	Devarsh Thakkar <devarsht@ti.com>,
	Heinrich Schuchardt <xypron.glpk@gmx.de>,
	Jaehoon Chung <jh80.chung@samsung.com>,
	Michael Polyntsov <michael.polyntsov@iopsys.eu>,
	Michael Trimarchi <michael@amarulasolutions.com>,
	Nikhil M Jain <n-jain1@ti.com>, Peng Fan <peng.fan@nxp.com>,
	Peter Robinson <pbrobinson@gmail.com>,
	Ronald Wahl <ronald.wahl@legrand.com>,
	Simon Glass <sjg@chromium.org>, Stefan Roese <sr@denx.de>,
	Tim Harvey <tharvey@gateworks.com>, Tom Rini <trini@konsulko.com>
Subject: Re: [PATCH 1/8] cyclic: Prevent corruption of cyclic list on reassignment
Date: Sat, 25 Jan 2025 14:22:59 +0100	[thread overview]
Message-ID: <f2192348-1c8b-4858-b138-e1a894dff92f@mailbox.org> (raw)
In-Reply-To: <87cyghx2ru.fsf@prevas.dk>

On 1/20/25 10:17 AM, Rasmus Villemoes wrote:
> On Sat, Jan 18 2025, Marek Vasut <marek.vasut+renesas@mailbox.org> wrote:
> 
>> Make cyclic_register() return error code, 0 in case of success,
>> -EALREADY in case the called attempts to re-register already
>> registered struct cyclic_info. The re-registration would lead
>> to corruption of gd->cyclic_list because the re-registration
>> would memset() one of its nodes, prevent that. Unregister only
>> initialized struct cyclic_info.
> 
> I had considered something like this, but I don't like it, because it
> relies on the cyclic structure (or more likely whatever structure it is
> embedded in) being initially zero-initialized.

True

> And if the caller doesn't
> know whether the cyclic_info is already registered or not, he can't do a
> memset() of it.

This is what can happen right now, which is dangerous and what this 
series attempts to address.

> So my preference would be that we instead simply iterate the current
> list to see if the struct cyclic_info is already registered that
> way.

That I can do, but it will be a bit slower.

> Also, I think I'd prefer if double cyclic_register() is allowed and
> always succeeds; this could be used to change the period of an already
> registered instance, for example.

This would be terribly overloaded interface, no, let's not do that.

Better introduce a dedicated function for that kind of period adjustment.

> Also, that avoids making the
> interfaces fallible.
> 
> And cyclic_unregister() could similarly just check
> whether the passed pointer is already on the list, and be a no-op in
> case it's not. Those extra list traversals are not expensive (we're
> traversing them thousands of times per second anyway in cyclic_run), and
> I doubt one would ever has more than about 10 items on the list.
> 
> IOW, I'd suggest adding an internal
> 
> bool cyclic_is_registered(struct cyclic_info *info)
> {
>    struct cyclic_info *c;
>    hlist_for_each(...) if (c == info) return true;

I don't think this works, because that struct cyclic_info contains 
.next_call member, which is updated over time, so this exact match would 
not work as-is. I have something like this now:

diff --git a/common/cyclic.c b/common/cyclic.c
index 807a3d73f67..d721a21a575 100644
--- a/common/cyclic.c
+++ b/common/cyclic.c
@@ -27,11 +27,29 @@ struct hlist_head *cyclic_get_list(void)
  	return (struct hlist_head *)&gd->cyclic_list;
  }

+static int cyclic_already_registered(struct cyclic_info *cyclic)
+{
+	struct cyclic_info *cycliclst;
+	struct hlist_node *tmp;
+
+	/* Reassignment of function would corrupt cyclic list, exit */
+	hlist_for_each_entry_safe(cycliclst, tmp, cyclic_get_list(), list) {
+		if (cycliclst->func == cyclic->func &&
+		    cycliclst->name == cyclic->name && // or strcmp() ?
+		    cycliclst->delay_us == cyclic->delay_us &&
+		    cycliclst->start_time_us == cyclic->start_time_us)
+			return -EALREADY;	/* Match found */
+	}
+
+	/* Match not found */
+	return 0;
+}
+
  int cyclic_register(struct cyclic_info *cyclic, cyclic_func_t func,
  		    uint64_t delay_us, const char *name)
  {
  	/* Reassignment of function would corrupt cyclic list, exit */
-	if (cyclic->func)
+	if (!cyclic_already_registered(cyclic))
  		return -EALREADY;

  	memset(cyclic, 0, sizeof(*cyclic));
@@ -49,7 +67,7 @@ int cyclic_register(struct cyclic_info *cyclic, 
cyclic_func_t func,
  void cyclic_unregister(struct cyclic_info *cyclic)
  {
  	/* Unregister only initialized struct cyclic_info */
-	if (cyclic->func)
+	if (cyclic_already_registered(cyclic))
  		hlist_del(&cyclic->list);
  }

[...]

>>   void cyclic_unregister(struct cyclic_info *cyclic)
>>   {
>> -	hlist_del(&cyclic->list);
>> +	/* Unregister only initialized struct cyclic_info */
>> +	if (cyclic->func)
>> +		hlist_del(&cyclic->list);
>>   }
> 
> So this already shows how error prone this approach is. You are not
> clearing cyclic->func, so if the caller subsequently tries to register
> that struct again, he would get -EALREADY, while a subsequent unregister
> could would lead to exactly the list corruption you want to avoid.

I would expect the caller should clear the structure before attempting 
to register it again. Shall we actually memset() the structure in 
cyclic_unregister() too ?

> And unless the caller immediately himself clears ->func, other code in
> the client cannot rely on ->func being NULL or not as a proxy for
> whether the struct is already registered (and the caller shouldn't do
> either of those things, as the struct cyclic_info should be considered
> opaque).

  parent reply	other threads:[~2025-01-25 13:23 UTC|newest]

Thread overview: 14+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-01-18  4:00 [PATCH 1/8] cyclic: Prevent corruption of cyclic list on reassignment Marek Vasut
2025-01-18  4:00 ` [PATCH 2/8] cyclic: Handle return code from cyclic_register() in demo and docs Marek Vasut
2025-01-18  4:00 ` [PATCH 3/8] cyclic: Test return code from cyclic_register() in test Marek Vasut
2025-01-18  4:00 ` [PATCH 4/8] mips: octeon: nic23: Handle return value from cyclic_register() Marek Vasut
2025-01-18  4:00 ` [PATCH 5/8] led: " Marek Vasut
2025-01-18  4:01 ` [PATCH 6/8] mmc: " Marek Vasut
2025-01-18  4:01 ` [PATCH 7/8] video: " Marek Vasut
2025-01-18  4:01 ` [PATCH 8/8] wdt: " Marek Vasut
2025-01-20  9:17 ` [PATCH 1/8] cyclic: Prevent corruption of cyclic list on reassignment Rasmus Villemoes
2025-01-22  9:25   ` Stefan Roese
2025-01-25 13:23     ` Marek Vasut
2025-01-25 13:22   ` Marek Vasut [this message]
2025-01-26 21:49     ` Rasmus Villemoes
2025-01-27 11:44       ` Marek Vasut

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=f2192348-1c8b-4858-b138-e1a894dff92f@mailbox.org \
    --to=marek.vasut@mailbox.org \
    --cc=agust@denx.de \
    --cc=angelo@kernel-space.org \
    --cc=ansuelsmth@gmail.com \
    --cc=awilliams@marvell.com \
    --cc=devarsht@ti.com \
    --cc=jh80.chung@samsung.com \
    --cc=marek.vasut+renesas@mailbox.org \
    --cc=michael.polyntsov@iopsys.eu \
    --cc=michael@amarulasolutions.com \
    --cc=n-jain1@ti.com \
    --cc=pbrobinson@gmail.com \
    --cc=peng.fan@nxp.com \
    --cc=ravi@prevas.dk \
    --cc=ronald.wahl@legrand.com \
    --cc=sjg@chromium.org \
    --cc=sr@denx.de \
    --cc=tharvey@gateworks.com \
    --cc=trini@konsulko.com \
    --cc=u-boot@lists.denx.de \
    --cc=xypron.glpk@gmx.de \
    /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