ACPI: hotkey.c fixes, fix for potential crash of hotkey.c

While going through the code, I found out some memory leaks and potential
crashes in drivers/acpi/hotkey.c Please find the patch to fix them.

This patch does the following,

1. Fixes memory leaks in error paths of hotkey_write_config

2. Fixes freeing unallocated pointers in the error paths of hotkey_write_config

3. Uses a loop instead of linear searching for parsing the userspace
   input in get_params

4. Uses array of char * instead of passing 4 pointer parameters
   explicitly into the init_{poll_}hotkey_* static functions

Signed-off-by: Andrew Morton <akpm@osdl.org>
Acked-by: Luming Yu <luming.yu@intel.com>
Signed-off-by: Len Brown <len.brown@intel.com>
This commit is contained in:
Handle X 2006-08-14 22:37:27 -07:00 committed by Len Brown
parent 9f737633e6
commit 5672bde635
1 changed files with 120 additions and 163 deletions

View File

@ -91,6 +91,14 @@ enum {
HK_EVENT_ENTERRING_S5, HK_EVENT_ENTERRING_S5,
}; };
enum conf_entry_enum {
bus_handle = 0,
bus_method = 1,
action_handle = 2,
method = 3,
LAST_CONF_ENTRY
};
/* procdir we use */ /* procdir we use */
static struct proc_dir_entry *hotkey_proc_dir; static struct proc_dir_entry *hotkey_proc_dir;
static struct proc_dir_entry *hotkey_config; static struct proc_dir_entry *hotkey_config;
@ -244,19 +252,15 @@ static int hotkey_info_open_fs(struct inode *inode, struct file *file)
static char *format_result(union acpi_object *object) static char *format_result(union acpi_object *object)
{ {
char *buf = NULL; char *buf;
buf = (char *)kmalloc(RESULT_STR_LEN, GFP_KERNEL);
if (buf)
memset(buf, 0, RESULT_STR_LEN);
else
goto do_fail;
buf = kzalloc(RESULT_STR_LEN, GFP_KERNEL);
if (!buf)
return NULL;
/* Now, just support integer type */ /* Now, just support integer type */
if (object->type == ACPI_TYPE_INTEGER) if (object->type == ACPI_TYPE_INTEGER)
sprintf(buf, "%d\n", (u32) object->integer.value); sprintf(buf, "%d\n", (u32) object->integer.value);
do_fail: return buf;
return (buf);
} }
static int hotkey_polling_seq_show(struct seq_file *seq, void *offset) static int hotkey_polling_seq_show(struct seq_file *seq, void *offset)
@ -486,98 +490,102 @@ static void free_hotkey_device(union acpi_hotkey *key)
static void free_hotkey_buffer(union acpi_hotkey *key) static void free_hotkey_buffer(union acpi_hotkey *key)
{ {
/* key would never be null, action method could be */
kfree(key->event_hotkey.action_method); kfree(key->event_hotkey.action_method);
} }
static void free_poll_hotkey_buffer(union acpi_hotkey *key) static void free_poll_hotkey_buffer(union acpi_hotkey *key)
{ {
/* key would never be null, others could be*/
kfree(key->poll_hotkey.action_method); kfree(key->poll_hotkey.action_method);
kfree(key->poll_hotkey.poll_method); kfree(key->poll_hotkey.poll_method);
kfree(key->poll_hotkey.poll_result); kfree(key->poll_hotkey.poll_result);
} }
static int static int
init_hotkey_device(union acpi_hotkey *key, char *bus_str, char *action_str, init_hotkey_device(union acpi_hotkey *key, char **config_entry,
char *method, int std_num, int external_num) int std_num, int external_num)
{ {
acpi_handle tmp_handle; acpi_handle tmp_handle;
acpi_status status = AE_OK; acpi_status status = AE_OK;
if (std_num < 0 || IS_POLL(std_num) || !key) if (std_num < 0 || IS_POLL(std_num) || !key)
goto do_fail; goto do_fail;
if (!bus_str || !action_str || !method) if (!config_entry[bus_handle] || !config_entry[action_handle]
|| !config_entry[method])
goto do_fail; goto do_fail;
key->link.hotkey_type = ACPI_HOTKEY_EVENT; key->link.hotkey_type = ACPI_HOTKEY_EVENT;
key->link.hotkey_standard_num = std_num; key->link.hotkey_standard_num = std_num;
key->event_hotkey.flag = 0; key->event_hotkey.flag = 0;
key->event_hotkey.action_method = method; key->event_hotkey.action_method = config_entry[method];
status = status = acpi_get_handle(NULL, config_entry[bus_handle],
acpi_get_handle(NULL, bus_str, &(key->event_hotkey.bus_handle)); &(key->event_hotkey.bus_handle));
if (ACPI_FAILURE(status)) if (ACPI_FAILURE(status))
goto do_fail; goto do_fail_zero;
key->event_hotkey.external_hotkey_num = external_num; key->event_hotkey.external_hotkey_num = external_num;
status = status = acpi_get_handle(NULL, config_entry[action_handle],
acpi_get_handle(NULL, action_str,
&(key->event_hotkey.action_handle)); &(key->event_hotkey.action_handle));
if (ACPI_FAILURE(status)) if (ACPI_FAILURE(status))
goto do_fail; goto do_fail_zero;
status = acpi_get_handle(key->event_hotkey.action_handle, status = acpi_get_handle(key->event_hotkey.action_handle,
method, &tmp_handle); config_entry[method], &tmp_handle);
if (ACPI_FAILURE(status)) if (ACPI_FAILURE(status))
goto do_fail; goto do_fail_zero;
return AE_OK; return AE_OK;
do_fail: do_fail_zero:
key->event_hotkey.action_method = NULL;
do_fail:
return -ENODEV; return -ENODEV;
} }
static int static int
init_poll_hotkey_device(union acpi_hotkey *key, init_poll_hotkey_device(union acpi_hotkey *key, char **config_entry,
char *poll_str, int std_num)
char *poll_method,
char *action_str, char *action_method, int std_num)
{ {
acpi_status status = AE_OK; acpi_status status = AE_OK;
acpi_handle tmp_handle; acpi_handle tmp_handle;
if (std_num < 0 || IS_EVENT(std_num) || !key) if (std_num < 0 || IS_EVENT(std_num) || !key)
goto do_fail; goto do_fail;
if (!config_entry[bus_handle] ||!config_entry[bus_method] ||
if (!poll_str || !poll_method || !action_str || !action_method) !config_entry[action_handle] || !config_entry[method])
goto do_fail; goto do_fail;
key->link.hotkey_type = ACPI_HOTKEY_POLLING; key->link.hotkey_type = ACPI_HOTKEY_POLLING;
key->link.hotkey_standard_num = std_num; key->link.hotkey_standard_num = std_num;
key->poll_hotkey.flag = 0; key->poll_hotkey.flag = 0;
key->poll_hotkey.poll_method = poll_method; key->poll_hotkey.poll_method = config_entry[bus_method];
key->poll_hotkey.action_method = action_method; key->poll_hotkey.action_method = config_entry[method];
status = status = acpi_get_handle(NULL, config_entry[bus_handle],
acpi_get_handle(NULL, poll_str, &(key->poll_hotkey.poll_handle)); &(key->poll_hotkey.poll_handle));
if (ACPI_FAILURE(status)) if (ACPI_FAILURE(status))
goto do_fail; goto do_fail_zero;
status = acpi_get_handle(key->poll_hotkey.poll_handle, status = acpi_get_handle(key->poll_hotkey.poll_handle,
poll_method, &tmp_handle); config_entry[bus_method], &tmp_handle);
if (ACPI_FAILURE(status)) if (ACPI_FAILURE(status))
goto do_fail; goto do_fail_zero;
status = status =
acpi_get_handle(NULL, action_str, acpi_get_handle(NULL, config_entry[action_handle],
&(key->poll_hotkey.action_handle)); &(key->poll_hotkey.action_handle));
if (ACPI_FAILURE(status)) if (ACPI_FAILURE(status))
goto do_fail; goto do_fail_zero;
status = acpi_get_handle(key->poll_hotkey.action_handle, status = acpi_get_handle(key->poll_hotkey.action_handle,
action_method, &tmp_handle); config_entry[method], &tmp_handle);
if (ACPI_FAILURE(status)) if (ACPI_FAILURE(status))
goto do_fail; goto do_fail_zero;
key->poll_hotkey.poll_result = key->poll_hotkey.poll_result =
(union acpi_object *)kmalloc(sizeof(union acpi_object), GFP_KERNEL); (union acpi_object *)kmalloc(sizeof(union acpi_object), GFP_KERNEL);
if (!key->poll_hotkey.poll_result) if (!key->poll_hotkey.poll_result)
goto do_fail; goto do_fail_zero;
return AE_OK; return AE_OK;
do_fail:
do_fail_zero:
key->poll_hotkey.poll_method = NULL;
key->poll_hotkey.action_method = NULL;
do_fail:
return -ENODEV; return -ENODEV;
} }
@ -652,17 +660,18 @@ static int hotkey_poll_config_seq_show(struct seq_file *seq, void *offset)
} }
static int static int
get_parms(char *config_record, get_parms(char *config_record, int *cmd, char **config_entry,
int *cmd, int *internal_event_num, int *external_event_num)
char **bus_handle,
char **bus_method,
char **action_handle,
char **method, int *internal_event_num, int *external_event_num)
{ {
/* the format of *config_record =
* "1:\d+:*" : "cmd:internal_event_num"
* "\d+:\w+:\w+:\w+:\w+:\d+:\d+" :
* "cmd:bus_handle:bus_method:action_handle:method:internal_event_num:external_event_num"
*/
char *tmp, *tmp1, count; char *tmp, *tmp1, count;
int i;
sscanf(config_record, "%d", cmd); sscanf(config_record, "%d", cmd);
if (*cmd == 1) { if (*cmd == 1) {
if (sscanf(config_record, "%d:%d", cmd, internal_event_num) != if (sscanf(config_record, "%d:%d", cmd, internal_event_num) !=
2) 2)
@ -674,59 +683,27 @@ get_parms(char *config_record,
if (!tmp) if (!tmp)
goto do_fail; goto do_fail;
tmp++; tmp++;
for (i = 0; i < LAST_CONF_ENTRY; i++) {
tmp1 = strchr(tmp, ':'); tmp1 = strchr(tmp, ':');
if (!tmp1) if (!tmp1) {
goto do_fail; goto do_fail;
}
count = tmp1 - tmp; count = tmp1 - tmp;
*bus_handle = (char *)kmalloc(count + 1, GFP_KERNEL); config_entry[i] = kzalloc(count + 1, GFP_KERNEL);
if (!*bus_handle) if (!config_entry[i])
goto do_fail; goto handle_failure;
strncpy(*bus_handle, tmp, count); strncpy(config_entry[i], tmp, count);
*(*bus_handle + count) = 0; tmp = tmp1 + 1;
}
tmp = tmp1; if (sscanf(tmp, "%d:%d", internal_event_num, external_event_num) <= 0)
tmp++; goto handle_failure;
tmp1 = strchr(tmp, ':'); if (!IS_OTHERS(*internal_event_num)) {
if (!tmp1)
goto do_fail;
count = tmp1 - tmp;
*bus_method = (char *)kmalloc(count + 1, GFP_KERNEL);
if (!*bus_method)
goto do_fail;
strncpy(*bus_method, tmp, count);
*(*bus_method + count) = 0;
tmp = tmp1;
tmp++;
tmp1 = strchr(tmp, ':');
if (!tmp1)
goto do_fail;
count = tmp1 - tmp;
*action_handle = (char *)kmalloc(count + 1, GFP_KERNEL);
if (!*action_handle)
goto do_fail;
strncpy(*action_handle, tmp, count);
*(*action_handle + count) = 0;
tmp = tmp1;
tmp++;
tmp1 = strchr(tmp, ':');
if (!tmp1)
goto do_fail;
count = tmp1 - tmp;
*method = (char *)kmalloc(count + 1, GFP_KERNEL);
if (!*method)
goto do_fail;
strncpy(*method, tmp, count);
*(*method + count) = 0;
if (sscanf(tmp1 + 1, "%d:%d", internal_event_num, external_event_num) <=
0)
goto do_fail;
return 6; return 6;
do_fail: }
handle_failure:
while (i-- > 0)
kfree(config_entry[i]);
do_fail:
return -1; return -1;
} }
@ -736,50 +713,34 @@ static ssize_t hotkey_write_config(struct file *file,
size_t count, loff_t * data) size_t count, loff_t * data)
{ {
char *config_record = NULL; char *config_record = NULL;
char *bus_handle = NULL; char *config_entry[LAST_CONF_ENTRY];
char *bus_method = NULL;
char *action_handle = NULL;
char *method = NULL;
int cmd, internal_event_num, external_event_num; int cmd, internal_event_num, external_event_num;
int ret = 0; int ret = 0;
union acpi_hotkey *key = NULL; union acpi_hotkey *key = kzalloc(sizeof(union acpi_hotkey), GFP_KERNEL);
if (!key)
config_record = (char *)kmalloc(count + 1, GFP_KERNEL);
if (!config_record)
return -ENOMEM; return -ENOMEM;
config_record = kzalloc(count + 1, GFP_KERNEL);
if (!config_record) {
kfree(key);
return -ENOMEM;
}
if (copy_from_user(config_record, buffer, count)) { if (copy_from_user(config_record, buffer, count)) {
kfree(config_record); kfree(config_record);
kfree(key);
printk(KERN_ERR PREFIX "Invalid data\n"); printk(KERN_ERR PREFIX "Invalid data\n");
return -EINVAL; return -EINVAL;
} }
config_record[count] = 0; ret = get_parms(config_record, &cmd, config_entry,
&internal_event_num, &external_event_num);
ret = get_parms(config_record,
&cmd,
&bus_handle,
&bus_method,
&action_handle,
&method, &internal_event_num, &external_event_num);
kfree(config_record); kfree(config_record);
if (IS_OTHERS(internal_event_num))
goto do_fail;
if (ret != 6) { if (ret != 6) {
do_fail:
kfree(bus_handle);
kfree(bus_method);
kfree(action_handle);
kfree(method);
printk(KERN_ERR PREFIX "Invalid data format ret=%d\n", ret); printk(KERN_ERR PREFIX "Invalid data format ret=%d\n", ret);
return -EINVAL; return -EINVAL;
} }
key = kmalloc(sizeof(union acpi_hotkey), GFP_KERNEL);
if (!key)
goto do_fail;
memset(key, 0, sizeof(union acpi_hotkey));
if (cmd == 1) { if (cmd == 1) {
union acpi_hotkey *tmp = NULL; union acpi_hotkey *tmp = NULL;
tmp = get_hotkey_by_event(&global_hotkey_list, tmp = get_hotkey_by_event(&global_hotkey_list,
@ -791,34 +752,19 @@ static ssize_t hotkey_write_config(struct file *file,
goto cont_cmd; goto cont_cmd;
} }
if (IS_EVENT(internal_event_num)) { if (IS_EVENT(internal_event_num)) {
kfree(bus_method); if (init_hotkey_device(key, config_entry,
ret = init_hotkey_device(key, bus_handle, action_handle, method, internal_event_num, external_event_num))
internal_event_num, goto init_hotkey_fail;
external_event_num); } else {
} else if (init_poll_hotkey_device(key, config_entry,
ret = init_poll_hotkey_device(key, bus_handle, bus_method, internal_event_num))
action_handle, method, goto init_poll_hotkey_fail;
internal_event_num);
if (ret) {
kfree(bus_handle);
kfree(action_handle);
if (IS_EVENT(internal_event_num))
free_hotkey_buffer(key);
else
free_poll_hotkey_buffer(key);
kfree(key);
printk(KERN_ERR PREFIX "Invalid hotkey\n");
return -EINVAL;
} }
cont_cmd:
cont_cmd:
kfree(bus_handle);
kfree(action_handle);
switch (cmd) { switch (cmd) {
case 0: case 0:
if (get_hotkey_by_event if (get_hotkey_by_event(&global_hotkey_list,
(&global_hotkey_list, key->link.hotkey_standard_num)) key->link.hotkey_standard_num))
goto fail_out; goto fail_out;
else else
hotkey_add(key); hotkey_add(key);
@ -827,6 +773,7 @@ static ssize_t hotkey_write_config(struct file *file,
hotkey_remove(key); hotkey_remove(key);
break; break;
case 2: case 2:
/* key is kfree()ed if matched*/
if (hotkey_update(key)) if (hotkey_update(key))
goto fail_out; goto fail_out;
break; break;
@ -835,11 +782,22 @@ static ssize_t hotkey_write_config(struct file *file,
break; break;
} }
return count; return count;
fail_out:
if (IS_EVENT(internal_event_num)) init_poll_hotkey_fail: /* failed init_poll_hotkey_device */
free_hotkey_buffer(key); kfree(config_entry[bus_method]);
else config_entry[bus_method] = NULL;
free_poll_hotkey_buffer(key); init_hotkey_fail: /* failed init_hotkey_device */
kfree(config_entry[method]);
fail_out:
kfree(config_entry[bus_handle]);
kfree(config_entry[action_handle]);
/* No double free since elements =NULL for error cases */
if (IS_EVENT(internal_event_num)) {
if (config_entry[bus_method])
kfree(config_entry[bus_method]);
free_hotkey_buffer(key); /* frees [method] */
} else
free_poll_hotkey_buffer(key); /* frees [bus_method]+[method] */
kfree(key); kfree(key);
printk(KERN_ERR PREFIX "invalid key\n"); printk(KERN_ERR PREFIX "invalid key\n");
return -EINVAL; return -EINVAL;
@ -923,10 +881,9 @@ static ssize_t hotkey_execute_aml_method(struct file *file,
union acpi_hotkey *key; union acpi_hotkey *key;
arg = (char *)kmalloc(count + 1, GFP_KERNEL); arg = kzalloc(count + 1, GFP_KERNEL);
if (!arg) if (!arg)
return -ENOMEM; return -ENOMEM;
arg[count] = 0;
if (copy_from_user(arg, buffer, count)) { if (copy_from_user(arg, buffer, count)) {
kfree(arg); kfree(arg);