Skip to contentSkip to conversation

kprobes sample driver

PATCH In review Rusty Lynch 2 participants 2 messages Nov 13, 2002 Char and misc drivers

lore
Share & tools
lore
#0 lore raw

PATCH 1/1 kprobes sample driver

This is a sample kprobes module that implements a simple misc char device that can cause arbitrary text to be printk'ed when arbitrary kernel addresses are executed.

This patch was created on top of a 2.5.47 kernel with the kprobes patch applied.

I created this driver just to learn what kprobes was all about. I am sending this to the mailing list for other people that are curious about kprobes (and also to have people who know more about kprobes show me where my understanding is flawed.)

To use the device, create a misc char node with a minor of 221, and then write strings of the format "0xADDRESS MESSAGE" to the device. When the file is closed all kprobes are unregistered, so I usually test by doing:

$ mknod /dev/noisy c 10 221
$ cat > /dev/noisy
0xc0107d50 sys_fork

.. and then go do something that will trigger a sys_fork.

-rustyl

drivers/char/Kconfig +15 -0
diff -urN linux-2.5.47-kprobes/drivers/char/Kconfig linux-2.5.47-patched/drivers/char/Kconfig
@@ -1270,5 +1270,20 @@
 	  Once bound, I/O against /dev/raw/rawN uses efficient zero-copy I/O. 
 	  See the raw(8) manpage for more details.
 
+config NOISY
+	tristate "Noisy Interface Support"
+	---help---
+	  If you say Y here and create a character special file /dev/noisy with
+	  major number 10 and minor number 221 using mknod ("man mknod"), you
+	  will get access to an interface for inserting arbitrary printk's
+	  into executing kernel code.
+
+	  This driver is also available as a module ( = code which can be
+	  inserted in and removed from the running kernel whenever you want).
+	  The module is called noisy.o. If you want to compile it as a module,
+	  say M here and read <file:Documentation/modules.txt>.
+
+	  If unsure, say N.
+
 endmenu
 
drivers/char/Makefile +1 -0
diff -urN linux-2.5.47-kprobes/drivers/char/Makefile linux-2.5.47-patched/drivers/char/Makefile
@@ -102,6 +102,7 @@
 obj-$(CONFIG_AGP) += agp/
 obj-$(CONFIG_DRM) += drm/
 obj-$(CONFIG_PCMCIA) += pcmcia/
+obj-$(CONFIG_NOISY) += noisy.o
 
 # Files generated that shall be removed upon make clean
 clean-files := consolemap_deftbl.c defkeymap.c qtronixmap.c
drivers/char/noisy.c +259 -0

259 lines not on this page · raw message

include/linux/miscdevice.h +1 -0
diff -urN linux-2.5.47-kprobes/include/linux/miscdevice.h linux-2.5.47-patched/include/linux/miscdevice.h
@@ -23,6 +23,7 @@
 #define MICROCODE_MINOR		184
 #define MWAVE_MINOR	219		/* ACP/Mwave Modem */
 #define MPT_MINOR	220
+#define NOISY_MINOR 221
 #define MISC_DYNAMIC_MINOR 255
 
 #define SGI_GRAPHICS_MINOR   146
#1 lore raw

Hi,

This is very nice. In fact, I will probably start using this for testing kprobes myself. I have a few comments, given below inline.

Thank you,
Vamsi.

PS: I am cc'ing dprobes mailing list for folks who hang out there to have a chance to take a look at this and may be comment/use. Hope you don't mind.

Signature · 5 lines
Vamsi Krishna S.
Linux Technology Center,
IBM Software Lab, Bangalore.
Ph: +91 80 5044959
Internet: [email protected]
Quoted 4 lines · Rusty Lynch

This is a sample kprobes module that implements a simple misc char device that can cause arbitrary text to be printk'ed when arbitrary kernel addresses are executed.

Nice idea.

drivers/char/noisy.c line 28

Quoted 7 lines
drivers/char/noisy.c +7 -0
+static struct list_head probe_list;
+struct nprobe {
+	struct list_head list;
+	struct kprobe probe;
+	char message[MAX_MSG_SIZE + 1];
+};
+

Good. This is how I meant struct kprobe to be used: as a part of a bigger structure that the caller uses to manage probes.

drivers/char/noisy.c line 43

Quoted 12 lines
drivers/char/noisy.c +12 -0
+static void noisy_pre_handler(struct kprobe *p, struct pt_regs *r)
+{
+	struct list_head *tmp;
+
+	printk(KERN_CRIT "noisy: noisy_prehandler\n");
+	list_for_each(tmp, &probe_list) {
+		struct nprobe *c = list_entry(tmp, struct nprobe, list);
+		if (&(c->probe) == p) {
+			printk(KERN_CRIT "%s\n", c->message);
+		}
+	}
+}

Actually, you can do this in a much easier way without having to loop through all probes. All you need is:

static void noisy_pre_handler(struct kprobe *p, struct pt_regs *r)

{
	struct nprobe *c = container_of(p, struct nprobe, probe);
	printk(KERN_CRIT "%s: %s\n", __FUNCTION__, c->message);
}

drivers/char/noisy.c line 100

Quoted 8 lines
drivers/char/noisy.c +8 -0
+static ssize_t noisy_read(struct file *file, char *buf,
+			size_t count, loff_t *ppos)
+{	
+	struct list_head *tmp;
+
+	printk(KERN_CRIT "noisy: noisy_read\n");	
+	list_for_each(tmp, &probe_list) {
+		struct nprobe *p = list_entry(tmp, struct nprobe, list);

You could have used list_for_each_entry as:

	struct nprobe *p;

	list_for_each_entry(p, &probe_list, list) {
		...
	}
Quoted 7 lines
drivers/char/noisy.c +7 -0
+static ssize_t noisy_write(struct file *file, const char *buf, size_t count, 
+			   loff_t *ppos)
+{
+	struct nprobe *n = 0;
+	size_t ret = -ENOMEM;
+	char *tmp = 0;
+

drivers/char/noisy.c line 144

Quoted 10 lines
drivers/char/noisy.c +10 -0
+	printk(KERN_CRIT "noisy: noisy_write\n");
+	if (count > MAX_MSG_SIZE) {
+		printk(KERN_CRIT 
+		       "noisy: Input buffer (%i bytes) is too big!\n",
+		       count);
+		ret = -EINVAL;
+		goto out;
+	}
+
+	tmp = (char *)kmalloc(count + 1, GFP_KERNEL);

Don't bother casting the return values from kmalloc. It is not needed. Same for all other kmalloc calls here.

Quoted 9 lines
drivers/char/noisy.c +9 -0
+	if (!tmp) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	n = (struct nprobe *)kmalloc(sizeof(struct nprobe), GFP_KERNEL);
+	if (!n) {
+		ret = -ENOMEM;
+		goto out;

drivers/char/noisy.c line 163

Quoted 10 lines
drivers/char/noisy.c +10 -0
+	}
+	memset(n, '\0', sizeof(struct nprobe));
+	
+	if (copy_from_user((void *)tmp, (void *)buf, count)) {
+		ret = -ENOMEM;
+		goto out;
+	} 
+	tmp[count] = '\0';
+
+	n = (struct nprobe *)kmalloc(sizeof(struct nprobe), GFP_KERNEL);

This is a duplicate call, kill it. You have already alloc'ed n above.

Quoted 19 lines
drivers/char/noisy.c +19 -0
+	if (!n) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	if (2 != sscanf(tmp, "0x%x %s", &(n->probe).addr, n->message)) {
+		ret = -EINVAL;
+		goto out;
+	}
+	(n->probe).pre_handler = noisy_pre_handler;
+	(n->probe).post_handler = noisy_post_handler;
+	(n->probe).fault_handler = noisy_fault_handler;
+
+	{
+		/* 
+		 * I am attempting to verify that the kernel-mode address
+		 * passed in is valid, but I suspect this is not the
+		 * right way of doing this.
+		 *

drivers/char/noisy.c line 192

Quoted 10 lines
drivers/char/noisy.c +10 -0
+		 * Although, it appears to work.  I can attempt to setup 
+		 * a probe for 0xfffffff0, and the write operation fails with 
+		 * -EINVAL. 
+		 */
+		unsigned short eip;
+		if (__get_user(eip, (unsigned short *)(n->probe).addr)) {
+			ret = -EINVAL;
+			goto out;
+		}
+	}

This is not quite right. I will reply to your other post on how to improve this check.

drivers/char/noisy.c line 198

Quoted 6 lines
drivers/char/noisy.c +6 -0
+
+	if (register_kprobe(&(n->probe))) {
+		printk(KERN_CRIT "Unable to register probe at %p\n", 
+		       (n->probe).addr);
+		if (n)
+			kfree(n);

kfree(NULL) is valid. No need for if (n). Same comment for kfree(tmp);