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

[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
...@@ -853,6 +853,65 @@ static struct file_operations proc_seccomp_operations = { ...@@ -853,6 +853,65 @@ static struct file_operations proc_seccomp_operations = {
}; };
#endif /* CONFIG_SECCOMP */ #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) static void *proc_pid_follow_link(struct dentry *dentry, struct nameidata *nd)
{ {
struct inode *inode = dentry->d_inode; struct inode *inode = dentry->d_inode;
...@@ -1793,6 +1852,9 @@ static struct pid_entry tgid_base_stuff[] = { ...@@ -1793,6 +1852,9 @@ static struct pid_entry tgid_base_stuff[] = {
#ifdef CONFIG_AUDITSYSCALL #ifdef CONFIG_AUDITSYSCALL
REG("loginuid", S_IWUSR|S_IRUGO, loginuid), REG("loginuid", S_IWUSR|S_IRUGO, loginuid),
#endif #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, static int proc_tgid_base_readdir(struct file * filp,
...@@ -2068,6 +2130,9 @@ static struct pid_entry tid_base_stuff[] = { ...@@ -2068,6 +2130,9 @@ static struct pid_entry tid_base_stuff[] = {
#ifdef CONFIG_AUDITSYSCALL #ifdef CONFIG_AUDITSYSCALL
REG("loginuid", S_IWUSR|S_IRUGO, loginuid), REG("loginuid", S_IWUSR|S_IRUGO, loginuid),
#endif #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, static int proc_tid_base_readdir(struct file * filp,
......
...@@ -17,6 +17,7 @@ struct fault_attr { ...@@ -17,6 +17,7 @@ struct fault_attr {
atomic_t times; atomic_t times;
atomic_t space; atomic_t space;
unsigned long verbose; unsigned long verbose;
u32 task_filter;
unsigned long count; unsigned long count;
...@@ -30,6 +31,7 @@ struct fault_attr { ...@@ -30,6 +31,7 @@ struct fault_attr {
struct dentry *times_file; struct dentry *times_file;
struct dentry *space_file; struct dentry *space_file;
struct dentry *verbose_file; struct dentry *verbose_file;
struct dentry *task_filter_file;
} dentries; } dentries;
#endif #endif
......
...@@ -1045,6 +1045,9 @@ struct task_struct { ...@@ -1045,6 +1045,9 @@ struct task_struct {
#ifdef CONFIG_TASK_DELAY_ACCT #ifdef CONFIG_TASK_DELAY_ACCT
struct task_delay_info *delays; struct task_delay_info *delays;
#endif #endif
#ifdef CONFIG_FAULT_INJECTION
int make_it_fail;
#endif
}; };
static inline pid_t process_group(struct task_struct *tsk) static inline pid_t process_group(struct task_struct *tsk)
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#include <linux/types.h> #include <linux/types.h>
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/fault-inject.h> #include <linux/fault-inject.h>
/* /*
...@@ -44,6 +45,11 @@ static void fail_dump(struct fault_attr *attr) ...@@ -44,6 +45,11 @@ static void fail_dump(struct fault_attr *attr)
#define atomic_dec_not_zero(v) atomic_add_unless((v), -1, 0) #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 * This code is stolen from failmalloc-1.0
* http://www.nongnu.org/failmalloc/ * http://www.nongnu.org/failmalloc/
...@@ -51,6 +57,9 @@ static void fail_dump(struct fault_attr *attr) ...@@ -51,6 +57,9 @@ static void fail_dump(struct fault_attr *attr)
int should_fail(struct fault_attr *attr, ssize_t size) 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) if (atomic_read(&attr->times) == 0)
return 0; return 0;
...@@ -135,6 +144,9 @@ void cleanup_fault_attr_dentries(struct fault_attr *attr) ...@@ -135,6 +144,9 @@ void cleanup_fault_attr_dentries(struct fault_attr *attr)
debugfs_remove(attr->dentries.verbose_file); debugfs_remove(attr->dentries.verbose_file);
attr->dentries.verbose_file = NULL; attr->dentries.verbose_file = NULL;
debugfs_remove(attr->dentries.task_filter_file);
attr->dentries.task_filter_file = NULL;
if (attr->dentries.dir) if (attr->dentries.dir)
WARN_ON(!simple_empty(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) ...@@ -169,9 +181,12 @@ int init_fault_attr_dentries(struct fault_attr *attr, const char *name)
attr->dentries.verbose_file = attr->dentries.verbose_file =
debugfs_create_ul("verbose", mode, dir, &attr->verbose); 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 if (!attr->dentries.probability_file || !attr->dentries.interval_file
|| !attr->dentries.times_file || !attr->dentries.space_file || !attr->dentries.times_file || !attr->dentries.space_file
|| !attr->dentries.verbose_file) || !attr->dentries.verbose_file || !attr->dentries.task_filter_file)
goto fail; goto fail;
return 0; return 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment