diff options
author | Akinobu Mita <akinobu.mita@gmail.com> | 2006-12-08 05:39:47 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.osdl.org> | 2006-12-08 11:29:02 -0500 |
commit | f4f154fd920b2178382a6a24a236348e4429ebc1 (patch) | |
tree | 0bba747eb50b5d7e18d2b828f8c707b2781d7544 | |
parent | c17bb4951752d3e0f49cd1ea9d2e868422f9e0d6 (diff) |
[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: Akinobu Mita <akinobu.mita@gmail.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r-- | fs/proc/base.c | 65 | ||||
-rw-r--r-- | include/linux/fault-inject.h | 2 | ||||
-rw-r--r-- | include/linux/sched.h | 3 | ||||
-rw-r--r-- | lib/fault-inject.c | 17 |
4 files changed, 86 insertions, 1 deletions
diff --git a/fs/proc/base.c b/fs/proc/base.c index a3b5074118a7..fd959d5b5a80 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c | |||
@@ -853,6 +853,65 @@ static struct file_operations proc_seccomp_operations = { | |||
853 | }; | 853 | }; |
854 | #endif /* CONFIG_SECCOMP */ | 854 | #endif /* CONFIG_SECCOMP */ |
855 | 855 | ||
856 | #ifdef CONFIG_FAULT_INJECTION | ||
857 | static ssize_t proc_fault_inject_read(struct file * file, char __user * buf, | ||
858 | size_t count, loff_t *ppos) | ||
859 | { | ||
860 | struct task_struct *task = get_proc_task(file->f_dentry->d_inode); | ||
861 | char buffer[PROC_NUMBUF]; | ||
862 | size_t len; | ||
863 | int make_it_fail; | ||
864 | loff_t __ppos = *ppos; | ||
865 | |||
866 | if (!task) | ||
867 | return -ESRCH; | ||
868 | make_it_fail = task->make_it_fail; | ||
869 | put_task_struct(task); | ||
870 | |||
871 | len = snprintf(buffer, sizeof(buffer), "%i\n", make_it_fail); | ||
872 | if (__ppos >= len) | ||
873 | return 0; | ||
874 | if (count > len-__ppos) | ||
875 | count = len-__ppos; | ||
876 | if (copy_to_user(buf, buffer + __ppos, count)) | ||
877 | return -EFAULT; | ||
878 | *ppos = __ppos + count; | ||
879 | return count; | ||
880 | } | ||
881 | |||
882 | static ssize_t proc_fault_inject_write(struct file * file, | ||
883 | const char __user * buf, size_t count, loff_t *ppos) | ||
884 | { | ||
885 | struct task_struct *task; | ||
886 | char buffer[PROC_NUMBUF], *end; | ||
887 | int make_it_fail; | ||
888 | |||
889 | if (!capable(CAP_SYS_RESOURCE)) | ||
890 | return -EPERM; | ||
891 | memset(buffer, 0, sizeof(buffer)); | ||
892 | if (count > sizeof(buffer) - 1) | ||
893 | count = sizeof(buffer) - 1; | ||
894 | if (copy_from_user(buffer, buf, count)) | ||
895 | return -EFAULT; | ||
896 | make_it_fail = simple_strtol(buffer, &end, 0); | ||
897 | if (*end == '\n') | ||
898 | end++; | ||
899 | task = get_proc_task(file->f_dentry->d_inode); | ||
900 | if (!task) | ||
901 | return -ESRCH; | ||
902 | task->make_it_fail = make_it_fail; | ||
903 | put_task_struct(task); | ||
904 | if (end - buffer == 0) | ||
905 | return -EIO; | ||
906 | return end - buffer; | ||
907 | } | ||
908 | |||
909 | static struct file_operations proc_fault_inject_operations = { | ||
910 | .read = proc_fault_inject_read, | ||
911 | .write = proc_fault_inject_write, | ||
912 | }; | ||
913 | #endif | ||
914 | |||
856 | static void *proc_pid_follow_link(struct dentry *dentry, struct nameidata *nd) | 915 | static void *proc_pid_follow_link(struct dentry *dentry, struct nameidata *nd) |
857 | { | 916 | { |
858 | struct inode *inode = dentry->d_inode; | 917 | struct inode *inode = dentry->d_inode; |
@@ -1793,6 +1852,9 @@ static struct pid_entry tgid_base_stuff[] = { | |||
1793 | #ifdef CONFIG_AUDITSYSCALL | 1852 | #ifdef CONFIG_AUDITSYSCALL |
1794 | REG("loginuid", S_IWUSR|S_IRUGO, loginuid), | 1853 | REG("loginuid", S_IWUSR|S_IRUGO, loginuid), |
1795 | #endif | 1854 | #endif |
1855 | #ifdef CONFIG_FAULT_INJECTION | ||
1856 | REG("make-it-fail", S_IRUGO|S_IWUSR, fault_inject), | ||
1857 | #endif | ||
1796 | }; | 1858 | }; |
1797 | 1859 | ||
1798 | static int proc_tgid_base_readdir(struct file * filp, | 1860 | static int proc_tgid_base_readdir(struct file * filp, |
@@ -2068,6 +2130,9 @@ static struct pid_entry tid_base_stuff[] = { | |||
2068 | #ifdef CONFIG_AUDITSYSCALL | 2130 | #ifdef CONFIG_AUDITSYSCALL |
2069 | REG("loginuid", S_IWUSR|S_IRUGO, loginuid), | 2131 | REG("loginuid", S_IWUSR|S_IRUGO, loginuid), |
2070 | #endif | 2132 | #endif |
2133 | #ifdef CONFIG_FAULT_INJECTION | ||
2134 | REG("make-it-fail", S_IRUGO|S_IWUSR, fault_inject), | ||
2135 | #endif | ||
2071 | }; | 2136 | }; |
2072 | 2137 | ||
2073 | static int proc_tid_base_readdir(struct file * filp, | 2138 | static int proc_tid_base_readdir(struct file * filp, |
diff --git a/include/linux/fault-inject.h b/include/linux/fault-inject.h index 4df4902bc8d8..a525f9b9f015 100644 --- a/include/linux/fault-inject.h +++ b/include/linux/fault-inject.h | |||
@@ -17,6 +17,7 @@ struct fault_attr { | |||
17 | atomic_t times; | 17 | atomic_t times; |
18 | atomic_t space; | 18 | atomic_t space; |
19 | unsigned long verbose; | 19 | unsigned long verbose; |
20 | u32 task_filter; | ||
20 | 21 | ||
21 | unsigned long count; | 22 | unsigned long count; |
22 | 23 | ||
@@ -30,6 +31,7 @@ struct fault_attr { | |||
30 | struct dentry *times_file; | 31 | struct dentry *times_file; |
31 | struct dentry *space_file; | 32 | struct dentry *space_file; |
32 | struct dentry *verbose_file; | 33 | struct dentry *verbose_file; |
34 | struct dentry *task_filter_file; | ||
33 | } dentries; | 35 | } dentries; |
34 | 36 | ||
35 | #endif | 37 | #endif |
diff --git a/include/linux/sched.h b/include/linux/sched.h index f0317edea141..ad9c46071ff8 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h | |||
@@ -1045,6 +1045,9 @@ struct task_struct { | |||
1045 | #ifdef CONFIG_TASK_DELAY_ACCT | 1045 | #ifdef CONFIG_TASK_DELAY_ACCT |
1046 | struct task_delay_info *delays; | 1046 | struct task_delay_info *delays; |
1047 | #endif | 1047 | #endif |
1048 | #ifdef CONFIG_FAULT_INJECTION | ||
1049 | int make_it_fail; | ||
1050 | #endif | ||
1048 | }; | 1051 | }; |
1049 | 1052 | ||
1050 | static inline pid_t process_group(struct task_struct *tsk) | 1053 | static inline pid_t process_group(struct task_struct *tsk) |
diff --git a/lib/fault-inject.c b/lib/fault-inject.c index a7cb3afd132a..03468609d701 100644 --- a/lib/fault-inject.c +++ b/lib/fault-inject.c | |||
@@ -5,6 +5,7 @@ | |||
5 | #include <linux/types.h> | 5 | #include <linux/types.h> |
6 | #include <linux/fs.h> | 6 | #include <linux/fs.h> |
7 | #include <linux/module.h> | 7 | #include <linux/module.h> |
8 | #include <linux/interrupt.h> | ||
8 | #include <linux/fault-inject.h> | 9 | #include <linux/fault-inject.h> |
9 | 10 | ||
10 | /* | 11 | /* |
@@ -44,6 +45,11 @@ static void fail_dump(struct fault_attr *attr) | |||
44 | 45 | ||
45 | #define atomic_dec_not_zero(v) atomic_add_unless((v), -1, 0) | 46 | #define atomic_dec_not_zero(v) atomic_add_unless((v), -1, 0) |
46 | 47 | ||
48 | static int fail_task(struct fault_attr *attr, struct task_struct *task) | ||
49 | { | ||
50 | return !in_interrupt() && task->make_it_fail; | ||
51 | } | ||
52 | |||
47 | /* | 53 | /* |
48 | * This code is stolen from failmalloc-1.0 | 54 | * This code is stolen from failmalloc-1.0 |
49 | * http://www.nongnu.org/failmalloc/ | 55 | * http://www.nongnu.org/failmalloc/ |
@@ -51,6 +57,9 @@ static void fail_dump(struct fault_attr *attr) | |||
51 | 57 | ||
52 | int should_fail(struct fault_attr *attr, ssize_t size) | 58 | int should_fail(struct fault_attr *attr, ssize_t size) |
53 | { | 59 | { |
60 | if (attr->task_filter && !fail_task(attr, current)) | ||
61 | return 0; | ||
62 | |||
54 | if (atomic_read(&attr->times) == 0) | 63 | if (atomic_read(&attr->times) == 0) |
55 | return 0; | 64 | return 0; |
56 | 65 | ||
@@ -135,6 +144,9 @@ void cleanup_fault_attr_dentries(struct fault_attr *attr) | |||
135 | debugfs_remove(attr->dentries.verbose_file); | 144 | debugfs_remove(attr->dentries.verbose_file); |
136 | attr->dentries.verbose_file = NULL; | 145 | attr->dentries.verbose_file = NULL; |
137 | 146 | ||
147 | debugfs_remove(attr->dentries.task_filter_file); | ||
148 | attr->dentries.task_filter_file = NULL; | ||
149 | |||
138 | if (attr->dentries.dir) | 150 | if (attr->dentries.dir) |
139 | WARN_ON(!simple_empty(attr->dentries.dir)); | 151 | WARN_ON(!simple_empty(attr->dentries.dir)); |
140 | 152 | ||
@@ -169,9 +181,12 @@ int init_fault_attr_dentries(struct fault_attr *attr, const char *name) | |||
169 | attr->dentries.verbose_file = | 181 | attr->dentries.verbose_file = |
170 | debugfs_create_ul("verbose", mode, dir, &attr->verbose); | 182 | debugfs_create_ul("verbose", mode, dir, &attr->verbose); |
171 | 183 | ||
184 | attr->dentries.task_filter_file = debugfs_create_bool("task-filter", | ||
185 | mode, dir, &attr->task_filter); | ||
186 | |||
172 | if (!attr->dentries.probability_file || !attr->dentries.interval_file | 187 | if (!attr->dentries.probability_file || !attr->dentries.interval_file |
173 | || !attr->dentries.times_file || !attr->dentries.space_file | 188 | || !attr->dentries.times_file || !attr->dentries.space_file |
174 | || !attr->dentries.verbose_file) | 189 | || !attr->dentries.verbose_file || !attr->dentries.task_filter_file) |
175 | goto fail; | 190 | goto fail; |
176 | 191 | ||
177 | return 0; | 192 | return 0; |