summaryrefslogtreecommitdiffstats
path: root/drivers/base/devcoredump.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/base/devcoredump.c')
-rw-r--r--drivers/base/devcoredump.c56
1 files changed, 48 insertions, 8 deletions
diff --git a/drivers/base/devcoredump.c b/drivers/base/devcoredump.c
index 96614b04544..1bd120a0b08 100644
--- a/drivers/base/devcoredump.c
+++ b/drivers/base/devcoredump.c
@@ -31,6 +31,11 @@
#include <linux/fs.h>
#include <linux/workqueue.h>
+static struct class devcd_class;
+
+/* global disable flag, for security purposes */
+static bool devcd_disabled;
+
/* if data isn't read by userspace after 5 minutes then delete it */
#define DEVCD_TIMEOUT (HZ * 60 * 5)
@@ -121,11 +126,51 @@ static const struct attribute_group *devcd_dev_groups[] = {
&devcd_dev_group, NULL,
};
+static int devcd_free(struct device *dev, void *data)
+{
+ struct devcd_entry *devcd = dev_to_devcd(dev);
+
+ flush_delayed_work(&devcd->del_wk);
+ return 0;
+}
+
+static ssize_t disabled_show(struct class *class, struct class_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "%d\n", devcd_disabled);
+}
+
+static ssize_t disabled_store(struct class *class, struct class_attribute *attr,
+ const char *buf, size_t count)
+{
+ long tmp = simple_strtol(buf, NULL, 10);
+
+ /*
+ * This essentially makes the attribute write-once, since you can't
+ * go back to not having it disabled. This is intentional, it serves
+ * as a system lockdown feature.
+ */
+ if (tmp != 1)
+ return -EINVAL;
+
+ devcd_disabled = true;
+
+ class_for_each_device(&devcd_class, NULL, NULL, devcd_free);
+
+ return count;
+}
+
+static struct class_attribute devcd_class_attrs[] = {
+ __ATTR_RW(disabled),
+ __ATTR_NULL
+};
+
static struct class devcd_class = {
.name = "devcoredump",
.owner = THIS_MODULE,
.dev_release = devcd_dev_release,
.dev_groups = devcd_dev_groups,
+ .class_attrs = devcd_class_attrs,
};
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,
struct devcd_entry *devcd;
struct device *existing;
+ if (devcd_disabled)
+ goto free;
+
existing = class_find_device(&devcd_class, NULL, dev,
devcd_match_failing);
if (existing) {
@@ -249,14 +297,6 @@ static int __init devcoredump_init(void)
}
__initcall(devcoredump_init);
-static int devcd_free(struct device *dev, void *data)
-{
- struct devcd_entry *devcd = dev_to_devcd(dev);
-
- flush_delayed_work(&devcd->del_wk);
- return 0;
-}
-
static void __exit devcoredump_exit(void)
{
class_for_each_device(&devcd_class, NULL, NULL, devcd_free);