Commit f4f154fd authored by Akinobu Mita's avatar Akinobu Mita Committed by Linus Torvalds
Browse files

[PATCH] fault injection: process filtering for fault-injection capabilities



This patch provides process filtering feature.
The process filter allows failing only permitted processes
by /proc/<pid>/make-it-fail

Please see the example that demostrates how to inject slab allocation
failures into module init/cleanup code
in Documentation/fault-injection/fault-injection.txt

Signed-off-by: default avatarAkinobu Mita <akinobu.mita@gmail.com>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent c17bb495
Loading
Loading
Loading
Loading
+65 −0
Original line number Diff line number Diff line
@@ -853,6 +853,65 @@ static struct file_operations proc_seccomp_operations = {
};
#endif /* CONFIG_SECCOMP */

#ifdef CONFIG_FAULT_INJECTION
static ssize_t proc_fault_inject_read(struct file * file, char __user * buf,
				      size_t count, loff_t *ppos)
{
	struct task_struct *task = get_proc_task(file->f_dentry->d_inode);
	char buffer[PROC_NUMBUF];
	size_t len;
	int make_it_fail;
	loff_t __ppos = *ppos;

	if (!task)
		return -ESRCH;
	make_it_fail = task->make_it_fail;
	put_task_struct(task);

	len = snprintf(buffer, sizeof(buffer), "%i\n", make_it_fail);
	if (__ppos >= len)
		return 0;
	if (count > len-__ppos)
		count = len-__ppos;
	if (copy_to_user(buf, buffer + __ppos, count))
		return -EFAULT;
	*ppos = __ppos + count;
	return count;
}

static ssize_t proc_fault_inject_write(struct file * file,
			const char __user * buf, size_t count, loff_t *ppos)
{
	struct task_struct *task;
	char buffer[PROC_NUMBUF], *end;
	int make_it_fail;

	if (!capable(CAP_SYS_RESOURCE))
		return -EPERM;
	memset(buffer, 0, sizeof(buffer));
	if (count > sizeof(buffer) - 1)
		count = sizeof(buffer) - 1;
	if (copy_from_user(buffer, buf, count))
		return -EFAULT;
	make_it_fail = simple_strtol(buffer, &end, 0);
	if (*end == '\n')
		end++;
	task = get_proc_task(file->f_dentry->d_inode);
	if (!task)
		return -ESRCH;
	task->make_it_fail = make_it_fail;
	put_task_struct(task);
	if (end - buffer == 0)
		return -EIO;
	return end - buffer;
}

static struct file_operations proc_fault_inject_operations = {
	.read		= proc_fault_inject_read,
	.write		= proc_fault_inject_write,
};
#endif

static void *proc_pid_follow_link(struct dentry *dentry, struct nameidata *nd)
{
	struct inode *inode = dentry->d_inode;
@@ -1793,6 +1852,9 @@ static struct pid_entry tgid_base_stuff[] = {
#ifdef CONFIG_AUDITSYSCALL
	REG("loginuid",   S_IWUSR|S_IRUGO, loginuid),
#endif
#ifdef CONFIG_FAULT_INJECTION
	REG("make-it-fail", S_IRUGO|S_IWUSR, fault_inject),
#endif
};

static int proc_tgid_base_readdir(struct file * filp,
@@ -2068,6 +2130,9 @@ static struct pid_entry tid_base_stuff[] = {
#ifdef CONFIG_AUDITSYSCALL
	REG("loginuid",  S_IWUSR|S_IRUGO, loginuid),
#endif
#ifdef CONFIG_FAULT_INJECTION
	REG("make-it-fail", S_IRUGO|S_IWUSR, fault_inject),
#endif
};

static int proc_tid_base_readdir(struct file * filp,
+2 −0
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@ struct fault_attr {
	atomic_t times;
	atomic_t space;
	unsigned long verbose;
	u32 task_filter;

	unsigned long count;

@@ -30,6 +31,7 @@ struct fault_attr {
		struct dentry *times_file;
		struct dentry *space_file;
		struct dentry *verbose_file;
		struct dentry *task_filter_file;
	} dentries;

#endif
+3 −0
Original line number Diff line number Diff line
@@ -1045,6 +1045,9 @@ struct task_struct {
#ifdef	CONFIG_TASK_DELAY_ACCT
	struct task_delay_info *delays;
#endif
#ifdef CONFIG_FAULT_INJECTION
	int make_it_fail;
#endif
};

static inline pid_t process_group(struct task_struct *tsk)
+16 −1
Original line number Diff line number Diff line
@@ -5,6 +5,7 @@
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/fault-inject.h>

/*
@@ -44,6 +45,11 @@ static void fail_dump(struct fault_attr *attr)

#define atomic_dec_not_zero(v)		atomic_add_unless((v), -1, 0)

static int fail_task(struct fault_attr *attr, struct task_struct *task)
{
	return !in_interrupt() && task->make_it_fail;
}

/*
 * This code is stolen from failmalloc-1.0
 * http://www.nongnu.org/failmalloc/
@@ -51,6 +57,9 @@ static void fail_dump(struct fault_attr *attr)

int should_fail(struct fault_attr *attr, ssize_t size)
{
	if (attr->task_filter && !fail_task(attr, current))
		return 0;

	if (atomic_read(&attr->times) == 0)
		return 0;

@@ -135,6 +144,9 @@ void cleanup_fault_attr_dentries(struct fault_attr *attr)
	debugfs_remove(attr->dentries.verbose_file);
	attr->dentries.verbose_file = NULL;

	debugfs_remove(attr->dentries.task_filter_file);
	attr->dentries.task_filter_file = NULL;

	if (attr->dentries.dir)
		WARN_ON(!simple_empty(attr->dentries.dir));

@@ -169,9 +181,12 @@ int init_fault_attr_dentries(struct fault_attr *attr, const char *name)
	attr->dentries.verbose_file =
		debugfs_create_ul("verbose", mode, dir, &attr->verbose);

	attr->dentries.task_filter_file = debugfs_create_bool("task-filter",
						mode, dir, &attr->task_filter);

	if (!attr->dentries.probability_file || !attr->dentries.interval_file
	    || !attr->dentries.times_file || !attr->dentries.space_file
	    || !attr->dentries.verbose_file)
	    || !attr->dentries.verbose_file || !attr->dentries.task_filter_file)
		goto fail;

	return 0;