Commit b9130b77 authored by Tony Lindgren's avatar Tony Lindgren Committed by Linus Walleij

pinctrl: add checks for empty function names

This is needed as otherwise we can get the following when
dealing with buggy data in a pinmux driver for
pinmux_search_function:

Unable to handle kernel NULL pointer dereference at virtual
address 00000000
...
PC is at strcmp+0xc/0x34
LR is at pinmux_get+0x350/0x8f4
...

As we need pctldev initialized to call ops->list_functions,
let's initialize it before check_ops calls and pass the
pctldev to the check_ops functions. Do this for both pinmux
and pinconf check_ops functions.
Signed-off-by: default avatarTony Lindgren <tony@atomide.com>
Signed-off-by: default avatarLinus Walleij <linus.walleij@linaro.org>
parent 9e2551e1
...@@ -583,40 +583,40 @@ struct pinctrl_dev *pinctrl_register(struct pinctrl_desc *pctldesc, ...@@ -583,40 +583,40 @@ struct pinctrl_dev *pinctrl_register(struct pinctrl_desc *pctldesc,
if (pctldesc->name == NULL) if (pctldesc->name == NULL)
return NULL; return NULL;
pctldev = kzalloc(sizeof(struct pinctrl_dev), GFP_KERNEL);
if (pctldev == NULL)
return NULL;
/* Initialize pin control device struct */
pctldev->owner = pctldesc->owner;
pctldev->desc = pctldesc;
pctldev->driver_data = driver_data;
INIT_RADIX_TREE(&pctldev->pin_desc_tree, GFP_KERNEL);
spin_lock_init(&pctldev->pin_desc_tree_lock);
INIT_LIST_HEAD(&pctldev->gpio_ranges);
mutex_init(&pctldev->gpio_ranges_lock);
pctldev->dev = dev;
/* If we're implementing pinmuxing, check the ops for sanity */ /* If we're implementing pinmuxing, check the ops for sanity */
if (pctldesc->pmxops) { if (pctldesc->pmxops) {
ret = pinmux_check_ops(pctldesc->pmxops); ret = pinmux_check_ops(pctldev);
if (ret) { if (ret) {
pr_err("%s pinmux ops lacks necessary functions\n", pr_err("%s pinmux ops lacks necessary functions\n",
pctldesc->name); pctldesc->name);
return NULL; goto out_err;
} }
} }
/* If we're implementing pinconfig, check the ops for sanity */ /* If we're implementing pinconfig, check the ops for sanity */
if (pctldesc->confops) { if (pctldesc->confops) {
ret = pinconf_check_ops(pctldesc->confops); ret = pinconf_check_ops(pctldev);
if (ret) { if (ret) {
pr_err("%s pin config ops lacks necessary functions\n", pr_err("%s pin config ops lacks necessary functions\n",
pctldesc->name); pctldesc->name);
return NULL; goto out_err;
} }
} }
pctldev = kzalloc(sizeof(struct pinctrl_dev), GFP_KERNEL);
if (pctldev == NULL)
return NULL;
/* Initialize pin control device struct */
pctldev->owner = pctldesc->owner;
pctldev->desc = pctldesc;
pctldev->driver_data = driver_data;
INIT_RADIX_TREE(&pctldev->pin_desc_tree, GFP_KERNEL);
spin_lock_init(&pctldev->pin_desc_tree_lock);
INIT_LIST_HEAD(&pctldev->gpio_ranges);
mutex_init(&pctldev->gpio_ranges_lock);
pctldev->dev = dev;
/* Register all the pins */ /* Register all the pins */
pr_debug("try to register %d pins on %s...\n", pr_debug("try to register %d pins on %s...\n",
pctldesc->npins, pctldesc->name); pctldesc->npins, pctldesc->name);
......
...@@ -205,8 +205,10 @@ int pin_config_group_set(const char *dev_name, const char *pin_group, ...@@ -205,8 +205,10 @@ int pin_config_group_set(const char *dev_name, const char *pin_group,
} }
EXPORT_SYMBOL(pin_config_group_set); EXPORT_SYMBOL(pin_config_group_set);
int pinconf_check_ops(const struct pinconf_ops *ops) int pinconf_check_ops(struct pinctrl_dev *pctldev)
{ {
const struct pinconf_ops *ops = pctldev->desc->confops;
/* We must be able to read out pin status */ /* We must be able to read out pin status */
if (!ops->pin_config_get && !ops->pin_config_group_get) if (!ops->pin_config_get && !ops->pin_config_group_get)
return -EINVAL; return -EINVAL;
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
#ifdef CONFIG_PINCONF #ifdef CONFIG_PINCONF
int pinconf_check_ops(const struct pinconf_ops *ops); int pinconf_check_ops(struct pinctrl_dev *pctldev);
void pinconf_init_device_debugfs(struct dentry *devroot, void pinconf_init_device_debugfs(struct dentry *devroot,
struct pinctrl_dev *pctldev); struct pinctrl_dev *pctldev);
int pin_config_get_for_pin(struct pinctrl_dev *pctldev, unsigned pin, int pin_config_get_for_pin(struct pinctrl_dev *pctldev, unsigned pin,
...@@ -23,7 +23,7 @@ int pin_config_set_for_pin(struct pinctrl_dev *pctldev, unsigned pin, ...@@ -23,7 +23,7 @@ int pin_config_set_for_pin(struct pinctrl_dev *pctldev, unsigned pin,
#else #else
static inline int pinconf_check_ops(const struct pinconf_ops *ops) static inline int pinconf_check_ops(struct pinctrl_dev *pctldev)
{ {
return 0; return 0;
} }
......
...@@ -889,8 +889,11 @@ void pinmux_disable(struct pinmux *pmx) ...@@ -889,8 +889,11 @@ void pinmux_disable(struct pinmux *pmx)
} }
EXPORT_SYMBOL_GPL(pinmux_disable); EXPORT_SYMBOL_GPL(pinmux_disable);
int pinmux_check_ops(const struct pinmux_ops *ops) int pinmux_check_ops(struct pinctrl_dev *pctldev)
{ {
const struct pinmux_ops *ops = pctldev->desc->pmxops;
unsigned selector = 0;
/* Check that we implement required operations */ /* Check that we implement required operations */
if (!ops->list_functions || if (!ops->list_functions ||
!ops->get_function_name || !ops->get_function_name ||
...@@ -899,6 +902,18 @@ int pinmux_check_ops(const struct pinmux_ops *ops) ...@@ -899,6 +902,18 @@ int pinmux_check_ops(const struct pinmux_ops *ops)
!ops->disable) !ops->disable)
return -EINVAL; return -EINVAL;
/* Check that all functions registered have names */
while (ops->list_functions(pctldev, selector) >= 0) {
const char *fname = ops->get_function_name(pctldev,
selector);
if (!fname) {
pr_err("pinmux ops has no name for function%u\n",
selector);
return -EINVAL;
}
selector++;
}
return 0; return 0;
} }
......
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
*/ */
#ifdef CONFIG_PINMUX #ifdef CONFIG_PINMUX
int pinmux_check_ops(const struct pinmux_ops *ops); int pinmux_check_ops(struct pinctrl_dev *pctldev);
void pinmux_init_device_debugfs(struct dentry *devroot, void pinmux_init_device_debugfs(struct dentry *devroot,
struct pinctrl_dev *pctldev); struct pinctrl_dev *pctldev);
void pinmux_init_debugfs(struct dentry *subsys_root); void pinmux_init_debugfs(struct dentry *subsys_root);
...@@ -21,7 +21,7 @@ void pinmux_unhog_maps(struct pinctrl_dev *pctldev); ...@@ -21,7 +21,7 @@ void pinmux_unhog_maps(struct pinctrl_dev *pctldev);
#else #else
static inline int pinmux_check_ops(const struct pinmux_ops *ops) static inline int pinmux_check_ops(struct pinctrl_dev *pctldev)
{ {
return 0; return 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment