All of lore.kernel.org
 help / color / mirror / Atom feed
From: Bjorn Helgaas <helgaas@kernel.org>
To: Tong Zhang <ztong0001@gmail.com>
Cc: Scott Murray <scott@spiteful.org>,
	Bjorn Helgaas <bhelgaas@google.com>,
	linux-pci@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: Re: [PATCH] PCI: hotplug: fix null-ptr-dereferencd in cpcihp error path
Date: Tue, 23 Mar 2021 17:19:40 -0500	[thread overview]
Message-ID: <20210323221940.GA493013@bjorn-Precision-5520> (raw)
In-Reply-To: <20210321055109.322496-1-ztong0001@gmail.com>

On Sun, Mar 21, 2021 at 01:51:08AM -0400, Tong Zhang wrote:
> There is an issue in the error path, which cpci_thread may remain NULL.
> Calling kthread_stop(cpci_thread) will trigger a BUG().
> It is better to check whether the thread is really created and started
> before stop it.
> 
> [    1.292859] BUG: kernel NULL pointer dereference, address: 0000000000000028
> [    1.293252] #PF: supervisor write access in kernel mode
> [    1.293533] #PF: error_code(0x0002) - not-present page
> [    1.295163] RIP: 0010:kthread_stop+0x22/0x170
> [    1.300491] Call Trace:
> [    1.300628]  cpci_hp_unregister_controller+0xf6/0x130
> [    1.300906]  zt5550_hc_init_one+0x27a/0x27f [cpcihp_zt5550]

Wow, I didn't know anybody actually used this driver :)

> Signed-off-by: Tong Zhang <ztong0001@gmail.com>
> ---
>  drivers/pci/hotplug/cpci_hotplug_core.c | 12 ++++++------
>  1 file changed, 6 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/pci/hotplug/cpci_hotplug_core.c b/drivers/pci/hotplug/cpci_hotplug_core.c
> index d0559d2faf50..b44da397d631 100644
> --- a/drivers/pci/hotplug/cpci_hotplug_core.c
> +++ b/drivers/pci/hotplug/cpci_hotplug_core.c
> @@ -47,7 +47,7 @@ static atomic_t extracting;
>  int cpci_debug;
>  static struct cpci_hp_controller *controller;
>  static struct task_struct *cpci_thread;
> -static int thread_finished;
> +static int thread_started;

Why are we messing around with "thread_started" or "thread_finished"?
We should know whether cpci_thread has been started by the control
flow.

There are two ways to start cpci_thread:

  1)  cpcihp_generic_init                        # module_init function
        cpci_hp_start
          cpci_start_thread
            cpci_thread = kthread_run(...)

  2)  zt5550_hc_init_one                         # .probe function
        cpci_hp_start
          cpci_start_thread
            cpci_thread = kthread_run(...)

cpci_hp_start() returns a non-zero error if kthread_run() fails, and 
both cpcihp_generic_init() and zt5550_hc_init_one() clean up and exit
in that case.

The error cleanup is a little sloppy: if cpci_hp_register_bus() fails,
cpcihp_generic_init() calls cpci_hp_unregister_controller(), which
stops cpci_thread if it has been started.  But in that case, we *know*
there's no cpci_thread because we haven't even tried to start it.  I
think this error cleanup could be done better by splitting the
cpci_stop_thread() out from cpci_hp_unregister_controller() so it
could be done separately.  zt5550_hc_init_one() has a similar problem.

If cpcihp_generic_init() or zt5550_hc_init_one() succeeds, we *know*
there is a cpci_thread.  We should be able to call kthread_stop() on
it unconditionally in the cpcihp_generic_exit() and
zt5550_hc_remove_one() paths.

What do you think?  It's a little more restructuring work, but I think
"thread_started" and "thread_finished" are basically kind of kludgy
and they add complication without giving me confidence that they're
actually correct.

>  static int enable_slot(struct hotplug_slot *slot);
>  static int disable_slot(struct hotplug_slot *slot);
> @@ -447,7 +447,7 @@ event_thread(void *data)
>  				msleep(500);
>  			} else if (rc < 0) {
>  				dbg("%s - error checking slots", __func__);
> -				thread_finished = 1;
> +				thread_started = 0;
>  				goto out;
>  			}
>  		} while (atomic_read(&extracting) && !kthread_should_stop());
> @@ -479,7 +479,7 @@ poll_thread(void *data)
>  					msleep(500);
>  				} else if (rc < 0) {
>  					dbg("%s - error checking slots", __func__);
> -					thread_finished = 1;
> +					thread_started = 0;
>  					goto out;
>  				}
>  			} while (atomic_read(&extracting) && !kthread_should_stop());
> @@ -501,7 +501,7 @@ cpci_start_thread(void)
>  		err("Can't start up our thread");
>  		return PTR_ERR(cpci_thread);
>  	}
> -	thread_finished = 0;
> +	thread_started = 1;
>  	return 0;
>  }
>  
> @@ -509,7 +509,7 @@ static void
>  cpci_stop_thread(void)
>  {
>  	kthread_stop(cpci_thread);
> -	thread_finished = 1;
> +	thread_started = 0;
>  }
>  
>  int
> @@ -571,7 +571,7 @@ cpci_hp_unregister_controller(struct cpci_hp_controller *old_controller)
>  	int status = 0;
>  
>  	if (controller) {
> -		if (!thread_finished)
> +		if (thread_started)
>  			cpci_stop_thread();
>  		if (controller->irq)
>  			free_irq(controller->irq, controller->dev_id);
> -- 
> 2.25.1
> 

      reply	other threads:[~2021-03-23 22:22 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-03-21  5:51 [PATCH] PCI: hotplug: fix null-ptr-dereferencd in cpcihp error path Tong Zhang
2021-03-23 22:19 ` Bjorn Helgaas [this message]

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=20210323221940.GA493013@bjorn-Precision-5520 \
    --to=helgaas@kernel.org \
    --cc=bhelgaas@google.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-pci@vger.kernel.org \
    --cc=scott@spiteful.org \
    --cc=ztong0001@gmail.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 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.