public inbox for systemtap@sourceware.org
 help / color / mirror / Atom feed
* [RFC][PATCH -tip 8/9] kprobes: support respawn probes for module  probing
@ 2009-03-20  0:08 Masami Hiramatsu
  2009-03-20  1:55 ` Frederic Weisbecker
  2009-03-20  3:03 ` Frederic Weisbecker
  0 siblings, 2 replies; 6+ messages in thread
From: Masami Hiramatsu @ 2009-03-20  0:08 UTC (permalink / raw)
  To: Ananth N Mavinakayanahalli, Ingo Molnar; +Cc: Jim Keniston, LKML, systemtap-ml

Add module_*probe API's to respawn probes on kernel modules.
kprobes which have been registered through register_module_*probe are
activated when the target module is loaded, and deactivated when unloading.
register_module_*probe require an activate_handler which is called right
before activating the probe, and if it returns !0, kprobes will be activated.

Thus, users can check whether the target module is true target or not
(e.g. checking build-id) in activate_handler.

Signed-off-by: Masami Hiramatsu <mhiramat@redhat.com>
Cc: Ananth N Mavinakayanahalli <ananth@in.ibm.com>
---
 include/linux/kprobes.h |   39 ++++++++
 kernel/kprobes.c        |  250 +++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 289 insertions(+), 0 deletions(-)

diff --git a/include/linux/kprobes.h b/include/linux/kprobes.h
index 2ec6cc1..9d47d08 100644
--- a/include/linux/kprobes.h
+++ b/include/linux/kprobes.h
@@ -37,6 +37,7 @@
 #include <linux/spinlock.h>
 #include <linux/rcupdate.h>
 #include <linux/mutex.h>
+#include <linux/module.h>

 #ifdef CONFIG_KPROBES
 #include <asm/kprobes.h>
@@ -69,6 +70,7 @@ typedef int (*kprobe_fault_handler_t) (struct kprobe *, struct pt_regs *,
 				       int trapnr);
 typedef int (*kretprobe_handler_t) (struct kretprobe_instance *,
 				    struct pt_regs *);
+typedef int (*probe_activate_handler_t)(void *, struct module *);

 struct kprobe {
 	struct hlist_node hlist;
@@ -279,6 +281,16 @@ void unregister_kretprobes(struct kretprobe **rps, int num);
 void kprobe_flush_task(struct task_struct *tk);
 void recycle_rp_inst(struct kretprobe_instance *ri, struct hlist_head *head);

+int register_module_kprobe(struct kprobe *kp,
+			   probe_activate_handler_t handler, void *data);
+int register_module_kretprobe(struct kretprobe *rp,
+			      probe_activate_handler_t handler, void *data);
+int register_module_jprobe(struct jprobe *jp,
+			   probe_activate_handler_t handler, void *data);
+void unregister_module_kprobe(struct kprobe *kp);
+void unregister_module_kretprobe(struct kretprobe *rp);
+void unregister_module_jprobe(struct jprobe *jp);
+
 #else /* !CONFIG_KPROBES: */

 static inline int kprobes_built_in(void)
@@ -345,5 +357,32 @@ static inline void unregister_kretprobes(struct kretprobe **rps, int num)
 static inline void kprobe_flush_task(struct task_struct *tk)
 {
 }
+static inline int register_module_kprobe(struct kprobe *kp,
+					 probe_activate_handler_t handler,
+					 void *data)
+{
+	return -ENOSYS;
+}
+static inline int register_module_kretprobe(struct kretprobe *rp,
+					    probe_activate_handler_t handler,
+					    void *data)
+{
+	return -ENOSYS;
+}
+static inline int register_module_jprobe(struct jprobe *jp,
+					 probe_activate_handler_t handler,
+					 void *data)
+{
+	return -ENOSYS;
+}
+void unregister_module_kprobe(struct kprobe *kp)
+{
+}
+void unregister_module_kretprobe(struct kretprobe *rp)
+{
+}
+void unregister_module_jprobe(struct jprobe *jp)
+{
+}
 #endif /* CONFIG_KPROBES */
 #endif /* _LINUX_KPROBES_H */
diff --git a/kernel/kprobes.c b/kernel/kprobes.c
index 5016bfb..f16a54e 100644
--- a/kernel/kprobes.c
+++ b/kernel/kprobes.c
@@ -1416,6 +1416,256 @@ static int __kprobes debugfs_kprobe_init(void)
 late_initcall(debugfs_kprobe_init);
 #endif /* CONFIG_DEBUG_FS */

+/* Kprobes module respawn support */
+enum probe_type {
+	PROBE_TYPE_KPROBE,
+	PROBE_TYPE_KRETPROBE,
+	PROBE_TYPE_JPROBE,
+};
+
+struct module_probe_client {
+	struct list_head list;
+	const char *module;	/* including symbol name */
+	int active;
+	void *data;
+	probe_activate_handler_t handler;
+	enum probe_type type;
+	union {
+		struct kprobe *kp;
+		struct kretprobe *rp;
+		struct jprobe *jp;
+	};
+};
+
+static DEFINE_MUTEX(module_probe_mutex);
+static LIST_HEAD(module_probe_list);
+
+static int activate_module_probe(struct module_probe_client *pc)
+{
+	int ret = 0;
+	if (pc->active)
+		return 0;
+	switch (pc->type) {
+	case PROBE_TYPE_KPROBE:
+		ret = register_kprobe(pc->kp);
+		break;
+	case PROBE_TYPE_KRETPROBE:
+		ret = register_kretprobe(pc->rp);
+		break;
+	case PROBE_TYPE_JPROBE:
+		ret = register_jprobe(pc->jp);
+		break;
+	default:
+		WARN_ON(1);
+		break;
+	}
+	if (!ret)
+		pc->active = 1;
+	return ret;
+}
+
+static void deactivate_module_probe(struct module_probe_client *pc)
+{
+	if (!pc->active)
+		return;
+	switch (pc->type) {
+	case PROBE_TYPE_KPROBE:
+		unregister_kprobe(pc->kp);
+		break;
+	case PROBE_TYPE_KRETPROBE:
+		unregister_kretprobe(pc->rp);
+		break;
+	case PROBE_TYPE_JPROBE:
+		unregister_jprobe(pc->jp);
+		break;
+	default:
+		WARN_ON(1);
+		break;
+	}
+	pc->active = 0;
+}
+
+static const char *probed_module_name(struct kprobe *kp)
+{
+	if ((kp->symbol_name) && strchr(kp->symbol_name, ':'))
+		return kp->symbol_name;
+	return NULL;
+}
+
+static int module_is_exist(const char *module)
+{
+	char buf[MODULE_NAME_LEN + 8];
+	snprintf(buf, MODULE_NAME_LEN + 8, "%s:__stext", module);
+	return module_kallsyms_lookup_name(buf) ? 1 : 0;
+}
+
+static int add_module_probe(const char *module, void *p, enum probe_type type,
+			    probe_activate_handler_t handler, void *data)
+{
+	struct module_probe_client *pc;
+	int ret = 0;
+
+	if (!handler)
+		return -EINVAL;
+
+	pc = kzalloc(sizeof(struct module_probe_client), GFP_KERNEL);
+	pc->kp = p;
+	pc->type = type;
+	pc->module = module;
+	pc->handler = handler;
+	pc->data = data;
+	INIT_LIST_HEAD(&pc->list);
+
+	mutex_lock(&module_probe_mutex);
+	if (module_is_exist(module))
+		ret = activate_module_probe(pc);
+	if (ret)
+		kfree(pc);
+	else
+		list_add_tail(&pc->list, &module_probe_list);
+	mutex_unlock(&module_probe_mutex);
+	return ret;
+}
+
+static void __del_module_probe(struct module_probe_client *pc)
+{
+	list_del(&pc->list);
+	deactivate_module_probe(pc);
+	kfree(pc);
+}
+
+static int del_module_probe(void *p)
+{
+	struct module_probe_client *pc;
+	int ret;
+
+	mutex_lock(&module_probe_mutex);
+	list_for_each_entry(pc, &module_probe_list, list)
+		if (pc->kp == p) {
+			/* don't need safe loop, we exit soon */
+			__del_module_probe(pc);
+			goto found;
+		}
+	ret = -ENOENT;
+found:
+	mutex_unlock(&module_probe_mutex);
+	return ret;
+}
+
+int __kprobes
+register_module_kprobe(struct kprobe *kp,
+		       probe_activate_handler_t handler, void *data)
+{
+	const char *module;
+	module = probed_module_name(kp);
+	if (!module)
+		return register_kprobe(kp);
+	return add_module_probe(module, kp, PROBE_TYPE_KPROBE,
+				handler, data);
+}
+EXPORT_SYMBOL_GPL(register_module_kprobe);
+
+int __kprobes
+register_module_kretprobe(struct kretprobe *rp,
+			  probe_activate_handler_t handler, void *data)
+{
+	const char *module;
+	module = probed_module_name(&rp->kp);
+	if (!module)
+		return register_kretprobe(rp);
+	return add_module_probe(module, rp, PROBE_TYPE_KRETPROBE,
+				handler, data);
+}
+EXPORT_SYMBOL_GPL(register_module_kretprobe);
+
+int __kprobes
+register_module_jprobe(struct jprobe *jp,
+		       probe_activate_handler_t handler, void *data)
+{
+	const char *module;
+	module = probed_module_name(&jp->kp);
+	if (!module)
+		return register_jprobe(jp);
+	return add_module_probe(module, jp, PROBE_TYPE_JPROBE,
+				handler, data);
+}
+EXPORT_SYMBOL_GPL(register_module_jprobe);
+
+void __kprobes unregister_module_kprobe(struct kprobe *kp)
+{
+	const char *module;
+	module = probed_module_name(kp);
+	if (!module)
+		unregister_kprobe(kp);
+	else
+		del_module_probe(kp);
+}
+EXPORT_SYMBOL_GPL(unregister_module_kprobe);
+
+void __kprobes unregister_module_kretprobe(struct kretprobe *rp)
+{
+	const char *module;
+	module = probed_module_name(&rp->kp);
+	if (!module)
+		unregister_kretprobe(rp);
+	else
+		del_module_probe(rp);
+}
+EXPORT_SYMBOL_GPL(unregister_module_kretprobe);
+
+void __kprobes unregister_module_jprobe(struct jprobe *jp)
+{
+	const char *module;
+	module = probed_module_name(&jp->kp);
+	if (!module)
+		unregister_jprobe(jp);
+	else
+		del_module_probe(jp);
+}
+EXPORT_SYMBOL_GPL(unregister_module_jprobe);
+
+static int module_is_probed(const char *mod, const char *sym)
+{
+	int len = strlen(mod);
+	return strncmp(mod, sym, len) == 0 && sym[len] == ':';
+}
+
+static int module_probe_callback(struct notifier_block *nb,
+				 unsigned long state, void *module)
+{
+	struct module_probe_client *pc;
+	struct module *mod = module;
+	if (state == MODULE_STATE_LIVE)
+		return NOTIFY_DONE;
+
+	mutex_lock(&module_probe_mutex);
+	list_for_each_entry(pc, &module_probe_list, list) {
+		if (!module_is_probed(mod->name, pc->module))
+			continue;
+		if (state == MODULE_STATE_COMING &&
+		    pc->handler(pc->data, module)) {
+			activate_module_probe(pc);
+		} else if (state == MODULE_STATE_GOING)
+			deactivate_module_probe(pc);
+	}
+	mutex_unlock(&module_probe_mutex);
+	return NOTIFY_DONE;
+}
+
+struct notifier_block module_probe_nb = {
+	.notifier_call = module_probe_callback
+};
+
+static int __init init_module_probes(void)
+{
+	int ret;
+	ret = register_module_notifier(&module_probe_nb);
+	if (ret)
+		pr_warning("Failed to register module notifier\n");
+	return ret;
+}
+module_init(init_module_probes);
+
 module_init(init_kprobes);

 EXPORT_SYMBOL_GPL(register_kprobe);
-- 
Masami Hiramatsu

Software Engineer
Hitachi Computer Products (America) Inc.
Software Solutions Division

e-mail: mhiramat@redhat.com


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

* Re: [RFC][PATCH -tip 8/9] kprobes: support respawn probes for  module probing
  2009-03-20  0:08 [RFC][PATCH -tip 8/9] kprobes: support respawn probes for module probing Masami Hiramatsu
@ 2009-03-20  1:55 ` Frederic Weisbecker
  2009-03-20  5:43   ` Masami Hiramatsu
  2009-03-20  3:03 ` Frederic Weisbecker
  1 sibling, 1 reply; 6+ messages in thread
From: Frederic Weisbecker @ 2009-03-20  1:55 UTC (permalink / raw)
  To: Masami Hiramatsu
  Cc: Ananth N Mavinakayanahalli, Ingo Molnar, Jim Keniston, LKML,
	systemtap-ml

On Thu, Mar 19, 2009 at 05:10:44PM -0400, Masami Hiramatsu wrote:
> Add module_*probe API's to respawn probes on kernel modules.
> kprobes which have been registered through register_module_*probe are
> activated when the target module is loaded, and deactivated when unloading.
> register_module_*probe require an activate_handler which is called right
> before activating the probe, and if it returns !0, kprobes will be activated.
> 
> Thus, users can check whether the target module is true target or not
> (e.g. checking build-id) in activate_handler.
> 
> Signed-off-by: Masami Hiramatsu <mhiramat@redhat.com>
> Cc: Ananth N Mavinakayanahalli <ananth@in.ibm.com>
> ---
>  include/linux/kprobes.h |   39 ++++++++
>  kernel/kprobes.c        |  250 +++++++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 289 insertions(+), 0 deletions(-)
> 
> diff --git a/include/linux/kprobes.h b/include/linux/kprobes.h
> index 2ec6cc1..9d47d08 100644
> --- a/include/linux/kprobes.h
> +++ b/include/linux/kprobes.h
> @@ -37,6 +37,7 @@
>  #include <linux/spinlock.h>
>  #include <linux/rcupdate.h>
>  #include <linux/mutex.h>
> +#include <linux/module.h>
> 
>  #ifdef CONFIG_KPROBES
>  #include <asm/kprobes.h>
> @@ -69,6 +70,7 @@ typedef int (*kprobe_fault_handler_t) (struct kprobe *, struct pt_regs *,
>  				       int trapnr);
>  typedef int (*kretprobe_handler_t) (struct kretprobe_instance *,
>  				    struct pt_regs *);
> +typedef int (*probe_activate_handler_t)(void *, struct module *);


I guess it could also be useful to call the handler when the module
is unloaded. Be using another parameter with a LOAD/UNLOAD enum value.


> 
>  struct kprobe {
>  	struct hlist_node hlist;
> @@ -279,6 +281,16 @@ void unregister_kretprobes(struct kretprobe **rps, int num);
>  void kprobe_flush_task(struct task_struct *tk);
>  void recycle_rp_inst(struct kretprobe_instance *ri, struct hlist_head *head);
> 
> +int register_module_kprobe(struct kprobe *kp,
> +			   probe_activate_handler_t handler, void *data);
> +int register_module_kretprobe(struct kretprobe *rp,
> +			      probe_activate_handler_t handler, void *data);
> +int register_module_jprobe(struct jprobe *jp,
> +			   probe_activate_handler_t handler, void *data);
> +void unregister_module_kprobe(struct kprobe *kp);
> +void unregister_module_kretprobe(struct kretprobe *rp);
> +void unregister_module_jprobe(struct jprobe *jp);
> +
>  #else /* !CONFIG_KPROBES: */
> 
>  static inline int kprobes_built_in(void)
> @@ -345,5 +357,32 @@ static inline void unregister_kretprobes(struct kretprobe **rps, int num)
>  static inline void kprobe_flush_task(struct task_struct *tk)
>  {
>  }
> +static inline int register_module_kprobe(struct kprobe *kp,
> +					 probe_activate_handler_t handler,
> +					 void *data)
> +{
> +	return -ENOSYS;
> +}
> +static inline int register_module_kretprobe(struct kretprobe *rp,
> +					    probe_activate_handler_t handler,
> +					    void *data)
> +{
> +	return -ENOSYS;
> +}
> +static inline int register_module_jprobe(struct jprobe *jp,
> +					 probe_activate_handler_t handler,
> +					 void *data)
> +{
> +	return -ENOSYS;
> +}
> +void unregister_module_kprobe(struct kprobe *kp)
> +{
> +}
> +void unregister_module_kretprobe(struct kretprobe *rp)
> +{
> +}
> +void unregister_module_jprobe(struct jprobe *jp)
> +{
> +}


Shouldn't the unregister_* be inlined too?
I think they all could be static inlined too in case of !CONFIG_MODULE


>  #endif /* CONFIG_KPROBES */
>  #endif /* _LINUX_KPROBES_H */
> diff --git a/kernel/kprobes.c b/kernel/kprobes.c
> index 5016bfb..f16a54e 100644
> --- a/kernel/kprobes.c
> +++ b/kernel/kprobes.c
> @@ -1416,6 +1416,256 @@ static int __kprobes debugfs_kprobe_init(void)
>  late_initcall(debugfs_kprobe_init);
>  #endif /* CONFIG_DEBUG_FS */
> 
> +/* Kprobes module respawn support */
> +enum probe_type {
> +	PROBE_TYPE_KPROBE,
> +	PROBE_TYPE_KRETPROBE,
> +	PROBE_TYPE_JPROBE,
> +};
> +
> +struct module_probe_client {
> +	struct list_head list;
> +	const char *module;	/* including symbol name */
> +	int active;
> +	void *data;
> +	probe_activate_handler_t handler;
> +	enum probe_type type;
> +	union {
> +		struct kprobe *kp;
> +		struct kretprobe *rp;
> +		struct jprobe *jp;
> +	};
> +};


#ifdef CONFIG_MODULE ?

Frederic.

> +static DEFINE_MUTEX(module_probe_mutex);
> +static LIST_HEAD(module_probe_list);
> +
> +static int activate_module_probe(struct module_probe_client *pc)
> +{
> +	int ret = 0;
> +	if (pc->active)
> +		return 0;
> +	switch (pc->type) {
> +	case PROBE_TYPE_KPROBE:
> +		ret = register_kprobe(pc->kp);
> +		break;
> +	case PROBE_TYPE_KRETPROBE:
> +		ret = register_kretprobe(pc->rp);
> +		break;
> +	case PROBE_TYPE_JPROBE:
> +		ret = register_jprobe(pc->jp);
> +		break;
> +	default:
> +		WARN_ON(1);
> +		break;
> +	}
> +	if (!ret)
> +		pc->active = 1;
> +	return ret;
> +}
> +
> +static void deactivate_module_probe(struct module_probe_client *pc)
> +{
> +	if (!pc->active)
> +		return;
> +	switch (pc->type) {
> +	case PROBE_TYPE_KPROBE:
> +		unregister_kprobe(pc->kp);
> +		break;
> +	case PROBE_TYPE_KRETPROBE:
> +		unregister_kretprobe(pc->rp);
> +		break;
> +	case PROBE_TYPE_JPROBE:
> +		unregister_jprobe(pc->jp);
> +		break;
> +	default:
> +		WARN_ON(1);
> +		break;
> +	}
> +	pc->active = 0;
> +}
> +
> +static const char *probed_module_name(struct kprobe *kp)
> +{
> +	if ((kp->symbol_name) && strchr(kp->symbol_name, ':'))
> +		return kp->symbol_name;
> +	return NULL;
> +}
> +
> +static int module_is_exist(const char *module)
> +{
> +	char buf[MODULE_NAME_LEN + 8];
> +	snprintf(buf, MODULE_NAME_LEN + 8, "%s:__stext", module);
> +	return module_kallsyms_lookup_name(buf) ? 1 : 0;
> +}
> +
> +static int add_module_probe(const char *module, void *p, enum probe_type type,
> +			    probe_activate_handler_t handler, void *data)
> +{
> +	struct module_probe_client *pc;
> +	int ret = 0;
> +
> +	if (!handler)
> +		return -EINVAL;
> +
> +	pc = kzalloc(sizeof(struct module_probe_client), GFP_KERNEL);
> +	pc->kp = p;
> +	pc->type = type;
> +	pc->module = module;
> +	pc->handler = handler;
> +	pc->data = data;
> +	INIT_LIST_HEAD(&pc->list);
> +
> +	mutex_lock(&module_probe_mutex);
> +	if (module_is_exist(module))
> +		ret = activate_module_probe(pc);
> +	if (ret)
> +		kfree(pc);
> +	else
> +		list_add_tail(&pc->list, &module_probe_list);
> +	mutex_unlock(&module_probe_mutex);
> +	return ret;
> +}
> +
> +static void __del_module_probe(struct module_probe_client *pc)
> +{
> +	list_del(&pc->list);
> +	deactivate_module_probe(pc);
> +	kfree(pc);
> +}
> +
> +static int del_module_probe(void *p)
> +{
> +	struct module_probe_client *pc;
> +	int ret;
> +
> +	mutex_lock(&module_probe_mutex);
> +	list_for_each_entry(pc, &module_probe_list, list)
> +		if (pc->kp == p) {
> +			/* don't need safe loop, we exit soon */
> +			__del_module_probe(pc);
> +			goto found;
> +		}
> +	ret = -ENOENT;
> +found:
> +	mutex_unlock(&module_probe_mutex);
> +	return ret;
> +}
> +
> +int __kprobes
> +register_module_kprobe(struct kprobe *kp,
> +		       probe_activate_handler_t handler, void *data)
> +{
> +	const char *module;
> +	module = probed_module_name(kp);
> +	if (!module)
> +		return register_kprobe(kp);
> +	return add_module_probe(module, kp, PROBE_TYPE_KPROBE,
> +				handler, data);
> +}
> +EXPORT_SYMBOL_GPL(register_module_kprobe);
> +
> +int __kprobes
> +register_module_kretprobe(struct kretprobe *rp,
> +			  probe_activate_handler_t handler, void *data)
> +{
> +	const char *module;
> +	module = probed_module_name(&rp->kp);
> +	if (!module)
> +		return register_kretprobe(rp);
> +	return add_module_probe(module, rp, PROBE_TYPE_KRETPROBE,
> +				handler, data);
> +}
> +EXPORT_SYMBOL_GPL(register_module_kretprobe);
> +
> +int __kprobes
> +register_module_jprobe(struct jprobe *jp,
> +		       probe_activate_handler_t handler, void *data)
> +{
> +	const char *module;
> +	module = probed_module_name(&jp->kp);
> +	if (!module)
> +		return register_jprobe(jp);
> +	return add_module_probe(module, jp, PROBE_TYPE_JPROBE,
> +				handler, data);
> +}
> +EXPORT_SYMBOL_GPL(register_module_jprobe);
> +
> +void __kprobes unregister_module_kprobe(struct kprobe *kp)
> +{
> +	const char *module;
> +	module = probed_module_name(kp);
> +	if (!module)
> +		unregister_kprobe(kp);
> +	else
> +		del_module_probe(kp);
> +}
> +EXPORT_SYMBOL_GPL(unregister_module_kprobe);
> +
> +void __kprobes unregister_module_kretprobe(struct kretprobe *rp)
> +{
> +	const char *module;
> +	module = probed_module_name(&rp->kp);
> +	if (!module)
> +		unregister_kretprobe(rp);
> +	else
> +		del_module_probe(rp);
> +}
> +EXPORT_SYMBOL_GPL(unregister_module_kretprobe);
> +
> +void __kprobes unregister_module_jprobe(struct jprobe *jp)
> +{
> +	const char *module;
> +	module = probed_module_name(&jp->kp);
> +	if (!module)
> +		unregister_jprobe(jp);
> +	else
> +		del_module_probe(jp);
> +}
> +EXPORT_SYMBOL_GPL(unregister_module_jprobe);
> +
> +static int module_is_probed(const char *mod, const char *sym)
> +{
> +	int len = strlen(mod);
> +	return strncmp(mod, sym, len) == 0 && sym[len] == ':';
> +}
> +
> +static int module_probe_callback(struct notifier_block *nb,
> +				 unsigned long state, void *module)
> +{
> +	struct module_probe_client *pc;
> +	struct module *mod = module;
> +	if (state == MODULE_STATE_LIVE)
> +		return NOTIFY_DONE;
> +
> +	mutex_lock(&module_probe_mutex);
> +	list_for_each_entry(pc, &module_probe_list, list) {
> +		if (!module_is_probed(mod->name, pc->module))
> +			continue;
> +		if (state == MODULE_STATE_COMING &&
> +		    pc->handler(pc->data, module)) {
> +			activate_module_probe(pc);
> +		} else if (state == MODULE_STATE_GOING)
> +			deactivate_module_probe(pc);
> +	}
> +	mutex_unlock(&module_probe_mutex);
> +	return NOTIFY_DONE;
> +}
> +
> +struct notifier_block module_probe_nb = {
> +	.notifier_call = module_probe_callback
> +};
> +
> +static int __init init_module_probes(void)
> +{
> +	int ret;
> +	ret = register_module_notifier(&module_probe_nb);
> +	if (ret)
> +		pr_warning("Failed to register module notifier\n");
> +	return ret;
> +}
> +module_init(init_module_probes);
> +
>  module_init(init_kprobes);
> 
>  EXPORT_SYMBOL_GPL(register_kprobe);
> -- 
> Masami Hiramatsu
> 
> Software Engineer
> Hitachi Computer Products (America) Inc.
> Software Solutions Division
> 
> e-mail: mhiramat@redhat.com
> 
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at  http://www.tux.org/lkml/

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

* Re: [RFC][PATCH -tip 8/9] kprobes: support respawn probes for  module probing
  2009-03-20  0:08 [RFC][PATCH -tip 8/9] kprobes: support respawn probes for module probing Masami Hiramatsu
  2009-03-20  1:55 ` Frederic Weisbecker
@ 2009-03-20  3:03 ` Frederic Weisbecker
  2009-03-20  4:41   ` Masami Hiramatsu
  1 sibling, 1 reply; 6+ messages in thread
From: Frederic Weisbecker @ 2009-03-20  3:03 UTC (permalink / raw)
  To: Masami Hiramatsu
  Cc: Ananth N Mavinakayanahalli, Ingo Molnar, Jim Keniston, LKML,
	systemtap-ml

On Thu, Mar 19, 2009 at 05:10:44PM -0400, Masami Hiramatsu wrote:
> Add module_*probe API's to respawn probes on kernel modules.
> kprobes which have been registered through register_module_*probe are
> activated when the target module is loaded, and deactivated when unloading.
> register_module_*probe require an activate_handler which is called right
> before activating the probe, and if it returns !0, kprobes will be activated.
> 
> Thus, users can check whether the target module is true target or not
> (e.g. checking build-id) in activate_handler.
> 
> Signed-off-by: Masami Hiramatsu <mhiramat@redhat.com>
> Cc: Ananth N Mavinakayanahalli <ananth@in.ibm.com>
> ---
>  include/linux/kprobes.h |   39 ++++++++
>  kernel/kprobes.c        |  250 +++++++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 289 insertions(+), 0 deletions(-)
> 
> diff --git a/include/linux/kprobes.h b/include/linux/kprobes.h
> index 2ec6cc1..9d47d08 100644
> --- a/include/linux/kprobes.h
> +++ b/include/linux/kprobes.h
> @@ -37,6 +37,7 @@
>  #include <linux/spinlock.h>
>  #include <linux/rcupdate.h>
>  #include <linux/mutex.h>
> +#include <linux/module.h>
> 
>  #ifdef CONFIG_KPROBES
>  #include <asm/kprobes.h>
> @@ -69,6 +70,7 @@ typedef int (*kprobe_fault_handler_t) (struct kprobe *, struct pt_regs *,
>  				       int trapnr);
>  typedef int (*kretprobe_handler_t) (struct kretprobe_instance *,
>  				    struct pt_regs *);
> +typedef int (*probe_activate_handler_t)(void *, struct module *);
> 
>  struct kprobe {
>  	struct hlist_node hlist;
> @@ -279,6 +281,16 @@ void unregister_kretprobes(struct kretprobe **rps, int num);
>  void kprobe_flush_task(struct task_struct *tk);
>  void recycle_rp_inst(struct kretprobe_instance *ri, struct hlist_head *head);
> 
> +int register_module_kprobe(struct kprobe *kp,
> +			   probe_activate_handler_t handler, void *data);
> +int register_module_kretprobe(struct kretprobe *rp,
> +			      probe_activate_handler_t handler, void *data);
> +int register_module_jprobe(struct jprobe *jp,
> +			   probe_activate_handler_t handler, void *data);
> +void unregister_module_kprobe(struct kprobe *kp);
> +void unregister_module_kretprobe(struct kretprobe *rp);
> +void unregister_module_jprobe(struct jprobe *jp);
> +
>  #else /* !CONFIG_KPROBES: */
> 
>  static inline int kprobes_built_in(void)
> @@ -345,5 +357,32 @@ static inline void unregister_kretprobes(struct kretprobe **rps, int num)
>  static inline void kprobe_flush_task(struct task_struct *tk)
>  {
>  }
> +static inline int register_module_kprobe(struct kprobe *kp,
> +					 probe_activate_handler_t handler,
> +					 void *data)
> +{
> +	return -ENOSYS;
> +}
> +static inline int register_module_kretprobe(struct kretprobe *rp,
> +					    probe_activate_handler_t handler,
> +					    void *data)
> +{
> +	return -ENOSYS;
> +}
> +static inline int register_module_jprobe(struct jprobe *jp,
> +					 probe_activate_handler_t handler,
> +					 void *data)
> +{
> +	return -ENOSYS;
> +}
> +void unregister_module_kprobe(struct kprobe *kp)
> +{
> +}
> +void unregister_module_kretprobe(struct kretprobe *rp)
> +{
> +}
> +void unregister_module_jprobe(struct jprobe *jp)
> +{
> +}
>  #endif /* CONFIG_KPROBES */
>  #endif /* _LINUX_KPROBES_H */
> diff --git a/kernel/kprobes.c b/kernel/kprobes.c
> index 5016bfb..f16a54e 100644
> --- a/kernel/kprobes.c
> +++ b/kernel/kprobes.c
> @@ -1416,6 +1416,256 @@ static int __kprobes debugfs_kprobe_init(void)
>  late_initcall(debugfs_kprobe_init);
>  #endif /* CONFIG_DEBUG_FS */
> 
> +/* Kprobes module respawn support */
> +enum probe_type {
> +	PROBE_TYPE_KPROBE,
> +	PROBE_TYPE_KRETPROBE,
> +	PROBE_TYPE_JPROBE,
> +};
> +
> +struct module_probe_client {
> +	struct list_head list;
> +	const char *module;	/* including symbol name */
> +	int active;
> +	void *data;
> +	probe_activate_handler_t handler;
> +	enum probe_type type;
> +	union {
> +		struct kprobe *kp;
> +		struct kretprobe *rp;
> +		struct jprobe *jp;
> +	};
> +};
> +
> +static DEFINE_MUTEX(module_probe_mutex);
> +static LIST_HEAD(module_probe_list);
> +
> +static int activate_module_probe(struct module_probe_client *pc)
> +{
> +	int ret = 0;
> +	if (pc->active)
> +		return 0;
> +	switch (pc->type) {
> +	case PROBE_TYPE_KPROBE:
> +		ret = register_kprobe(pc->kp);
> +		break;
> +	case PROBE_TYPE_KRETPROBE:
> +		ret = register_kretprobe(pc->rp);
> +		break;
> +	case PROBE_TYPE_JPROBE:
> +		ret = register_jprobe(pc->jp);
> +		break;
> +	default:
> +		WARN_ON(1);
> +		break;
> +	}
> +	if (!ret)
> +		pc->active = 1;
> +	return ret;
> +}
> +
> +static void deactivate_module_probe(struct module_probe_client *pc)
> +{
> +	if (!pc->active)
> +		return;
> +	switch (pc->type) {
> +	case PROBE_TYPE_KPROBE:
> +		unregister_kprobe(pc->kp);
> +		break;
> +	case PROBE_TYPE_KRETPROBE:
> +		unregister_kretprobe(pc->rp);
> +		break;
> +	case PROBE_TYPE_JPROBE:
> +		unregister_jprobe(pc->jp);
> +		break;
> +	default:
> +		WARN_ON(1);
> +		break;
> +	}
> +	pc->active = 0;
> +}
> +
> +static const char *probed_module_name(struct kprobe *kp)
> +{
> +	if ((kp->symbol_name) && strchr(kp->symbol_name, ':'))
> +		return kp->symbol_name;
> +	return NULL;
> +}
> +
> +static int module_is_exist(const char *module)
> +{
> +	char buf[MODULE_NAME_LEN + 8];
> +	snprintf(buf, MODULE_NAME_LEN + 8, "%s:__stext", module);
> +	return module_kallsyms_lookup_name(buf) ? 1 : 0;
> +}
> +
> +static int add_module_probe(const char *module, void *p, enum probe_type type,
> +			    probe_activate_handler_t handler, void *data)
> +{
> +	struct module_probe_client *pc;
> +	int ret = 0;
> +
> +	if (!handler)
> +		return -EINVAL;
> +
> +	pc = kzalloc(sizeof(struct module_probe_client), GFP_KERNEL);
> +	pc->kp = p;
> +	pc->type = type;
> +	pc->module = module;
> +	pc->handler = handler;
> +	pc->data = data;
> +	INIT_LIST_HEAD(&pc->list);
> +
> +	mutex_lock(&module_probe_mutex);
> +	if (module_is_exist(module))
> +		ret = activate_module_probe(pc);
> +	if (ret)
> +		kfree(pc);
> +	else
> +		list_add_tail(&pc->list, &module_probe_list);
> +	mutex_unlock(&module_probe_mutex);
> +	return ret;
> +}
> +
> +static void __del_module_probe(struct module_probe_client *pc)
> +{
> +	list_del(&pc->list);
> +	deactivate_module_probe(pc);
> +	kfree(pc);
> +}
> +
> +static int del_module_probe(void *p)
> +{
> +	struct module_probe_client *pc;
> +	int ret;
> +
> +	mutex_lock(&module_probe_mutex);
> +	list_for_each_entry(pc, &module_probe_list, list)
> +		if (pc->kp == p) {
> +			/* don't need safe loop, we exit soon */
> +			__del_module_probe(pc);
> +			goto found;
> +		}
> +	ret = -ENOENT;
> +found:
> +	mutex_unlock(&module_probe_mutex);
> +	return ret;
> +}
> +
> +int __kprobes
> +register_module_kprobe(struct kprobe *kp,
> +		       probe_activate_handler_t handler, void *data)
> +{
> +	const char *module;
> +	module = probed_module_name(kp);
> +	if (!module)
> +		return register_kprobe(kp);
> +	return add_module_probe(module, kp, PROBE_TYPE_KPROBE,
> +				handler, data);
> +}
> +EXPORT_SYMBOL_GPL(register_module_kprobe);
> +
> +int __kprobes
> +register_module_kretprobe(struct kretprobe *rp,
> +			  probe_activate_handler_t handler, void *data)
> +{
> +	const char *module;
> +	module = probed_module_name(&rp->kp);
> +	if (!module)
> +		return register_kretprobe(rp);
> +	return add_module_probe(module, rp, PROBE_TYPE_KRETPROBE,
> +				handler, data);
> +}
> +EXPORT_SYMBOL_GPL(register_module_kretprobe);
> +
> +int __kprobes
> +register_module_jprobe(struct jprobe *jp,
> +		       probe_activate_handler_t handler, void *data)
> +{
> +	const char *module;
> +	module = probed_module_name(&jp->kp);
> +	if (!module)
> +		return register_jprobe(jp);
> +	return add_module_probe(module, jp, PROBE_TYPE_JPROBE,
> +				handler, data);
> +}
> +EXPORT_SYMBOL_GPL(register_module_jprobe);
> +
> +void __kprobes unregister_module_kprobe(struct kprobe *kp)
> +{
> +	const char *module;
> +	module = probed_module_name(kp);
> +	if (!module)
> +		unregister_kprobe(kp);
> +	else
> +		del_module_probe(kp);
> +}
> +EXPORT_SYMBOL_GPL(unregister_module_kprobe);
> +
> +void __kprobes unregister_module_kretprobe(struct kretprobe *rp)
> +{
> +	const char *module;
> +	module = probed_module_name(&rp->kp);
> +	if (!module)
> +		unregister_kretprobe(rp);
> +	else
> +		del_module_probe(rp);
> +}
> +EXPORT_SYMBOL_GPL(unregister_module_kretprobe);
> +
> +void __kprobes unregister_module_jprobe(struct jprobe *jp)
> +{
> +	const char *module;
> +	module = probed_module_name(&jp->kp);
> +	if (!module)
> +		unregister_jprobe(jp);
> +	else
> +		del_module_probe(jp);
> +}
> +EXPORT_SYMBOL_GPL(unregister_module_jprobe);
> +
> +static int module_is_probed(const char *mod, const char *sym)
> +{
> +	int len = strlen(mod);
> +	return strncmp(mod, sym, len) == 0 && sym[len] == ':';
> +}
> +
> +static int module_probe_callback(struct notifier_block *nb,
> +				 unsigned long state, void *module)
> +{
> +	struct module_probe_client *pc;
> +	struct module *mod = module;
> +	if (state == MODULE_STATE_LIVE)
> +		return NOTIFY_DONE;
> +
> +	mutex_lock(&module_probe_mutex);
> +	list_for_each_entry(pc, &module_probe_list, list) {
> +		if (!module_is_probed(mod->name, pc->module))
> +			continue;
> +		if (state == MODULE_STATE_COMING &&
> +		    pc->handler(pc->data, module)) {


I don't see a place where you check if pc->handler != NULL
May be you could attach a stub in such cases.

Frederic.


> +			activate_module_probe(pc);
> +		} else if (state == MODULE_STATE_GOING)
> +			deactivate_module_probe(pc);
> +	}
> +	mutex_unlock(&module_probe_mutex);
> +	return NOTIFY_DONE;
> +}
> +
> +struct notifier_block module_probe_nb = {
> +	.notifier_call = module_probe_callback
> +};
> +
> +static int __init init_module_probes(void)
> +{
> +	int ret;
> +	ret = register_module_notifier(&module_probe_nb);
> +	if (ret)
> +		pr_warning("Failed to register module notifier\n");
> +	return ret;
> +}
> +module_init(init_module_probes);
> +
>  module_init(init_kprobes);
> 
>  EXPORT_SYMBOL_GPL(register_kprobe);
> -- 
> Masami Hiramatsu
> 
> Software Engineer
> Hitachi Computer Products (America) Inc.
> Software Solutions Division
> 
> e-mail: mhiramat@redhat.com
> 
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at  http://www.tux.org/lkml/

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

* Re: [RFC][PATCH -tip 8/9] kprobes: support respawn probes for module  probing
  2009-03-20  3:03 ` Frederic Weisbecker
@ 2009-03-20  4:41   ` Masami Hiramatsu
  2009-03-21  1:32     ` Frederic Weisbecker
  0 siblings, 1 reply; 6+ messages in thread
From: Masami Hiramatsu @ 2009-03-20  4:41 UTC (permalink / raw)
  To: Frederic Weisbecker
  Cc: Ananth N Mavinakayanahalli, Ingo Molnar, Jim Keniston, LKML,
	systemtap-ml

Frederic Weisbecker wrote:
>> diff --git a/kernel/kprobes.c b/kernel/kprobes.c
>> index 5016bfb..f16a54e 100644
>> --- a/kernel/kprobes.c
>> +++ b/kernel/kprobes.c
>> @@ -1416,6 +1416,256 @@ static int __kprobes debugfs_kprobe_init(void)
>>  late_initcall(debugfs_kprobe_init);
>>  #endif /* CONFIG_DEBUG_FS */
>>
>> +/* Kprobes module respawn support */
>> +enum probe_type {
>> +	PROBE_TYPE_KPROBE,
>> +	PROBE_TYPE_KRETPROBE,
>> +	PROBE_TYPE_JPROBE,
>> +};
>> +
>> +struct module_probe_client {
>> +	struct list_head list;
>> +	const char *module;	/* including symbol name */
>> +	int active;
>> +	void *data;
>> +	probe_activate_handler_t handler;
>> +	enum probe_type type;
>> +	union {
>> +		struct kprobe *kp;
>> +		struct kretprobe *rp;
>> +		struct jprobe *jp;
>> +	};
>> +};
>> +
>> +static DEFINE_MUTEX(module_probe_mutex);
>> +static LIST_HEAD(module_probe_list);
>> +
>> +static int activate_module_probe(struct module_probe_client *pc)
>> +{
>> +	int ret = 0;
>> +	if (pc->active)
>> +		return 0;
>> +	switch (pc->type) {
>> +	case PROBE_TYPE_KPROBE:
>> +		ret = register_kprobe(pc->kp);
>> +		break;
>> +	case PROBE_TYPE_KRETPROBE:
>> +		ret = register_kretprobe(pc->rp);
>> +		break;
>> +	case PROBE_TYPE_JPROBE:
>> +		ret = register_jprobe(pc->jp);
>> +		break;
>> +	default:
>> +		WARN_ON(1);
>> +		break;
>> +	}
>> +	if (!ret)
>> +		pc->active = 1;
>> +	return ret;
>> +}
>> +
>> +static void deactivate_module_probe(struct module_probe_client *pc)
>> +{
>> +	if (!pc->active)
>> +		return;
>> +	switch (pc->type) {
>> +	case PROBE_TYPE_KPROBE:
>> +		unregister_kprobe(pc->kp);
>> +		break;
>> +	case PROBE_TYPE_KRETPROBE:
>> +		unregister_kretprobe(pc->rp);
>> +		break;
>> +	case PROBE_TYPE_JPROBE:
>> +		unregister_jprobe(pc->jp);
>> +		break;
>> +	default:
>> +		WARN_ON(1);
>> +		break;
>> +	}
>> +	pc->active = 0;
>> +}
>> +
>> +static const char *probed_module_name(struct kprobe *kp)
>> +{
>> +	if ((kp->symbol_name) && strchr(kp->symbol_name, ':'))
>> +		return kp->symbol_name;
>> +	return NULL;
>> +}
>> +
>> +static int module_is_exist(const char *module)
>> +{
>> +	char buf[MODULE_NAME_LEN + 8];
>> +	snprintf(buf, MODULE_NAME_LEN + 8, "%s:__stext", module);
>> +	return module_kallsyms_lookup_name(buf) ? 1 : 0;
>> +}
>> +
>> +static int add_module_probe(const char *module, void *p, enum probe_type type,
>> +			    probe_activate_handler_t handler, void *data)
>> +{
>> +	struct module_probe_client *pc;
>> +	int ret = 0;
>> +
>> +	if (!handler)
>> +		return -EINVAL;
>> +
>> +	pc = kzalloc(sizeof(struct module_probe_client), GFP_KERNEL);
>> +	pc->kp = p;
>> +	pc->type = type;
>> +	pc->module = module;
>> +	pc->handler = handler;
>> +	pc->data = data;
>> +	INIT_LIST_HEAD(&pc->list);
>> +
>> +	mutex_lock(&module_probe_mutex);
>> +	if (module_is_exist(module))
>> +		ret = activate_module_probe(pc);
>> +	if (ret)
>> +		kfree(pc);
>> +	else
>> +		list_add_tail(&pc->list, &module_probe_list);
>> +	mutex_unlock(&module_probe_mutex);
>> +	return ret;
>> +}
>> +
>> +static void __del_module_probe(struct module_probe_client *pc)
>> +{
>> +	list_del(&pc->list);
>> +	deactivate_module_probe(pc);
>> +	kfree(pc);
>> +}
>> +
>> +static int del_module_probe(void *p)
>> +{
>> +	struct module_probe_client *pc;
>> +	int ret;
>> +
>> +	mutex_lock(&module_probe_mutex);
>> +	list_for_each_entry(pc, &module_probe_list, list)
>> +		if (pc->kp == p) {
>> +			/* don't need safe loop, we exit soon */
>> +			__del_module_probe(pc);
>> +			goto found;
>> +		}
>> +	ret = -ENOENT;
>> +found:
>> +	mutex_unlock(&module_probe_mutex);
>> +	return ret;
>> +}
>> +
>> +int __kprobes
>> +register_module_kprobe(struct kprobe *kp,
>> +		       probe_activate_handler_t handler, void *data)
>> +{
>> +	const char *module;
>> +	module = probed_module_name(kp);
>> +	if (!module)
>> +		return register_kprobe(kp);
>> +	return add_module_probe(module, kp, PROBE_TYPE_KPROBE,
>> +				handler, data);
>> +}
>> +EXPORT_SYMBOL_GPL(register_module_kprobe);
>> +
>> +int __kprobes
>> +register_module_kretprobe(struct kretprobe *rp,
>> +			  probe_activate_handler_t handler, void *data)
>> +{
>> +	const char *module;
>> +	module = probed_module_name(&rp->kp);
>> +	if (!module)
>> +		return register_kretprobe(rp);
>> +	return add_module_probe(module, rp, PROBE_TYPE_KRETPROBE,
>> +				handler, data);
>> +}
>> +EXPORT_SYMBOL_GPL(register_module_kretprobe);
>> +
>> +int __kprobes
>> +register_module_jprobe(struct jprobe *jp,
>> +		       probe_activate_handler_t handler, void *data)
>> +{
>> +	const char *module;
>> +	module = probed_module_name(&jp->kp);
>> +	if (!module)
>> +		return register_jprobe(jp);
>> +	return add_module_probe(module, jp, PROBE_TYPE_JPROBE,
>> +				handler, data);
>> +}
>> +EXPORT_SYMBOL_GPL(register_module_jprobe);
>> +
>> +void __kprobes unregister_module_kprobe(struct kprobe *kp)
>> +{
>> +	const char *module;
>> +	module = probed_module_name(kp);
>> +	if (!module)
>> +		unregister_kprobe(kp);
>> +	else
>> +		del_module_probe(kp);
>> +}
>> +EXPORT_SYMBOL_GPL(unregister_module_kprobe);
>> +
>> +void __kprobes unregister_module_kretprobe(struct kretprobe *rp)
>> +{
>> +	const char *module;
>> +	module = probed_module_name(&rp->kp);
>> +	if (!module)
>> +		unregister_kretprobe(rp);
>> +	else
>> +		del_module_probe(rp);
>> +}
>> +EXPORT_SYMBOL_GPL(unregister_module_kretprobe);
>> +
>> +void __kprobes unregister_module_jprobe(struct jprobe *jp)
>> +{
>> +	const char *module;
>> +	module = probed_module_name(&jp->kp);
>> +	if (!module)
>> +		unregister_jprobe(jp);
>> +	else
>> +		del_module_probe(jp);
>> +}
>> +EXPORT_SYMBOL_GPL(unregister_module_jprobe);
>> +
>> +static int module_is_probed(const char *mod, const char *sym)
>> +{
>> +	int len = strlen(mod);
>> +	return strncmp(mod, sym, len) == 0 && sym[len] == ':';
>> +}
>> +
>> +static int module_probe_callback(struct notifier_block *nb,
>> +				 unsigned long state, void *module)
>> +{
>> +	struct module_probe_client *pc;
>> +	struct module *mod = module;
>> +	if (state == MODULE_STATE_LIVE)
>> +		return NOTIFY_DONE;
>> +
>> +	mutex_lock(&module_probe_mutex);
>> +	list_for_each_entry(pc, &module_probe_list, list) {
>> +		if (!module_is_probed(mod->name, pc->module))
>> +			continue;
>> +		if (state == MODULE_STATE_COMING &&
>> +		    pc->handler(pc->data, module)) {
> 
> 
> I don't see a place where you check if pc->handler != NULL
> May be you could attach a stub in such cases.

Please see add_module_probe(), handler == NULL case returns -EINVAL,
and module_probe_list is internal list. So, pc->handler never be NULL.

Thank you,

-- 
Masami Hiramatsu

Software Engineer
Hitachi Computer Products (America) Inc.
Software Solutions Division

e-mail: mhiramat@redhat.com

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

* Re: [RFC][PATCH -tip 8/9] kprobes: support respawn probes for  module  probing
  2009-03-20  1:55 ` Frederic Weisbecker
@ 2009-03-20  5:43   ` Masami Hiramatsu
  0 siblings, 0 replies; 6+ messages in thread
From: Masami Hiramatsu @ 2009-03-20  5:43 UTC (permalink / raw)
  To: Frederic Weisbecker
  Cc: Ananth N Mavinakayanahalli, Ingo Molnar, Jim Keniston, LKML,
	systemtap-ml

Frederic Weisbecker wrote:
> On Thu, Mar 19, 2009 at 05:10:44PM -0400, Masami Hiramatsu wrote:
>> Add module_*probe API's to respawn probes on kernel modules.
>> kprobes which have been registered through register_module_*probe are
>> activated when the target module is loaded, and deactivated when unloading.
>> register_module_*probe require an activate_handler which is called right
>> before activating the probe, and if it returns !0, kprobes will be activated.
>>
>> Thus, users can check whether the target module is true target or not
>> (e.g. checking build-id) in activate_handler.
>>
>> Signed-off-by: Masami Hiramatsu <mhiramat@redhat.com>
>> Cc: Ananth N Mavinakayanahalli <ananth@in.ibm.com>
>> ---
>>  include/linux/kprobes.h |   39 ++++++++
>>  kernel/kprobes.c        |  250 +++++++++++++++++++++++++++++++++++++++++++++++
>>  2 files changed, 289 insertions(+), 0 deletions(-)
>>
>> diff --git a/include/linux/kprobes.h b/include/linux/kprobes.h
>> index 2ec6cc1..9d47d08 100644
>> --- a/include/linux/kprobes.h
>> +++ b/include/linux/kprobes.h
>> @@ -37,6 +37,7 @@
>>  #include <linux/spinlock.h>
>>  #include <linux/rcupdate.h>
>>  #include <linux/mutex.h>
>> +#include <linux/module.h>
>>
>>  #ifdef CONFIG_KPROBES
>>  #include <asm/kprobes.h>
>> @@ -69,6 +70,7 @@ typedef int (*kprobe_fault_handler_t) (struct kprobe *, struct pt_regs *,
>>  				       int trapnr);
>>  typedef int (*kretprobe_handler_t) (struct kretprobe_instance *,
>>  				    struct pt_regs *);
>> +typedef int (*probe_activate_handler_t)(void *, struct module *);
> 
> 
> I guess it could also be useful to call the handler when the module
> is unloaded. Be using another parameter with a LOAD/UNLOAD enum value.

Hmm, when unloading module, kprobes becomes "gone". So I think we don't
need to check whether it can be deactivated or not when unloading.



>>  struct kprobe {
>>  	struct hlist_node hlist;
>> @@ -279,6 +281,16 @@ void unregister_kretprobes(struct kretprobe **rps, int num);
>>  void kprobe_flush_task(struct task_struct *tk);
>>  void recycle_rp_inst(struct kretprobe_instance *ri, struct hlist_head *head);
>>
>> +int register_module_kprobe(struct kprobe *kp,
>> +			   probe_activate_handler_t handler, void *data);
>> +int register_module_kretprobe(struct kretprobe *rp,
>> +			      probe_activate_handler_t handler, void *data);
>> +int register_module_jprobe(struct jprobe *jp,
>> +			   probe_activate_handler_t handler, void *data);
>> +void unregister_module_kprobe(struct kprobe *kp);
>> +void unregister_module_kretprobe(struct kretprobe *rp);
>> +void unregister_module_jprobe(struct jprobe *jp);
>> +
>>  #else /* !CONFIG_KPROBES: */
>>
>>  static inline int kprobes_built_in(void)
>> @@ -345,5 +357,32 @@ static inline void unregister_kretprobes(struct kretprobe **rps, int num)
>>  static inline void kprobe_flush_task(struct task_struct *tk)
>>  {
>>  }
>> +static inline int register_module_kprobe(struct kprobe *kp,
>> +					 probe_activate_handler_t handler,
>> +					 void *data)
>> +{
>> +	return -ENOSYS;
>> +}
>> +static inline int register_module_kretprobe(struct kretprobe *rp,
>> +					    probe_activate_handler_t handler,
>> +					    void *data)
>> +{
>> +	return -ENOSYS;
>> +}
>> +static inline int register_module_jprobe(struct jprobe *jp,
>> +					 probe_activate_handler_t handler,
>> +					 void *data)
>> +{
>> +	return -ENOSYS;
>> +}
>> +void unregister_module_kprobe(struct kprobe *kp)
>> +{
>> +}
>> +void unregister_module_kretprobe(struct kretprobe *rp)
>> +{
>> +}
>> +void unregister_module_jprobe(struct jprobe *jp)
>> +{
>> +}
> 
> 
> Shouldn't the unregister_* be inlined too?
> I think they all could be static inlined too in case of !CONFIG_MODULE

Ah, right... it was my mistake.


>>  #endif /* CONFIG_KPROBES */
>>  #endif /* _LINUX_KPROBES_H */
>> diff --git a/kernel/kprobes.c b/kernel/kprobes.c
>> index 5016bfb..f16a54e 100644
>> --- a/kernel/kprobes.c
>> +++ b/kernel/kprobes.c
>> @@ -1416,6 +1416,256 @@ static int __kprobes debugfs_kprobe_init(void)
>>  late_initcall(debugfs_kprobe_init);
>>  #endif /* CONFIG_DEBUG_FS */
>>
>> +/* Kprobes module respawn support */
>> +enum probe_type {
>> +	PROBE_TYPE_KPROBE,
>> +	PROBE_TYPE_KRETPROBE,
>> +	PROBE_TYPE_JPROBE,
>> +};
>> +
>> +struct module_probe_client {
>> +	struct list_head list;
>> +	const char *module;	/* including symbol name */
>> +	int active;
>> +	void *data;
>> +	probe_activate_handler_t handler;
>> +	enum probe_type type;
>> +	union {
>> +		struct kprobe *kp;
>> +		struct kretprobe *rp;
>> +		struct jprobe *jp;
>> +	};
>> +};
> 
> 
> #ifdef CONFIG_MODULE ?
> 
> Frederic.

Yes.

Thank you,


-- 
Masami Hiramatsu

Software Engineer
Hitachi Computer Products (America) Inc.
Software Solutions Division

e-mail: mhiramat@redhat.com

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

* Re: [RFC][PATCH -tip 8/9] kprobes: support respawn probes for  module probing
  2009-03-20  4:41   ` Masami Hiramatsu
@ 2009-03-21  1:32     ` Frederic Weisbecker
  0 siblings, 0 replies; 6+ messages in thread
From: Frederic Weisbecker @ 2009-03-21  1:32 UTC (permalink / raw)
  To: Masami Hiramatsu
  Cc: Ananth N Mavinakayanahalli, Ingo Molnar, Jim Keniston, LKML,
	systemtap-ml

On Thu, Mar 19, 2009 at 11:48:22PM -0400, Masami Hiramatsu wrote:
> Frederic Weisbecker wrote:
> >> diff --git a/kernel/kprobes.c b/kernel/kprobes.c
> >> index 5016bfb..f16a54e 100644
> >> --- a/kernel/kprobes.c
> >> +++ b/kernel/kprobes.c
> >> @@ -1416,6 +1416,256 @@ static int __kprobes debugfs_kprobe_init(void)
> >>  late_initcall(debugfs_kprobe_init);
> >>  #endif /* CONFIG_DEBUG_FS */
> >>
> >> +/* Kprobes module respawn support */
> >> +enum probe_type {
> >> +	PROBE_TYPE_KPROBE,
> >> +	PROBE_TYPE_KRETPROBE,
> >> +	PROBE_TYPE_JPROBE,
> >> +};
> >> +
> >> +struct module_probe_client {
> >> +	struct list_head list;
> >> +	const char *module;	/* including symbol name */
> >> +	int active;
> >> +	void *data;
> >> +	probe_activate_handler_t handler;
> >> +	enum probe_type type;
> >> +	union {
> >> +		struct kprobe *kp;
> >> +		struct kretprobe *rp;
> >> +		struct jprobe *jp;
> >> +	};
> >> +};
> >> +
> >> +static DEFINE_MUTEX(module_probe_mutex);
> >> +static LIST_HEAD(module_probe_list);
> >> +
> >> +static int activate_module_probe(struct module_probe_client *pc)
> >> +{
> >> +	int ret = 0;
> >> +	if (pc->active)
> >> +		return 0;
> >> +	switch (pc->type) {
> >> +	case PROBE_TYPE_KPROBE:
> >> +		ret = register_kprobe(pc->kp);
> >> +		break;
> >> +	case PROBE_TYPE_KRETPROBE:
> >> +		ret = register_kretprobe(pc->rp);
> >> +		break;
> >> +	case PROBE_TYPE_JPROBE:
> >> +		ret = register_jprobe(pc->jp);
> >> +		break;
> >> +	default:
> >> +		WARN_ON(1);
> >> +		break;
> >> +	}
> >> +	if (!ret)
> >> +		pc->active = 1;
> >> +	return ret;
> >> +}
> >> +
> >> +static void deactivate_module_probe(struct module_probe_client *pc)
> >> +{
> >> +	if (!pc->active)
> >> +		return;
> >> +	switch (pc->type) {
> >> +	case PROBE_TYPE_KPROBE:
> >> +		unregister_kprobe(pc->kp);
> >> +		break;
> >> +	case PROBE_TYPE_KRETPROBE:
> >> +		unregister_kretprobe(pc->rp);
> >> +		break;
> >> +	case PROBE_TYPE_JPROBE:
> >> +		unregister_jprobe(pc->jp);
> >> +		break;
> >> +	default:
> >> +		WARN_ON(1);
> >> +		break;
> >> +	}
> >> +	pc->active = 0;
> >> +}
> >> +
> >> +static const char *probed_module_name(struct kprobe *kp)
> >> +{
> >> +	if ((kp->symbol_name) && strchr(kp->symbol_name, ':'))
> >> +		return kp->symbol_name;
> >> +	return NULL;
> >> +}
> >> +
> >> +static int module_is_exist(const char *module)
> >> +{
> >> +	char buf[MODULE_NAME_LEN + 8];
> >> +	snprintf(buf, MODULE_NAME_LEN + 8, "%s:__stext", module);
> >> +	return module_kallsyms_lookup_name(buf) ? 1 : 0;
> >> +}
> >> +
> >> +static int add_module_probe(const char *module, void *p, enum probe_type type,
> >> +			    probe_activate_handler_t handler, void *data)
> >> +{
> >> +	struct module_probe_client *pc;
> >> +	int ret = 0;
> >> +
> >> +	if (!handler)
> >> +		return -EINVAL;
> >> +
> >> +	pc = kzalloc(sizeof(struct module_probe_client), GFP_KERNEL);
> >> +	pc->kp = p;
> >> +	pc->type = type;
> >> +	pc->module = module;
> >> +	pc->handler = handler;
> >> +	pc->data = data;
> >> +	INIT_LIST_HEAD(&pc->list);
> >> +
> >> +	mutex_lock(&module_probe_mutex);
> >> +	if (module_is_exist(module))
> >> +		ret = activate_module_probe(pc);
> >> +	if (ret)
> >> +		kfree(pc);
> >> +	else
> >> +		list_add_tail(&pc->list, &module_probe_list);
> >> +	mutex_unlock(&module_probe_mutex);
> >> +	return ret;
> >> +}
> >> +
> >> +static void __del_module_probe(struct module_probe_client *pc)
> >> +{
> >> +	list_del(&pc->list);
> >> +	deactivate_module_probe(pc);
> >> +	kfree(pc);
> >> +}
> >> +
> >> +static int del_module_probe(void *p)
> >> +{
> >> +	struct module_probe_client *pc;
> >> +	int ret;
> >> +
> >> +	mutex_lock(&module_probe_mutex);
> >> +	list_for_each_entry(pc, &module_probe_list, list)
> >> +		if (pc->kp == p) {
> >> +			/* don't need safe loop, we exit soon */
> >> +			__del_module_probe(pc);
> >> +			goto found;
> >> +		}
> >> +	ret = -ENOENT;
> >> +found:
> >> +	mutex_unlock(&module_probe_mutex);
> >> +	return ret;
> >> +}
> >> +
> >> +int __kprobes
> >> +register_module_kprobe(struct kprobe *kp,
> >> +		       probe_activate_handler_t handler, void *data)
> >> +{
> >> +	const char *module;
> >> +	module = probed_module_name(kp);
> >> +	if (!module)
> >> +		return register_kprobe(kp);
> >> +	return add_module_probe(module, kp, PROBE_TYPE_KPROBE,
> >> +				handler, data);
> >> +}
> >> +EXPORT_SYMBOL_GPL(register_module_kprobe);
> >> +
> >> +int __kprobes
> >> +register_module_kretprobe(struct kretprobe *rp,
> >> +			  probe_activate_handler_t handler, void *data)
> >> +{
> >> +	const char *module;
> >> +	module = probed_module_name(&rp->kp);
> >> +	if (!module)
> >> +		return register_kretprobe(rp);
> >> +	return add_module_probe(module, rp, PROBE_TYPE_KRETPROBE,
> >> +				handler, data);
> >> +}
> >> +EXPORT_SYMBOL_GPL(register_module_kretprobe);
> >> +
> >> +int __kprobes
> >> +register_module_jprobe(struct jprobe *jp,
> >> +		       probe_activate_handler_t handler, void *data)
> >> +{
> >> +	const char *module;
> >> +	module = probed_module_name(&jp->kp);
> >> +	if (!module)
> >> +		return register_jprobe(jp);
> >> +	return add_module_probe(module, jp, PROBE_TYPE_JPROBE,
> >> +				handler, data);
> >> +}
> >> +EXPORT_SYMBOL_GPL(register_module_jprobe);
> >> +
> >> +void __kprobes unregister_module_kprobe(struct kprobe *kp)
> >> +{
> >> +	const char *module;
> >> +	module = probed_module_name(kp);
> >> +	if (!module)
> >> +		unregister_kprobe(kp);
> >> +	else
> >> +		del_module_probe(kp);
> >> +}
> >> +EXPORT_SYMBOL_GPL(unregister_module_kprobe);
> >> +
> >> +void __kprobes unregister_module_kretprobe(struct kretprobe *rp)
> >> +{
> >> +	const char *module;
> >> +	module = probed_module_name(&rp->kp);
> >> +	if (!module)
> >> +		unregister_kretprobe(rp);
> >> +	else
> >> +		del_module_probe(rp);
> >> +}
> >> +EXPORT_SYMBOL_GPL(unregister_module_kretprobe);
> >> +
> >> +void __kprobes unregister_module_jprobe(struct jprobe *jp)
> >> +{
> >> +	const char *module;
> >> +	module = probed_module_name(&jp->kp);
> >> +	if (!module)
> >> +		unregister_jprobe(jp);
> >> +	else
> >> +		del_module_probe(jp);
> >> +}
> >> +EXPORT_SYMBOL_GPL(unregister_module_jprobe);
> >> +
> >> +static int module_is_probed(const char *mod, const char *sym)
> >> +{
> >> +	int len = strlen(mod);
> >> +	return strncmp(mod, sym, len) == 0 && sym[len] == ':';
> >> +}
> >> +
> >> +static int module_probe_callback(struct notifier_block *nb,
> >> +				 unsigned long state, void *module)
> >> +{
> >> +	struct module_probe_client *pc;
> >> +	struct module *mod = module;
> >> +	if (state == MODULE_STATE_LIVE)
> >> +		return NOTIFY_DONE;
> >> +
> >> +	mutex_lock(&module_probe_mutex);
> >> +	list_for_each_entry(pc, &module_probe_list, list) {
> >> +		if (!module_is_probed(mod->name, pc->module))
> >> +			continue;
> >> +		if (state == MODULE_STATE_COMING &&
> >> +		    pc->handler(pc->data, module)) {
> > 
> > 
> > I don't see a place where you check if pc->handler != NULL
> > May be you could attach a stub in such cases.
> 
> Please see add_module_probe(), handler == NULL case returns -EINVAL,
> and module_probe_list is internal list. So, pc->handler never be NULL.


Ah ok, I missed it.
And indeed if the users don't care about giving a handler, they just
have to use the usual kprobe functions.

 
> Thank you,
> 
> -- 
> Masami Hiramatsu
> 
> Software Engineer
> Hitachi Computer Products (America) Inc.
> Software Solutions Division
> 
> e-mail: mhiramat@redhat.com
> 

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

end of thread, other threads:[~2009-03-21  0:08 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-03-20  0:08 [RFC][PATCH -tip 8/9] kprobes: support respawn probes for module probing Masami Hiramatsu
2009-03-20  1:55 ` Frederic Weisbecker
2009-03-20  5:43   ` Masami Hiramatsu
2009-03-20  3:03 ` Frederic Weisbecker
2009-03-20  4:41   ` Masami Hiramatsu
2009-03-21  1:32     ` Frederic Weisbecker

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).