diff options
author | Thomas Renninger <trenn@suse.de> | 2011-05-26 06:26:24 -0400 |
---|---|---|
committer | Len Brown <len.brown@intel.com> | 2011-05-29 01:50:40 -0400 |
commit | 526b4af47f44148c9d665e57723ed9f86634c6e3 (patch) | |
tree | 7906a3b45703aa730d5e7ebadc36a55aee5417ca | |
parent | aecad432fd68dafa5b3b497c4816fbfce6fd4066 (diff) |
ACPI: Split out custom_method functionality into an own driver
With /sys/kernel/debug/acpi/custom_method root can write
to arbitrary memory and increase his priveleges, even if
these are restricted.
-> Make this an own debug .config option and warn about the
security issue in the config description.
-> Still keep acpi/debugfs.c which now only creates an empty
/sys/kernel/debug/acpi directory. There might be other
users of it later.
Signed-off-by: Thomas Renninger <trenn@suse.de>
Acked-by: Rafael J. Wysocki <rjw@sisk.pl>
Acked-by: rui.zhang@intel.com
Signed-off-by: Len Brown <len.brown@intel.com>
-rw-r--r-- | Documentation/acpi/method-customizing.txt | 5 | ||||
-rw-r--r-- | drivers/acpi/Kconfig | 15 | ||||
-rw-r--r-- | drivers/acpi/Makefile | 1 | ||||
-rw-r--r-- | drivers/acpi/custom_method.c | 100 | ||||
-rw-r--r-- | drivers/acpi/debugfs.c | 80 |
5 files changed, 122 insertions, 79 deletions
diff --git a/Documentation/acpi/method-customizing.txt b/Documentation/acpi/method-customizing.txt index 3e1d25aee3fb..5f55373dd53b 100644 --- a/Documentation/acpi/method-customizing.txt +++ b/Documentation/acpi/method-customizing.txt | |||
@@ -66,3 +66,8 @@ Note: We can use a kernel with multiple custom ACPI method running, | |||
66 | But each individual write to debugfs can implement a SINGLE | 66 | But each individual write to debugfs can implement a SINGLE |
67 | method override. i.e. if we want to insert/override multiple | 67 | method override. i.e. if we want to insert/override multiple |
68 | ACPI methods, we need to redo step c) ~ g) for multiple times. | 68 | ACPI methods, we need to redo step c) ~ g) for multiple times. |
69 | |||
70 | Note: Be aware that root can mis-use this driver to modify arbitrary | ||
71 | memory and gain additional rights, if root's privileges got | ||
72 | restricted (for example if root is not allowed to load additional | ||
73 | modules after boot). | ||
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 3a17ca5fff6f..d918e130bef3 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig | |||
@@ -380,6 +380,21 @@ config ACPI_HED | |||
380 | which is used to report some hardware errors notified via | 380 | which is used to report some hardware errors notified via |
381 | SCI, mainly the corrected errors. | 381 | SCI, mainly the corrected errors. |
382 | 382 | ||
383 | config ACPI_CUSTOM_METHOD | ||
384 | tristate "Allow ACPI methods to be inserted/replaced at run time" | ||
385 | depends on DEBUG_FS | ||
386 | default n | ||
387 | help | ||
388 | This debug facility allows ACPI AML methods to me inserted and/or | ||
389 | replaced without rebooting the system. For details refer to: | ||
390 | Documentation/acpi/method-customizing.txt. | ||
391 | |||
392 | NOTE: This option is security sensitive, because it allows arbitrary | ||
393 | kernel memory to be written to by root (uid=0) users, allowing them | ||
394 | to bypass certain security measures (e.g. if root is not allowed to | ||
395 | load additional kernel modules after boot, this feature may be used | ||
396 | to override that restriction). | ||
397 | |||
383 | source "drivers/acpi/apei/Kconfig" | 398 | source "drivers/acpi/apei/Kconfig" |
384 | 399 | ||
385 | endif # ACPI | 400 | endif # ACPI |
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index d113fa5100b2..cba0b2334b9b 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile | |||
@@ -62,6 +62,7 @@ obj-$(CONFIG_ACPI_SBS) += sbs.o | |||
62 | obj-$(CONFIG_ACPI_POWER_METER) += power_meter.o | 62 | obj-$(CONFIG_ACPI_POWER_METER) += power_meter.o |
63 | obj-$(CONFIG_ACPI_HED) += hed.o | 63 | obj-$(CONFIG_ACPI_HED) += hed.o |
64 | obj-$(CONFIG_ACPI_EC_DEBUGFS) += ec_sys.o | 64 | obj-$(CONFIG_ACPI_EC_DEBUGFS) += ec_sys.o |
65 | obj-$(CONFIG_ACPI_CUSTOM_METHOD)+= custom_method.o | ||
65 | 66 | ||
66 | # processor has its own "processor." module_param namespace | 67 | # processor has its own "processor." module_param namespace |
67 | processor-y := processor_driver.o processor_throttling.o | 68 | processor-y := processor_driver.o processor_throttling.o |
diff --git a/drivers/acpi/custom_method.c b/drivers/acpi/custom_method.c new file mode 100644 index 000000000000..5d42c2414ae5 --- /dev/null +++ b/drivers/acpi/custom_method.c | |||
@@ -0,0 +1,100 @@ | |||
1 | /* | ||
2 | * debugfs.c - ACPI debugfs interface to userspace. | ||
3 | */ | ||
4 | |||
5 | #include <linux/init.h> | ||
6 | #include <linux/module.h> | ||
7 | #include <linux/kernel.h> | ||
8 | #include <linux/uaccess.h> | ||
9 | #include <linux/debugfs.h> | ||
10 | #include <acpi/acpi_drivers.h> | ||
11 | |||
12 | #include "internal.h" | ||
13 | |||
14 | #define _COMPONENT ACPI_SYSTEM_COMPONENT | ||
15 | ACPI_MODULE_NAME("custom_method"); | ||
16 | MODULE_LICENSE("GPL"); | ||
17 | |||
18 | static struct dentry *cm_dentry; | ||
19 | |||
20 | /* /sys/kernel/debug/acpi/custom_method */ | ||
21 | |||
22 | static ssize_t cm_write(struct file *file, const char __user * user_buf, | ||
23 | size_t count, loff_t *ppos) | ||
24 | { | ||
25 | static char *buf; | ||
26 | static u32 max_size; | ||
27 | static u32 uncopied_bytes; | ||
28 | |||
29 | struct acpi_table_header table; | ||
30 | acpi_status status; | ||
31 | |||
32 | if (!(*ppos)) { | ||
33 | /* parse the table header to get the table length */ | ||
34 | if (count <= sizeof(struct acpi_table_header)) | ||
35 | return -EINVAL; | ||
36 | if (copy_from_user(&table, user_buf, | ||
37 | sizeof(struct acpi_table_header))) | ||
38 | return -EFAULT; | ||
39 | uncopied_bytes = max_size = table.length; | ||
40 | buf = kzalloc(max_size, GFP_KERNEL); | ||
41 | if (!buf) | ||
42 | return -ENOMEM; | ||
43 | } | ||
44 | |||
45 | if (buf == NULL) | ||
46 | return -EINVAL; | ||
47 | |||
48 | if ((*ppos > max_size) || | ||
49 | (*ppos + count > max_size) || | ||
50 | (*ppos + count < count) || | ||
51 | (count > uncopied_bytes)) | ||
52 | return -EINVAL; | ||
53 | |||
54 | if (copy_from_user(buf + (*ppos), user_buf, count)) { | ||
55 | kfree(buf); | ||
56 | buf = NULL; | ||
57 | return -EFAULT; | ||
58 | } | ||
59 | |||
60 | uncopied_bytes -= count; | ||
61 | *ppos += count; | ||
62 | |||
63 | if (!uncopied_bytes) { | ||
64 | status = acpi_install_method(buf); | ||
65 | kfree(buf); | ||
66 | buf = NULL; | ||
67 | if (ACPI_FAILURE(status)) | ||
68 | return -EINVAL; | ||
69 | add_taint(TAINT_OVERRIDDEN_ACPI_TABLE); | ||
70 | } | ||
71 | |||
72 | return count; | ||
73 | } | ||
74 | |||
75 | static const struct file_operations cm_fops = { | ||
76 | .write = cm_write, | ||
77 | .llseek = default_llseek, | ||
78 | }; | ||
79 | |||
80 | static int __init acpi_custom_method_init(void) | ||
81 | { | ||
82 | if (acpi_debugfs_dir == NULL) | ||
83 | return -ENOENT; | ||
84 | |||
85 | cm_dentry = debugfs_create_file("custom_method", S_IWUSR, | ||
86 | acpi_debugfs_dir, NULL, &cm_fops); | ||
87 | if (cm_dentry == NULL) | ||
88 | return -ENODEV; | ||
89 | |||
90 | return 0; | ||
91 | } | ||
92 | |||
93 | static void __exit acpi_custom_method_exit(void) | ||
94 | { | ||
95 | if (cm_dentry) | ||
96 | debugfs_remove(cm_dentry); | ||
97 | } | ||
98 | |||
99 | module_init(acpi_custom_method_init); | ||
100 | module_exit(acpi_custom_method_exit); | ||
diff --git a/drivers/acpi/debugfs.c b/drivers/acpi/debugfs.c index e7abc6e3bba0..182a9fc36355 100644 --- a/drivers/acpi/debugfs.c +++ b/drivers/acpi/debugfs.c | |||
@@ -3,9 +3,6 @@ | |||
3 | */ | 3 | */ |
4 | 4 | ||
5 | #include <linux/init.h> | 5 | #include <linux/init.h> |
6 | #include <linux/module.h> | ||
7 | #include <linux/kernel.h> | ||
8 | #include <linux/uaccess.h> | ||
9 | #include <linux/debugfs.h> | 6 | #include <linux/debugfs.h> |
10 | #include <acpi/acpi_drivers.h> | 7 | #include <acpi/acpi_drivers.h> |
11 | 8 | ||
@@ -13,84 +10,9 @@ | |||
13 | ACPI_MODULE_NAME("debugfs"); | 10 | ACPI_MODULE_NAME("debugfs"); |
14 | 11 | ||
15 | struct dentry *acpi_debugfs_dir; | 12 | struct dentry *acpi_debugfs_dir; |
16 | static struct dentry *cm_dentry; | 13 | EXPORT_SYMBOL_GPL(acpi_debugfs_dir); |
17 | |||
18 | /* /sys/kernel/debug/acpi/custom_method */ | ||
19 | |||
20 | static ssize_t cm_write(struct file *file, const char __user * user_buf, | ||
21 | size_t count, loff_t *ppos) | ||
22 | { | ||
23 | static char *buf; | ||
24 | static u32 max_size; | ||
25 | static u32 uncopied_bytes; | ||
26 | |||
27 | struct acpi_table_header table; | ||
28 | acpi_status status; | ||
29 | |||
30 | if (!(*ppos)) { | ||
31 | /* parse the table header to get the table length */ | ||
32 | if (count <= sizeof(struct acpi_table_header)) | ||
33 | return -EINVAL; | ||
34 | if (copy_from_user(&table, user_buf, | ||
35 | sizeof(struct acpi_table_header))) | ||
36 | return -EFAULT; | ||
37 | uncopied_bytes = max_size = table.length; | ||
38 | buf = kzalloc(max_size, GFP_KERNEL); | ||
39 | if (!buf) | ||
40 | return -ENOMEM; | ||
41 | } | ||
42 | |||
43 | if (buf == NULL) | ||
44 | return -EINVAL; | ||
45 | |||
46 | if ((*ppos > max_size) || | ||
47 | (*ppos + count > max_size) || | ||
48 | (*ppos + count < count) || | ||
49 | (count > uncopied_bytes)) | ||
50 | return -EINVAL; | ||
51 | |||
52 | if (copy_from_user(buf + (*ppos), user_buf, count)) { | ||
53 | kfree(buf); | ||
54 | buf = NULL; | ||
55 | return -EFAULT; | ||
56 | } | ||
57 | |||
58 | uncopied_bytes -= count; | ||
59 | *ppos += count; | ||
60 | |||
61 | if (!uncopied_bytes) { | ||
62 | status = acpi_install_method(buf); | ||
63 | kfree(buf); | ||
64 | buf = NULL; | ||
65 | if (ACPI_FAILURE(status)) | ||
66 | return -EINVAL; | ||
67 | add_taint(TAINT_OVERRIDDEN_ACPI_TABLE); | ||
68 | } | ||
69 | |||
70 | return count; | ||
71 | } | ||
72 | |||
73 | static const struct file_operations cm_fops = { | ||
74 | .write = cm_write, | ||
75 | .llseek = default_llseek, | ||
76 | }; | ||
77 | |||
78 | static int __init acpi_custom_method_init(void) | ||
79 | { | ||
80 | if (!acpi_debugfs_dir) | ||
81 | return -ENOENT; | ||
82 | |||
83 | cm_dentry = debugfs_create_file("custom_method", S_IWUSR, | ||
84 | acpi_debugfs_dir, NULL, &cm_fops); | ||
85 | if (!cm_dentry) | ||
86 | return -ENODEV; | ||
87 | |||
88 | return 0; | ||
89 | } | ||
90 | 14 | ||
91 | void __init acpi_debugfs_init(void) | 15 | void __init acpi_debugfs_init(void) |
92 | { | 16 | { |
93 | acpi_debugfs_dir = debugfs_create_dir("acpi", NULL); | 17 | acpi_debugfs_dir = debugfs_create_dir("acpi", NULL); |
94 | |||
95 | acpi_custom_method_init(); | ||
96 | } | 18 | } |