Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

devcoredump: provide a one-way disable function

Since device/firmware coredumps can contain private data, it can
be desirable to turn them off unconditionally to be certain that
no such data will be collected by the system.

To achieve this, provide a "disabled" sysfs class attribute that
can only be changed from 0 to 1 and not back. Upon disabling,
discard existing coredumps and stop storing new ones.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Reviewed-by: Kees Cook <keescook@chromium.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Johannes Berg and committed by
Greg Kroah-Hartman
d4533329 e135303b

+48 -8
+48 -8
drivers/base/devcoredump.c
··· 31 31 #include <linux/fs.h> 32 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 39 /* if data isn't read by userspace after 5 minutes then delete it */ 35 40 #define DEVCD_TIMEOUT (HZ * 60 * 5) 36 41 ··· 126 121 &devcd_dev_group, NULL, 127 122 }; 128 123 124 + static int devcd_free(struct device *dev, void *data) 125 + { 126 + struct devcd_entry *devcd = dev_to_devcd(dev); 127 + 128 + flush_delayed_work(&devcd->del_wk); 129 + return 0; 130 + } 131 + 132 + static ssize_t disabled_show(struct class *class, struct class_attribute *attr, 133 + char *buf) 134 + { 135 + return sprintf(buf, "%d\n", devcd_disabled); 136 + } 137 + 138 + static ssize_t disabled_store(struct class *class, struct class_attribute *attr, 139 + const char *buf, size_t count) 140 + { 141 + long tmp = simple_strtol(buf, NULL, 10); 142 + 143 + /* 144 + * This essentially makes the attribute write-once, since you can't 145 + * go back to not having it disabled. This is intentional, it serves 146 + * as a system lockdown feature. 147 + */ 148 + if (tmp != 1) 149 + return -EINVAL; 150 + 151 + devcd_disabled = true; 152 + 153 + class_for_each_device(&devcd_class, NULL, NULL, devcd_free); 154 + 155 + return count; 156 + } 157 + 158 + static struct class_attribute devcd_class_attrs[] = { 159 + __ATTR_RW(disabled), 160 + __ATTR_NULL 161 + }; 162 + 129 163 static struct class devcd_class = { 130 164 .name = "devcoredump", 131 165 .owner = THIS_MODULE, 132 166 .dev_release = devcd_dev_release, 133 167 .dev_groups = devcd_dev_groups, 168 + .class_attrs = devcd_class_attrs, 134 169 }; 135 170 136 171 static ssize_t devcd_readv(char *buffer, loff_t offset, size_t count, ··· 237 192 struct devcd_entry *devcd; 238 193 struct device *existing; 239 194 195 + if (devcd_disabled) 196 + goto free; 197 + 240 198 existing = class_find_device(&devcd_class, NULL, dev, 241 199 devcd_match_failing); 242 200 if (existing) { ··· 296 248 return class_register(&devcd_class); 297 249 } 298 250 __initcall(devcoredump_init); 299 - 300 - static int devcd_free(struct device *dev, void *data) 301 - { 302 - struct devcd_entry *devcd = dev_to_devcd(dev); 303 - 304 - flush_delayed_work(&devcd->del_wk); 305 - return 0; 306 - } 307 251 308 252 static void __exit devcoredump_exit(void) 309 253 {