diff mbox series

[SRU,X,1/1] pinctrl: devicetree: Avoid taking direct reference to device name string

Message ID 20201021184351.17328-1-khalid.elmously@canonical.com
State New
Headers show
Series [SRU,X,1/1] pinctrl: devicetree: Avoid taking direct reference to device name string | expand

Commit Message

Khalid Elmously Oct. 21, 2020, 6:43 p.m. UTC
From: Will Deacon <will@kernel.org>

CVE-2020-0427

When populating the pinctrl mapping table entries for a device, the
'dev_name' field for each entry is initialised to point directly at the
string returned by 'dev_name()' for the device and subsequently used by
'create_pinctrl()' when looking up the mappings for the device being
probed.

This is unreliable in the presence of calls to 'dev_set_name()', which may
reallocate the device name string leaving the pinctrl mappings with a
dangling reference. This then leads to a use-after-free every time the
name is dereferenced by a device probe:

  | BUG: KASAN: invalid-access in strcmp+0x20/0x64
  | Read of size 1 at addr 13ffffc153494b00 by task modprobe/590
  | Pointer tag: [13], memory tag: [fe]
  |
  | Call trace:
  |  __kasan_report+0x16c/0x1dc
  |  kasan_report+0x10/0x18
  |  check_memory_region
  |  __hwasan_load1_noabort+0x4c/0x54
  |  strcmp+0x20/0x64
  |  create_pinctrl+0x18c/0x7f4
  |  pinctrl_get+0x90/0x114
  |  devm_pinctrl_get+0x44/0x98
  |  pinctrl_bind_pins+0x5c/0x450
  |  really_probe+0x1c8/0x9a4
  |  driver_probe_device+0x120/0x1d8

Follow the example of sysfs, and duplicate the device name string before
stashing it away in the pinctrl mapping entries.

Cc: Linus Walleij <linus.walleij@linaro.org>
Reported-by: Elena Petrova <lenaptr@google.com>
Tested-by: Elena Petrova <lenaptr@google.com>
Signed-off-by: Will Deacon <will@kernel.org>
Link: https://lore.kernel.org/r/20191002124206.22928-1-will@kernel.org
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
(backported from commit be4c60b563edee3712d392aaeb0943a768df7023)
[ kmously: Extra dev_err() in dt_remember_or_free_map() required
 manual merging ]
Signed-off-by: Khalid Elmously <khalid.elmously@canonical.com>
---
 drivers/pinctrl/devicetree.c | 22 +++++++++++++++++++---
 1 file changed, 19 insertions(+), 3 deletions(-)

Comments

Stefan Bader Oct. 22, 2020, 6:36 a.m. UTC | #1
On 21.10.20 20:43, Khalid Elmously wrote:
> From: Will Deacon <will@kernel.org>
> 
> CVE-2020-0427
> 
> When populating the pinctrl mapping table entries for a device, the
> 'dev_name' field for each entry is initialised to point directly at the
> string returned by 'dev_name()' for the device and subsequently used by
> 'create_pinctrl()' when looking up the mappings for the device being
> probed.
> 
> This is unreliable in the presence of calls to 'dev_set_name()', which may
> reallocate the device name string leaving the pinctrl mappings with a
> dangling reference. This then leads to a use-after-free every time the
> name is dereferenced by a device probe:
> 
>   | BUG: KASAN: invalid-access in strcmp+0x20/0x64
>   | Read of size 1 at addr 13ffffc153494b00 by task modprobe/590
>   | Pointer tag: [13], memory tag: [fe]
>   |
>   | Call trace:
>   |  __kasan_report+0x16c/0x1dc
>   |  kasan_report+0x10/0x18
>   |  check_memory_region
>   |  __hwasan_load1_noabort+0x4c/0x54
>   |  strcmp+0x20/0x64
>   |  create_pinctrl+0x18c/0x7f4
>   |  pinctrl_get+0x90/0x114
>   |  devm_pinctrl_get+0x44/0x98
>   |  pinctrl_bind_pins+0x5c/0x450
>   |  really_probe+0x1c8/0x9a4
>   |  driver_probe_device+0x120/0x1d8
> 
> Follow the example of sysfs, and duplicate the device name string before
> stashing it away in the pinctrl mapping entries.
> 
> Cc: Linus Walleij <linus.walleij@linaro.org>
> Reported-by: Elena Petrova <lenaptr@google.com>
> Tested-by: Elena Petrova <lenaptr@google.com>
> Signed-off-by: Will Deacon <will@kernel.org>
> Link: https://lore.kernel.org/r/20191002124206.22928-1-will@kernel.org
> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
> (backported from commit be4c60b563edee3712d392aaeb0943a768df7023)
> [ kmously: Extra dev_err() in dt_remember_or_free_map() required
>  manual merging ]
> Signed-off-by: Khalid Elmously <khalid.elmously@canonical.com>
Acked-by: Stefan Bader <stefan.bader@canonical.com>
> ---
>  drivers/pinctrl/devicetree.c | 22 +++++++++++++++++++---
>  1 file changed, 19 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/pinctrl/devicetree.c b/drivers/pinctrl/devicetree.c
> index fe04e748dfe4b..74eebcbd4e057 100644
> --- a/drivers/pinctrl/devicetree.c
> +++ b/drivers/pinctrl/devicetree.c
> @@ -40,6 +40,13 @@ struct pinctrl_dt_map {
>  static void dt_free_map(struct pinctrl_dev *pctldev,
>  		     struct pinctrl_map *map, unsigned num_maps)
>  {
> +	int i;
> +
> +	for (i = 0; i < num_maps; ++i) {
> +		kfree_const(map[i].dev_name);
> +		map[i].dev_name = NULL;
> +	}
> +
>  	if (pctldev) {
>  		const struct pinctrl_ops *ops = pctldev->desc->pctlops;
>  		ops->dt_free_map(pctldev, map, num_maps);
> @@ -73,7 +80,13 @@ static int dt_remember_or_free_map(struct pinctrl *p, const char *statename,
>  
>  	/* Initialize common mapping table entry fields */
>  	for (i = 0; i < num_maps; i++) {
> -		map[i].dev_name = dev_name(p->dev);
> +		const char *devname;
> +
> +		devname = kstrdup_const(dev_name(p->dev), GFP_KERNEL);
> +		if (!devname)
> +			goto err_free_map;
> +
> +		map[i].dev_name = devname;
>  		map[i].name = statename;
>  		if (pctldev)
>  			map[i].ctrl_dev_name = dev_name(pctldev->dev);
> @@ -83,8 +96,7 @@ static int dt_remember_or_free_map(struct pinctrl *p, const char *statename,
>  	dt_map = kzalloc(sizeof(*dt_map), GFP_KERNEL);
>  	if (!dt_map) {
>  		dev_err(p->dev, "failed to alloc struct pinctrl_dt_map\n");
> -		dt_free_map(pctldev, map, num_maps);
> -		return -ENOMEM;
> +		goto err_free_map;
>  	}
>  
>  	dt_map->pctldev = pctldev;
> @@ -93,6 +105,10 @@ static int dt_remember_or_free_map(struct pinctrl *p, const char *statename,
>  	list_add_tail(&dt_map->node, &p->dt_maps);
>  
>  	return pinctrl_register_map(map, num_maps, false);
> +
> +err_free_map:
> +	dt_free_map(pctldev, map, num_maps);
> +	return -ENOMEM;
>  }
>  
>  struct pinctrl_dev *of_pinctrl_get(struct device_node *np)
>
Andrea Righi Oct. 22, 2020, 7:26 a.m. UTC | #2
On Wed, Oct 21, 2020 at 02:43:51PM -0400, Khalid Elmously wrote:
> From: Will Deacon <will@kernel.org>
> 
> CVE-2020-0427
> 
> When populating the pinctrl mapping table entries for a device, the
> 'dev_name' field for each entry is initialised to point directly at the
> string returned by 'dev_name()' for the device and subsequently used by
> 'create_pinctrl()' when looking up the mappings for the device being
> probed.
> 
> This is unreliable in the presence of calls to 'dev_set_name()', which may
> reallocate the device name string leaving the pinctrl mappings with a
> dangling reference. This then leads to a use-after-free every time the
> name is dereferenced by a device probe:
> 
>   | BUG: KASAN: invalid-access in strcmp+0x20/0x64
>   | Read of size 1 at addr 13ffffc153494b00 by task modprobe/590
>   | Pointer tag: [13], memory tag: [fe]
>   |
>   | Call trace:
>   |  __kasan_report+0x16c/0x1dc
>   |  kasan_report+0x10/0x18
>   |  check_memory_region
>   |  __hwasan_load1_noabort+0x4c/0x54
>   |  strcmp+0x20/0x64
>   |  create_pinctrl+0x18c/0x7f4
>   |  pinctrl_get+0x90/0x114
>   |  devm_pinctrl_get+0x44/0x98
>   |  pinctrl_bind_pins+0x5c/0x450
>   |  really_probe+0x1c8/0x9a4
>   |  driver_probe_device+0x120/0x1d8
> 
> Follow the example of sysfs, and duplicate the device name string before
> stashing it away in the pinctrl mapping entries.
> 
> Cc: Linus Walleij <linus.walleij@linaro.org>
> Reported-by: Elena Petrova <lenaptr@google.com>
> Tested-by: Elena Petrova <lenaptr@google.com>
> Signed-off-by: Will Deacon <will@kernel.org>
> Link: https://lore.kernel.org/r/20191002124206.22928-1-will@kernel.org
> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
> (backported from commit be4c60b563edee3712d392aaeb0943a768df7023)
> [ kmously: Extra dev_err() in dt_remember_or_free_map() required
>  manual merging ]
> Signed-off-by: Khalid Elmously <khalid.elmously@canonical.com>

Acked-by: Andrea Righi <andrea.righi@canonical.com>
Ian May Oct. 22, 2020, 7:42 p.m. UTC | #3
Applied to Xenial/master-next

Thanks,
Ian

On 2020-10-21 14:43:51 , Khalid Elmously wrote:
> From: Will Deacon <will@kernel.org>
> 
> CVE-2020-0427
> 
> When populating the pinctrl mapping table entries for a device, the
> 'dev_name' field for each entry is initialised to point directly at the
> string returned by 'dev_name()' for the device and subsequently used by
> 'create_pinctrl()' when looking up the mappings for the device being
> probed.
> 
> This is unreliable in the presence of calls to 'dev_set_name()', which may
> reallocate the device name string leaving the pinctrl mappings with a
> dangling reference. This then leads to a use-after-free every time the
> name is dereferenced by a device probe:
> 
>   | BUG: KASAN: invalid-access in strcmp+0x20/0x64
>   | Read of size 1 at addr 13ffffc153494b00 by task modprobe/590
>   | Pointer tag: [13], memory tag: [fe]
>   |
>   | Call trace:
>   |  __kasan_report+0x16c/0x1dc
>   |  kasan_report+0x10/0x18
>   |  check_memory_region
>   |  __hwasan_load1_noabort+0x4c/0x54
>   |  strcmp+0x20/0x64
>   |  create_pinctrl+0x18c/0x7f4
>   |  pinctrl_get+0x90/0x114
>   |  devm_pinctrl_get+0x44/0x98
>   |  pinctrl_bind_pins+0x5c/0x450
>   |  really_probe+0x1c8/0x9a4
>   |  driver_probe_device+0x120/0x1d8
> 
> Follow the example of sysfs, and duplicate the device name string before
> stashing it away in the pinctrl mapping entries.
> 
> Cc: Linus Walleij <linus.walleij@linaro.org>
> Reported-by: Elena Petrova <lenaptr@google.com>
> Tested-by: Elena Petrova <lenaptr@google.com>
> Signed-off-by: Will Deacon <will@kernel.org>
> Link: https://lore.kernel.org/r/20191002124206.22928-1-will@kernel.org
> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
> (backported from commit be4c60b563edee3712d392aaeb0943a768df7023)
> [ kmously: Extra dev_err() in dt_remember_or_free_map() required
>  manual merging ]
> Signed-off-by: Khalid Elmously <khalid.elmously@canonical.com>
> ---
>  drivers/pinctrl/devicetree.c | 22 +++++++++++++++++++---
>  1 file changed, 19 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/pinctrl/devicetree.c b/drivers/pinctrl/devicetree.c
> index fe04e748dfe4b..74eebcbd4e057 100644
> --- a/drivers/pinctrl/devicetree.c
> +++ b/drivers/pinctrl/devicetree.c
> @@ -40,6 +40,13 @@ struct pinctrl_dt_map {
>  static void dt_free_map(struct pinctrl_dev *pctldev,
>  		     struct pinctrl_map *map, unsigned num_maps)
>  {
> +	int i;
> +
> +	for (i = 0; i < num_maps; ++i) {
> +		kfree_const(map[i].dev_name);
> +		map[i].dev_name = NULL;
> +	}
> +
>  	if (pctldev) {
>  		const struct pinctrl_ops *ops = pctldev->desc->pctlops;
>  		ops->dt_free_map(pctldev, map, num_maps);
> @@ -73,7 +80,13 @@ static int dt_remember_or_free_map(struct pinctrl *p, const char *statename,
>  
>  	/* Initialize common mapping table entry fields */
>  	for (i = 0; i < num_maps; i++) {
> -		map[i].dev_name = dev_name(p->dev);
> +		const char *devname;
> +
> +		devname = kstrdup_const(dev_name(p->dev), GFP_KERNEL);
> +		if (!devname)
> +			goto err_free_map;
> +
> +		map[i].dev_name = devname;
>  		map[i].name = statename;
>  		if (pctldev)
>  			map[i].ctrl_dev_name = dev_name(pctldev->dev);
> @@ -83,8 +96,7 @@ static int dt_remember_or_free_map(struct pinctrl *p, const char *statename,
>  	dt_map = kzalloc(sizeof(*dt_map), GFP_KERNEL);
>  	if (!dt_map) {
>  		dev_err(p->dev, "failed to alloc struct pinctrl_dt_map\n");
> -		dt_free_map(pctldev, map, num_maps);
> -		return -ENOMEM;
> +		goto err_free_map;
>  	}
>  
>  	dt_map->pctldev = pctldev;
> @@ -93,6 +105,10 @@ static int dt_remember_or_free_map(struct pinctrl *p, const char *statename,
>  	list_add_tail(&dt_map->node, &p->dt_maps);
>  
>  	return pinctrl_register_map(map, num_maps, false);
> +
> +err_free_map:
> +	dt_free_map(pctldev, map, num_maps);
> +	return -ENOMEM;
>  }
>  
>  struct pinctrl_dev *of_pinctrl_get(struct device_node *np)
> -- 
> 2.17.1
> 
> 
> -- 
> kernel-team mailing list
> kernel-team@lists.ubuntu.com
> https://lists.ubuntu.com/mailman/listinfo/kernel-team
diff mbox series

Patch

diff --git a/drivers/pinctrl/devicetree.c b/drivers/pinctrl/devicetree.c
index fe04e748dfe4b..74eebcbd4e057 100644
--- a/drivers/pinctrl/devicetree.c
+++ b/drivers/pinctrl/devicetree.c
@@ -40,6 +40,13 @@  struct pinctrl_dt_map {
 static void dt_free_map(struct pinctrl_dev *pctldev,
 		     struct pinctrl_map *map, unsigned num_maps)
 {
+	int i;
+
+	for (i = 0; i < num_maps; ++i) {
+		kfree_const(map[i].dev_name);
+		map[i].dev_name = NULL;
+	}
+
 	if (pctldev) {
 		const struct pinctrl_ops *ops = pctldev->desc->pctlops;
 		ops->dt_free_map(pctldev, map, num_maps);
@@ -73,7 +80,13 @@  static int dt_remember_or_free_map(struct pinctrl *p, const char *statename,
 
 	/* Initialize common mapping table entry fields */
 	for (i = 0; i < num_maps; i++) {
-		map[i].dev_name = dev_name(p->dev);
+		const char *devname;
+
+		devname = kstrdup_const(dev_name(p->dev), GFP_KERNEL);
+		if (!devname)
+			goto err_free_map;
+
+		map[i].dev_name = devname;
 		map[i].name = statename;
 		if (pctldev)
 			map[i].ctrl_dev_name = dev_name(pctldev->dev);
@@ -83,8 +96,7 @@  static int dt_remember_or_free_map(struct pinctrl *p, const char *statename,
 	dt_map = kzalloc(sizeof(*dt_map), GFP_KERNEL);
 	if (!dt_map) {
 		dev_err(p->dev, "failed to alloc struct pinctrl_dt_map\n");
-		dt_free_map(pctldev, map, num_maps);
-		return -ENOMEM;
+		goto err_free_map;
 	}
 
 	dt_map->pctldev = pctldev;
@@ -93,6 +105,10 @@  static int dt_remember_or_free_map(struct pinctrl *p, const char *statename,
 	list_add_tail(&dt_map->node, &p->dt_maps);
 
 	return pinctrl_register_map(map, num_maps, false);
+
+err_free_map:
+	dt_free_map(pctldev, map, num_maps);
+	return -ENOMEM;
 }
 
 struct pinctrl_dev *of_pinctrl_get(struct device_node *np)