public inbox for systemtap@sourceware.org
 help / color / mirror / Atom feed
* [PATCH] uprobes: register_uprobe() crashes when bailing out.
@ 2013-04-10 20:40 Torsten Polle
  2013-04-13  0:09 ` Frank Ch. Eigler
  0 siblings, 1 reply; 4+ messages in thread
From: Torsten Polle @ 2013-04-10 20:40 UTC (permalink / raw)
  To: systemtap

[-- Attachment #1: Type: text/plain, Size: 188 bytes --]

Hi,

I had a problem with register_uprobe() when it did not return
successfully. I've not checked whether any other uses of hlist_del() may
cause similar symptoms.

Kind Regards,
Torsten


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-uprobes-register_uprobe-crashes-when-bailing-out.patch --]
[-- Type: text/x-patch, Size: 1999 bytes --]

From 9ea411a8619d2fe7d927e8068c66059c7fd002a6 Mon Sep 17 00:00:00 2001
Message-Id: <9ea411a8619d2fe7d927e8068c66059c7fd002a6.1365626073.git.Torsten.Polle@gmx.de>
From: Torsten Polle <Torsten.Polle@gmx.de>
Date: Wed, 10 Apr 2013 22:33:47 +0200
Subject: [PATCH] uprobes: register_uprobe() crashes when bailing out.

uprobe_mk_process() initialises uproc->hlist, but does not put uproc on any
list, i.e. uproc_table. If register_uprobe() now bails out before uproc is put
on a list, uprobe_free_process() still tries to remove uproc from a list. But
hlist_del() only works, if the element is already on list. hlist_del_init()
first checks if the element is on any list, before it removes the element
(uproc) from the list.

Signed-off-by: Torsten Polle <Torsten.Polle@gmx.de>
---
 runtime/linux/uprobes/uprobes.c  |    2 +-
 runtime/linux/uprobes2/uprobes.c |    2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/runtime/linux/uprobes/uprobes.c b/runtime/linux/uprobes/uprobes.c
index 01e2652..100dee9 100644
--- a/runtime/linux/uprobes/uprobes.c
+++ b/runtime/linux/uprobes/uprobes.c
@@ -519,7 +519,7 @@ static void uprobe_free_process(struct uprobe_process *uproc)
 		uprobe_release_ssol_vma(uproc);
 	if (area->slots)
 		kfree(area->slots);
-	hlist_del(&uproc->hlist);
+	hlist_del_init(&uproc->hlist);
 	list_for_each_entry_safe(utask, tmp, &uproc->thread_list, list) {
 		/*
 		 * utrace_detach() is OK here (required, it seems) even if
diff --git a/runtime/linux/uprobes2/uprobes.c b/runtime/linux/uprobes2/uprobes.c
index bb997f2..b8003f9 100644
--- a/runtime/linux/uprobes2/uprobes.c
+++ b/runtime/linux/uprobes2/uprobes.c
@@ -611,7 +611,7 @@ static void uprobe_free_process(struct uprobe_process *uproc, int in_callback)
 
 	if (area->slots)
 		kfree(area->slots);
-	hlist_del(&uproc->hlist);
+	hlist_del_init(&uproc->hlist);
 	list_for_each_entry_safe(utask, tmp, &uproc->thread_list, list)
 		uprobe_free_task(utask, in_callback);
 	put_pid(uproc->tg_leader);
-- 
1.7.4.1


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH] uprobes: register_uprobe() crashes when bailing out.
  2013-04-10 20:40 [PATCH] uprobes: register_uprobe() crashes when bailing out Torsten Polle
@ 2013-04-13  0:09 ` Frank Ch. Eigler
  2013-04-13 11:19   ` Torsten Polle
  0 siblings, 1 reply; 4+ messages in thread
From: Frank Ch. Eigler @ 2013-04-13  0:09 UTC (permalink / raw)
  To: Torsten Polle; +Cc: systemtap


Hi -

Torsten.Polle wrote:

> [...]
> Subject: [PATCH] uprobes: register_uprobe() crashes when bailing out.
>
> uprobe_mk_process() initialises uproc->hlist, but does not put uproc on any
> list, i.e. uproc_table. If register_uprobe() now bails out before uproc is put
> on a list, uprobe_free_process() still tries to remove uproc from a list. But
> hlist_del() only works, if the element is already on list. hlist_del_init()
> first checks if the element is on any list, before it removes the element
> (uproc) from the list.

That description doesn't sound quite right to me.  I could be
mistaken, but I thought the hlist_del_init() variant was available so
that the hlist pointers are cleared after deletion (for purposes of
reuse in a different list perhaps), not in order to be checked for
list-membership before deletion.  This The change may still be right,
but perhaps for a different reason.

- FChE

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH] uprobes: register_uprobe() crashes when bailing out.
  2013-04-13  0:09 ` Frank Ch. Eigler
@ 2013-04-13 11:19   ` Torsten Polle
  2013-06-19 21:09     ` David Smith
  0 siblings, 1 reply; 4+ messages in thread
From: Torsten Polle @ 2013-04-13 11:19 UTC (permalink / raw)
  To: systemtap

Hi Frank,

Frank Ch Eigler writes:

 > Hi -

 > Torsten.Polle wrote:

 >> [...]
 >> Subject: [PATCH] uprobes: register_uprobe() crashes when bailing out.
 >> 
 >> uprobe_mk_process() initialises uproc->hlist, but does not put uproc
 >> on any list, i.e. uproc_table. If register_uprobe() now bails out
 >> before uproc is put on a list, uprobe_free_process() still tries to
 >> remove uproc from a list. But hlist_del() only works, if the element
 >> is already on list. hlist_del_init() first checks if the element is
 >> on any list, before it removes the element (uproc) from the list.

 > That description doesn't sound quite right to me.  I could be
 > mistaken, but I thought the hlist_del_init() variant was available so
 > that the hlist pointers are cleared after deletion (for purposes of
 > reuse in a different list perhaps), not in order to be checked for
 > list-membership before deletion.  This The change may still be right,
 > but perhaps for a different reason.

below is the flow of events in this case. I have an issue with a probe
definition which makes register_uprobe() bail out. (I will follow up on
the that issue after I'm through with the analysis.)

register_uprobe() is called.
	...
	uproc = uprobe_mk_process(p, 0);
		INIT_HLIST_NODE(&uproc->hlist);
			h->next = NULL;
			h->pprev = NULL;
	uproc_is_new = 1;

Now uproc->hlist.pprev is NULL.

I know why register_uprobe() bails out but not where. I only know that 
	goto fail_uproc;
is executed.

Therefore uprobe_free_process() is called. This in turn does the following.

hlist_del(&uproc->hlist);
	__hlist_del(n);
		struct hlist_node *next = n->next;
		struct hlist_node **pprev = n->pprev;
		*pprev = next;
As n->pprev is still NULL, *pprev leads to a NULL pointer exception.

The reason why I chose hlist_del_init() is that it first checks for
pprev to be not NULL by calling hlist_unhashed().

static inline void hlist_del_init(struct hlist_node *n)
{
	if (!hlist_unhashed(n)) {
		__hlist_del(n);
		INIT_HLIST_NODE(n);
	}
}

My idea was to either call hlist_unhashed() in uprobe_free_process() or
let hlist_del_init() do the job.

Kind Regards,
Torsten

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH] uprobes: register_uprobe() crashes when bailing out.
  2013-04-13 11:19   ` Torsten Polle
@ 2013-06-19 21:09     ` David Smith
  0 siblings, 0 replies; 4+ messages in thread
From: David Smith @ 2013-06-19 21:09 UTC (permalink / raw)
  To: Torsten Polle; +Cc: systemtap

On 04/13/2013 06:18 AM, Torsten Polle wrote:
> Hi Frank,
> 
> Frank Ch Eigler writes:
> 
>  > Hi -
> 
>  > Torsten.Polle wrote:
> 
>  >> [...]
>  >> Subject: [PATCH] uprobes: register_uprobe() crashes when bailing out.
>  >> 
>  >> uprobe_mk_process() initialises uproc->hlist, but does not put uproc
>  >> on any list, i.e. uproc_table. If register_uprobe() now bails out
>  >> before uproc is put on a list, uprobe_free_process() still tries to
>  >> remove uproc from a list. But hlist_del() only works, if the element
>  >> is already on list. hlist_del_init() first checks if the element is
>  >> on any list, before it removes the element (uproc) from the list.
> 
>  > That description doesn't sound quite right to me.  I could be
>  > mistaken, but I thought the hlist_del_init() variant was available so
>  > that the hlist pointers are cleared after deletion (for purposes of
>  > reuse in a different list perhaps), not in order to be checked for
>  > list-membership before deletion.  This The change may still be right,
>  > but perhaps for a different reason.
> 
> below is the flow of events in this case. I have an issue with a probe
> definition which makes register_uprobe() bail out. (I will follow up on
> the that issue after I'm through with the analysis.)
> 
> register_uprobe() is called.
> 	...
> 	uproc = uprobe_mk_process(p, 0);
> 		INIT_HLIST_NODE(&uproc->hlist);
> 			h->next = NULL;
> 			h->pprev = NULL;
> 	uproc_is_new = 1;
> 
> Now uproc->hlist.pprev is NULL.
> 
> I know why register_uprobe() bails out but not where. I only know that 
> 	goto fail_uproc;
> is executed.
> 
> Therefore uprobe_free_process() is called. This in turn does the following.
> 
> hlist_del(&uproc->hlist);
> 	__hlist_del(n);
> 		struct hlist_node *next = n->next;
> 		struct hlist_node **pprev = n->pprev;
> 		*pprev = next;
> As n->pprev is still NULL, *pprev leads to a NULL pointer exception.
> 
> The reason why I chose hlist_del_init() is that it first checks for
> pprev to be not NULL by calling hlist_unhashed().
> 
> static inline void hlist_del_init(struct hlist_node *n)
> {
> 	if (!hlist_unhashed(n)) {
> 		__hlist_del(n);
> 		INIT_HLIST_NODE(n);
> 	}
> }
> 
> My idea was to either call hlist_unhashed() in uprobe_free_process() or
> let hlist_del_init() do the job.

I took a look at this one and decided this change made sense. I decided
to call hlist_unhashed() in uprobe_free_process() instead of
hlist_del_init(), since that seemed to make a bit more sense.

Here's the commit:

====
commit 5bb23177feb7974ef726c6b34bf3461729aeadd2
Author: Torsten Polle <Torsten.Polle@gmx.de>
Date:   Wed Jun 19 16:05:48 2013 -0500

    Avoid uprobes crash on error.

    * runtime/linux/uprobes/uprobes.c (uprobe_free_process): Make sure the
      uprobe_proceess' hlist field is initialized before trying to
deleted it,
      to avoid a NULL pointer dereference.
    * runtime/linux/uprobes2/uprobes.c (uprobe_free_process): Ditto.
====

Thanks for the patch. Sorry it took so long to resolve.

-- 
David Smith
dsmith@redhat.com
Red Hat
http://www.redhat.com
256.217.0141 (direct)
256.837.0057 (fax)

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2013-06-19 21:09 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-04-10 20:40 [PATCH] uprobes: register_uprobe() crashes when bailing out Torsten Polle
2013-04-13  0:09 ` Frank Ch. Eigler
2013-04-13 11:19   ` Torsten Polle
2013-06-19 21:09     ` David Smith

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).