diff options
Diffstat (limited to 'drivers/base')
-rw-r--r-- | drivers/base/devcoredump.c | 56 |
1 files changed, 48 insertions, 8 deletions
diff --git a/drivers/base/devcoredump.c b/drivers/base/devcoredump.c index 96614b04544c..1bd120a0b084 100644 --- a/drivers/base/devcoredump.c +++ b/drivers/base/devcoredump.c | |||
@@ -31,6 +31,11 @@ | |||
31 | #include <linux/fs.h> | 31 | #include <linux/fs.h> |
32 | #include <linux/workqueue.h> | 32 | #include <linux/workqueue.h> |
33 | 33 | ||
34 | static struct class devcd_class; | ||
35 | |||
36 | /* global disable flag, for security purposes */ | ||
37 | static bool devcd_disabled; | ||
38 | |||
34 | /* if data isn't read by userspace after 5 minutes then delete it */ | 39 | /* if data isn't read by userspace after 5 minutes then delete it */ |
35 | #define DEVCD_TIMEOUT (HZ * 60 * 5) | 40 | #define DEVCD_TIMEOUT (HZ * 60 * 5) |
36 | 41 | ||
@@ -121,11 +126,51 @@ static const struct attribute_group *devcd_dev_groups[] = { | |||
121 | &devcd_dev_group, NULL, | 126 | &devcd_dev_group, NULL, |
122 | }; | 127 | }; |
123 | 128 | ||
129 | static int devcd_free(struct device *dev, void *data) | ||
130 | { | ||
131 | struct devcd_entry *devcd = dev_to_devcd(dev); | ||
132 | |||
133 | flush_delayed_work(&devcd->del_wk); | ||
134 | return 0; | ||
135 | } | ||
136 | |||
137 | static ssize_t disabled_show(struct class *class, struct class_attribute *attr, | ||
138 | char *buf) | ||
139 | { | ||
140 | return sprintf(buf, "%d\n", devcd_disabled); | ||
141 | } | ||
142 | |||
143 | static ssize_t disabled_store(struct class *class, struct class_attribute *attr, | ||
144 | const char *buf, size_t count) | ||
145 | { | ||
146 | long tmp = simple_strtol(buf, NULL, 10); | ||
147 | |||
148 | /* | ||
149 | * This essentially makes the attribute write-once, since you can't | ||
150 | * go back to not having it disabled. This is intentional, it serves | ||
151 | * as a system lockdown feature. | ||
152 | */ | ||
153 | if (tmp != 1) | ||
154 | return -EINVAL; | ||
155 | |||
156 | devcd_disabled = true; | ||
157 | |||
158 | class_for_each_device(&devcd_class, NULL, NULL, devcd_free); | ||
159 | |||
160 | return count; | ||
161 | } | ||
162 | |||
163 | static struct class_attribute devcd_class_attrs[] = { | ||
164 | __ATTR_RW(disabled), | ||
165 | __ATTR_NULL | ||
166 | }; | ||
167 | |||
124 | static struct class devcd_class = { | 168 | static struct class devcd_class = { |
125 | .name = "devcoredump", | 169 | .name = "devcoredump", |
126 | .owner = THIS_MODULE, | 170 | .owner = THIS_MODULE, |
127 | .dev_release = devcd_dev_release, | 171 | .dev_release = devcd_dev_release, |
128 | .dev_groups = devcd_dev_groups, | 172 | .dev_groups = devcd_dev_groups, |
173 | .class_attrs = devcd_class_attrs, | ||
129 | }; | 174 | }; |
130 | 175 | ||
131 | static ssize_t devcd_readv(char *buffer, loff_t offset, size_t count, | 176 | static ssize_t devcd_readv(char *buffer, loff_t offset, size_t count, |
@@ -192,6 +237,9 @@ void dev_coredumpm(struct device *dev, struct module *owner, | |||
192 | struct devcd_entry *devcd; | 237 | struct devcd_entry *devcd; |
193 | struct device *existing; | 238 | struct device *existing; |
194 | 239 | ||
240 | if (devcd_disabled) | ||
241 | goto free; | ||
242 | |||
195 | existing = class_find_device(&devcd_class, NULL, dev, | 243 | existing = class_find_device(&devcd_class, NULL, dev, |
196 | devcd_match_failing); | 244 | devcd_match_failing); |
197 | if (existing) { | 245 | if (existing) { |
@@ -249,14 +297,6 @@ static int __init devcoredump_init(void) | |||
249 | } | 297 | } |
250 | __initcall(devcoredump_init); | 298 | __initcall(devcoredump_init); |
251 | 299 | ||
252 | static int devcd_free(struct device *dev, void *data) | ||
253 | { | ||
254 | struct devcd_entry *devcd = dev_to_devcd(dev); | ||
255 | |||
256 | flush_delayed_work(&devcd->del_wk); | ||
257 | return 0; | ||
258 | } | ||
259 | |||
260 | static void __exit devcoredump_exit(void) | 300 | static void __exit devcoredump_exit(void) |
261 | { | 301 | { |
262 | class_for_each_device(&devcd_class, NULL, NULL, devcd_free); | 302 | class_for_each_device(&devcd_class, NULL, NULL, devcd_free); |