diff mbox series

[v1] parisc: Use for_each_console() helper

Message ID 20200124160739.65256-1-andriy.shevchenko@linux.intel.com (mailing list archive)
State Rejected, archived
Headers show
Series [v1] parisc: Use for_each_console() helper | expand

Commit Message

Andy Shevchenko Jan. 24, 2020, 4:07 p.m. UTC
Replace open coded single-linked list iteration loop with for_each_console()
helper in use.

Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
---
 arch/parisc/kernel/pdc_cons.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

Comments

James Bottomley Jan. 24, 2020, 4:39 p.m. UTC | #1
On Fri, 2020-01-24 at 18:07 +0200, Andy Shevchenko wrote:
> Replace open coded single-linked list iteration loop with
> for_each_console()
> helper in use.
> 
> Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
> ---
>  arch/parisc/kernel/pdc_cons.c | 4 ++--
>  1 file changed, 2 insertions(+), 2 deletions(-)
> 
> diff --git a/arch/parisc/kernel/pdc_cons.c
> b/arch/parisc/kernel/pdc_cons.c
> index 7ed404c60a9e..aa01448f377c 100644
> --- a/arch/parisc/kernel/pdc_cons.c
> +++ b/arch/parisc/kernel/pdc_cons.c
> @@ -259,8 +259,8 @@ void pdc_console_restart(void)
>  	if (console_drivers != NULL)
>  		pdc_cons.flags &= ~CON_PRINTBUFFER;
>  
> -	while ((console = console_drivers) != NULL)
> -		unregister_console(console_drivers);
> +	for_each_console(console)
> +		unregister_console(console);

This is wrong.  The old formulation iterates correctly in the face of
element removal.  for_each_console is defined:

#define for_each_console(con) \
	for (con = console_drivers; con != NULL; con = con->next)

So it's not safe for any iteration that alters the list elements.

James
Andy Shevchenko Jan. 24, 2020, 5:38 p.m. UTC | #2
On Fri, Jan 24, 2020 at 08:39:02AM -0800, James Bottomley wrote:
> On Fri, 2020-01-24 at 18:07 +0200, Andy Shevchenko wrote:
> > Replace open coded single-linked list iteration loop with
> > for_each_console()
> > helper in use.

> > -	while ((console = console_drivers) != NULL)
> > -		unregister_console(console_drivers);
> > +	for_each_console(console)
> > +		unregister_console(console);
> 
> This is wrong.  The old formulation iterates correctly in the face of
> element removal.  for_each_console is defined:
> 
> #define for_each_console(con) \
> 	for (con = console_drivers; con != NULL; con = con->next)
> 
> So it's not safe for any iteration that alters the list elements.

Ah, I see. In this case we need to keep a pointer to the next element.
Though, the original code assumes that console_drivers after unregistration
will be promoted to the next element. Do we have this assumption solid?

Thanks!
James Bottomley Jan. 24, 2020, 5:59 p.m. UTC | #3
On Fri, 2020-01-24 at 19:38 +0200, Andy Shevchenko wrote:
> On Fri, Jan 24, 2020 at 08:39:02AM -0800, James Bottomley wrote:
> > On Fri, 2020-01-24 at 18:07 +0200, Andy Shevchenko wrote:
> > > Replace open coded single-linked list iteration loop with
> > > for_each_console()
> > > helper in use.
> > > -	while ((console = console_drivers) != NULL)
> > > -		unregister_console(console_drivers);
> > > +	for_each_console(console)
> > > +		unregister_console(console);
> > 
> > This is wrong.  The old formulation iterates correctly in the face
> > of element removal.  for_each_console is defined:
> > 
> > #define for_each_console(con) \
> > 	for (con = console_drivers; con != NULL; con = con->next)
> > 
> > So it's not safe for any iteration that alters the list elements.
> 
> Ah, I see. In this case we need to keep a pointer to the next
> element. Though, the original code assumes that console_drivers after
> unregistration will be promoted to the next element. Do we have this
> assumption solid?

Yes, the original code simply removes the head until the list is empty.
 That's a recognized way of emptying any list while letting the remove
code take care of the locking ... it works because parisc doesn't have
a braille console.

James
Andy Shevchenko Jan. 25, 2020, 10:25 a.m. UTC | #4
On Fri, Jan 24, 2020 at 09:59:48AM -0800, James Bottomley wrote:
> On Fri, 2020-01-24 at 19:38 +0200, Andy Shevchenko wrote:
> > On Fri, Jan 24, 2020 at 08:39:02AM -0800, James Bottomley wrote:
> > > On Fri, 2020-01-24 at 18:07 +0200, Andy Shevchenko wrote:
> > > > Replace open coded single-linked list iteration loop with
> > > > for_each_console()
> > > > helper in use.
> > > > -	while ((console = console_drivers) != NULL)
> > > > -		unregister_console(console_drivers);
> > > > +	for_each_console(console)
> > > > +		unregister_console(console);
> > > 
> > > This is wrong.  The old formulation iterates correctly in the face
> > > of element removal.  for_each_console is defined:
> > > 
> > > #define for_each_console(con) \
> > > 	for (con = console_drivers; con != NULL; con = con->next)
> > > 
> > > So it's not safe for any iteration that alters the list elements.
> > 
> > Ah, I see. In this case we need to keep a pointer to the next
> > element. Though, the original code assumes that console_drivers after
> > unregistration will be promoted to the next element. Do we have this
> > assumption solid?
> 
> Yes, the original code simply removes the head until the list is empty.
>  That's a recognized way of emptying any list while letting the remove
> code take care of the locking ... it works because parisc doesn't have
> a braille console.

By the way, consider this code from register_console()

  for_each_console(bcon)
    if (bcon->flags & CON_BOOT)
      unregister_console(bcon);

It works based on assumption that next pointer of the just unregistered console
is not damaged. So, My initial patch will work in the same way.
Helge Deller Jan. 27, 2020, 8:48 a.m. UTC | #5
On 25.01.20 11:25, Andy Shevchenko wrote:
> On Fri, Jan 24, 2020 at 09:59:48AM -0800, James Bottomley wrote:
>> On Fri, 2020-01-24 at 19:38 +0200, Andy Shevchenko wrote:
>>> On Fri, Jan 24, 2020 at 08:39:02AM -0800, James Bottomley wrote:
>>>> On Fri, 2020-01-24 at 18:07 +0200, Andy Shevchenko wrote:
>>>>> Replace open coded single-linked list iteration loop with
>>>>> for_each_console()
>>>>> helper in use.
>>>>> -	while ((console = console_drivers) != NULL)
>>>>> -		unregister_console(console_drivers);
>>>>> +	for_each_console(console)
>>>>> +		unregister_console(console);
>>>>
>>>> This is wrong.  The old formulation iterates correctly in the face
>>>> of element removal.  for_each_console is defined:
>>>>
>>>> #define for_each_console(con) \
>>>> 	for (con = console_drivers; con != NULL; con = con->next)
>>>>
>>>> So it's not safe for any iteration that alters the list elements.
>>>
>>> Ah, I see. In this case we need to keep a pointer to the next
>>> element. Though, the original code assumes that console_drivers after
>>> unregistration will be promoted to the next element. Do we have this
>>> assumption solid?
>>
>> Yes, the original code simply removes the head until the list is empty.
>>  That's a recognized way of emptying any list while letting the remove
>> code take care of the locking ... it works because parisc doesn't have
>> a braille console.
>
> By the way, consider this code from register_console()
>
>   for_each_console(bcon)
>     if (bcon->flags & CON_BOOT)
>       unregister_console(bcon);
>
> It works based on assumption that next pointer of the just unregistered console
> is not damaged. So, My initial patch will work in the same way.

Yeah, but that's a typical use-after-free issue, which I wouldn't count on.
Isn't there a way to make both safe?

Helge
Andy Shevchenko Jan. 27, 2020, 9:47 a.m. UTC | #6
On Mon, Jan 27, 2020 at 09:48:58AM +0100, Helge Deller wrote:
> On 25.01.20 11:25, Andy Shevchenko wrote:
> > On Fri, Jan 24, 2020 at 09:59:48AM -0800, James Bottomley wrote:

...

> > By the way, consider this code from register_console()
> >
> >   for_each_console(bcon)
> >     if (bcon->flags & CON_BOOT)
> >       unregister_console(bcon);
> >
> > It works based on assumption that next pointer of the just unregistered console
> > is not damaged. So, My initial patch will work in the same way.
> 
> Yeah, but that's a typical use-after-free issue, which I wouldn't count on.

I think here is misinterpretation, i.e. unregister != free.
Entire console code is written in the assumption that console is not being
freed when unregistered.

+Cc: PRINTK people.

> Isn't there a way to make both safe?
Petr Mladek Jan. 30, 2020, 2:07 p.m. UTC | #7
On Mon 2020-01-27 11:47:22, Andy Shevchenko wrote:
> On Mon, Jan 27, 2020 at 09:48:58AM +0100, Helge Deller wrote:
> > On 25.01.20 11:25, Andy Shevchenko wrote:
> > > On Fri, Jan 24, 2020 at 09:59:48AM -0800, James Bottomley wrote:
> 
> ...
> 
> > > By the way, consider this code from register_console()
> > >
> > >   for_each_console(bcon)
> > >     if (bcon->flags & CON_BOOT)
> > >       unregister_console(bcon);
> > >
> > > It works based on assumption that next pointer of the just unregistered console
> > > is not damaged. So, My initial patch will work in the same way.
> > 
> > Yeah, but that's a typical use-after-free issue, which I wouldn't count on.
> 
> I think here is misinterpretation, i.e. unregister != free.
> Entire console code is written in the assumption that console is not being
> freed when unregistered.

Honestly, I am not sure if this is true for all console drivers
and if it is by design.

I would prefer to stay on the safe side and keep the original
code. Hotplug practices are more and more popular as everything
gets virtualized.

Best Regards,
Petr
diff mbox series

Patch

diff --git a/arch/parisc/kernel/pdc_cons.c b/arch/parisc/kernel/pdc_cons.c
index 7ed404c60a9e..aa01448f377c 100644
--- a/arch/parisc/kernel/pdc_cons.c
+++ b/arch/parisc/kernel/pdc_cons.c
@@ -259,8 +259,8 @@  void pdc_console_restart(void)
 	if (console_drivers != NULL)
 		pdc_cons.flags &= ~CON_PRINTBUFFER;
 
-	while ((console = console_drivers) != NULL)
-		unregister_console(console_drivers);
+	for_each_console(console)
+		unregister_console(console);
 
 	/* force registering the pdc console */
 	pdc_console_init_force();