configfs: fix kernel infoleak through user-controlled format string

Some modules call config_item_init_type_name() and config_group_init_type_name()
with parameter "name" directly controlled by userspace.  These two
functions call config_item_set_name() with this name used as a format
string, which can be used to leak information such as content of the
stack to userspace.

For example, make_netconsole_target() in netconsole module calls
config_item_init_type_name() with the name of a newly-created directory.
This means that the following commands give some unexpected output, with
configfs mounted in /sys/kernel/config/ and on a system with a
configured eth0 ethernet interface:

    # modprobe netconsole
    # mkdir /sys/kernel/config/netconsole/target_%lx
    # echo eth0 > /sys/kernel/config/netconsole/target_%lx/dev_name
    # echo 1 > /sys/kernel/config/netconsole/target_%lx/enabled
    # echo eth0 > /sys/kernel/config/netconsole/target_%lx/dev_name
    # dmesg |tail -n1
    [  142.697668] netconsole: target (target_ffffffffc0ae8080) is
    enabled, disable to update parameters

The directory name is correct but %lx has been interpreted in the
internal item name, displayed here in the error message used by
store_dev_name() in drivers/net/netconsole.c.

To fix this, update every caller of config_item_set_name to use "%s"
when operating on untrusted input.

This issue was found using -Wformat-security gcc flag, once a __printf
attribute has been added to config_item_set_name().

Signed-off-by: Nicolas Iooss <nicolas.iooss_linux@m4x.org>
Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Acked-by: Felipe Balbi <balbi@ti.com>
Acked-by: Joel Becker <jlbec@evilplan.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
Nicolas Iooss 2015-07-17 16:23:45 -07:00 committed by Linus Torvalds
parent 8db1486065
commit 3958b79266
2 changed files with 3 additions and 3 deletions

View file

@ -571,7 +571,7 @@ static struct config_group *function_make(
if (IS_ERR(fi)) if (IS_ERR(fi))
return ERR_CAST(fi); return ERR_CAST(fi);
ret = config_item_set_name(&fi->group.cg_item, name); ret = config_item_set_name(&fi->group.cg_item, "%s", name);
if (ret) { if (ret) {
usb_put_function_instance(fi); usb_put_function_instance(fi);
return ERR_PTR(ret); return ERR_PTR(ret);

View file

@ -115,7 +115,7 @@ void config_item_init_type_name(struct config_item *item,
const char *name, const char *name,
struct config_item_type *type) struct config_item_type *type)
{ {
config_item_set_name(item, name); config_item_set_name(item, "%s", name);
item->ci_type = type; item->ci_type = type;
config_item_init(item); config_item_init(item);
} }
@ -124,7 +124,7 @@ EXPORT_SYMBOL(config_item_init_type_name);
void config_group_init_type_name(struct config_group *group, const char *name, void config_group_init_type_name(struct config_group *group, const char *name,
struct config_item_type *type) struct config_item_type *type)
{ {
config_item_set_name(&group->cg_item, name); config_item_set_name(&group->cg_item, "%s", name);
group->cg_item.ci_type = type; group->cg_item.ci_type = type;
config_group_init(group); config_group_init(group);
} }