Skip to content

Commit

Permalink
pinctrl: add checks for empty function names
Browse files Browse the repository at this point in the history
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: Tony Lindgren <tony@atomide.com>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
  • Loading branch information
Tony Lindgren authored and Linus Walleij committed Jan 26, 2012
1 parent 9e2551e commit b9130b7
Show file tree
Hide file tree
Showing 5 changed files with 41 additions and 24 deletions.
36 changes: 18 additions & 18 deletions drivers/pinctrl/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -583,40 +583,40 @@ struct pinctrl_dev *pinctrl_register(struct pinctrl_desc *pctldesc,
if (pctldesc->name == 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 (pctldesc->pmxops) {
ret = pinmux_check_ops(pctldesc->pmxops);
ret = pinmux_check_ops(pctldev);
if (ret) {
pr_err("%s pinmux ops lacks necessary functions\n",
pctldesc->name);
return NULL;
goto out_err;
}
}

/* If we're implementing pinconfig, check the ops for sanity */
if (pctldesc->confops) {
ret = pinconf_check_ops(pctldesc->confops);
ret = pinconf_check_ops(pctldev);
if (ret) {
pr_err("%s pin config ops lacks necessary functions\n",
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 */
pr_debug("try to register %d pins on %s...\n",
pctldesc->npins, pctldesc->name);
Expand Down
4 changes: 3 additions & 1 deletion drivers/pinctrl/pinconf.c
Original file line number Diff line number Diff line change
Expand Up @@ -205,8 +205,10 @@ int pin_config_group_set(const char *dev_name, const char *pin_group,
}
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 */
if (!ops->pin_config_get && !ops->pin_config_group_get)
return -EINVAL;
Expand Down
4 changes: 2 additions & 2 deletions drivers/pinctrl/pinconf.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

#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,
struct pinctrl_dev *pctldev);
int pin_config_get_for_pin(struct pinctrl_dev *pctldev, unsigned pin,
Expand All @@ -23,7 +23,7 @@ int pin_config_set_for_pin(struct pinctrl_dev *pctldev, unsigned pin,

#else

static inline int pinconf_check_ops(const struct pinconf_ops *ops)
static inline int pinconf_check_ops(struct pinctrl_dev *pctldev)
{
return 0;
}
Expand Down
17 changes: 16 additions & 1 deletion drivers/pinctrl/pinmux.c
Original file line number Diff line number Diff line change
Expand Up @@ -889,8 +889,11 @@ void pinmux_disable(struct pinmux *pmx)
}
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 */
if (!ops->list_functions ||
!ops->get_function_name ||
Expand All @@ -899,6 +902,18 @@ int pinmux_check_ops(const struct pinmux_ops *ops)
!ops->disable)
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;
}

Expand Down
4 changes: 2 additions & 2 deletions drivers/pinctrl/pinmux.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
*/
#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,
struct pinctrl_dev *pctldev);
void pinmux_init_debugfs(struct dentry *subsys_root);
Expand All @@ -21,7 +21,7 @@ void pinmux_unhog_maps(struct pinctrl_dev *pctldev);

#else

static inline int pinmux_check_ops(const struct pinmux_ops *ops)
static inline int pinmux_check_ops(struct pinctrl_dev *pctldev)
{
return 0;
}
Expand Down

0 comments on commit b9130b7

Please sign in to comment.