diff options
| author | Xie Yuanbin <xieyuanbin1@huawei.com> | 2026-07-28 03:16:42 +0100 |
|---|---|---|
| committer | Russell King <rmk+kernel@armlinux.org.uk> | 2026-08-05 16:15:51 +0100 |
| commit | 1039bffd6ae9c75b42b7d148d6c1106134107b66 (patch) | |
| tree | e8ee4ac80f87e2ad1b6b0c877eccbb62b0915c02 /drivers/pinctrl/pinctrl-utils.c | |
| download | linux-stable-1039bffd6ae9c75b42b7d148d6c1106134107b66.tar.gz linux-stable-1039bffd6ae9c75b42b7d148d6c1106134107b66.zip | |
ARM: 9485/1: mm: acquire mmap write lock around show_pte() for user faultsgrafted
When CONFIG_DEBUG_USER=y, and cmdline "user_debug=31" is set,
a user fault may trigger show_pte() without any lock.
If another thread in the same process concurrently calls munmap(),
the page table pages may be freed while show_pte() is still traversing
them, causing a use-after-free in show_pte().
If CONFIG_ARM_LPAE=y, this may cause a kernel panic if the pages table
of PMD are freed when show_pte() is running.
Acquire mmap_write_lock() around show_pte() for user faults to fix the
contention.
For user faults, additionally restrict that show_pte() is called only
when the addr is a user-space address (addr < TASK_SIZE). This is because
the lock of tsk->mm only protects the virtual memory of user address space,
furthermore, dumping the page tables of a kernel-space address for user
faults is unnecessary and may have security implications.
Keep everything unchanged for kernel faults, because the kernel is
already in the "oops" state, acquiring a lock may risk a deadlock.
Co-developed-by: Qi Xi <xiqi2@huawei.com>
Fixes: 6d021b724481 ("ARM: dump pgd, pmd and pte states on unhandled data abort faults")
Link: https://lore.kernel.org/20260716014022.2823-1-xieyuanbin1@huawei.com
Acked-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
Reviewed-by: Linus Walleij <linusw@kernel.org>
Signed-off-by: Qi Xi <xiqi2@huawei.com>
Signed-off-by: Xie Yuanbin <xieyuanbin1@huawei.com>
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
Diffstat (limited to 'drivers/pinctrl/pinctrl-utils.c')
| -rw-r--r-- | drivers/pinctrl/pinctrl-utils.c | 129 |
1 files changed, 129 insertions, 0 deletions
diff --git a/drivers/pinctrl/pinctrl-utils.c b/drivers/pinctrl/pinctrl-utils.c new file mode 100644 index 000000000..b880e44b8 --- /dev/null +++ b/drivers/pinctrl/pinctrl-utils.c @@ -0,0 +1,129 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Utils functions to implement the pincontrol driver. + * + * Copyright (c) 2013, NVIDIA Corporation. + * + * Author: Laxman Dewangan <ldewangan@nvidia.com> + */ +#include <linux/array_size.h> +#include <linux/device.h> +#include <linux/export.h> +#include <linux/of.h> +#include <linux/slab.h> + +#include <linux/pinctrl/pinctrl.h> + +#include "core.h" +#include "pinctrl-utils.h" + +int pinctrl_utils_reserve_map(struct pinctrl_dev *pctldev, + struct pinctrl_map **map, unsigned int *reserved_maps, + unsigned int *num_maps, unsigned int reserve) +{ + unsigned int old_num = *reserved_maps; + unsigned int new_num = *num_maps + reserve; + struct pinctrl_map *new_map; + + if (old_num >= new_num) + return 0; + + new_map = krealloc_array(*map, new_num, sizeof(*new_map), GFP_KERNEL); + if (!new_map) { + dev_err(pctldev->dev, "krealloc(map) failed\n"); + return -ENOMEM; + } + + memset(new_map + old_num, 0, (new_num - old_num) * sizeof(*new_map)); + + *map = new_map; + *reserved_maps = new_num; + return 0; +} +EXPORT_SYMBOL_GPL(pinctrl_utils_reserve_map); + +int pinctrl_utils_add_map_mux(struct pinctrl_dev *pctldev, + struct pinctrl_map **map, unsigned int *reserved_maps, + unsigned int *num_maps, const char *group, + const char *function) +{ + if (WARN_ON(*num_maps == *reserved_maps)) + return -ENOSPC; + + (*map)[*num_maps].type = PIN_MAP_TYPE_MUX_GROUP; + (*map)[*num_maps].data.mux.group = group; + (*map)[*num_maps].data.mux.function = function; + (*num_maps)++; + + return 0; +} +EXPORT_SYMBOL_GPL(pinctrl_utils_add_map_mux); + +int pinctrl_utils_add_map_configs(struct pinctrl_dev *pctldev, + struct pinctrl_map **map, unsigned int *reserved_maps, + unsigned int *num_maps, const char *group, + unsigned long *configs, unsigned int num_configs, + enum pinctrl_map_type type) +{ + unsigned long *dup_configs; + + if (WARN_ON(*num_maps == *reserved_maps)) + return -ENOSPC; + + dup_configs = kmemdup_array(configs, num_configs, + sizeof(*dup_configs), GFP_KERNEL); + if (!dup_configs) + return -ENOMEM; + + (*map)[*num_maps].type = type; + (*map)[*num_maps].data.configs.group_or_pin = group; + (*map)[*num_maps].data.configs.configs = dup_configs; + (*map)[*num_maps].data.configs.num_configs = num_configs; + (*num_maps)++; + + return 0; +} +EXPORT_SYMBOL_GPL(pinctrl_utils_add_map_configs); + +int pinctrl_utils_add_config(struct pinctrl_dev *pctldev, + unsigned long **configs, unsigned int *num_configs, + unsigned long config) +{ + unsigned int old_num = *num_configs; + unsigned int new_num = old_num + 1; + unsigned long *new_configs; + + new_configs = krealloc(*configs, sizeof(*new_configs) * new_num, + GFP_KERNEL); + if (!new_configs) { + dev_err(pctldev->dev, "krealloc(configs) failed\n"); + return -ENOMEM; + } + + new_configs[old_num] = config; + + *configs = new_configs; + *num_configs = new_num; + + return 0; +} +EXPORT_SYMBOL_GPL(pinctrl_utils_add_config); + +void pinctrl_utils_free_map(struct pinctrl_dev *pctldev, + struct pinctrl_map *map, unsigned int num_maps) +{ + int i; + + for (i = 0; i < num_maps; i++) { + switch (map[i].type) { + case PIN_MAP_TYPE_CONFIGS_GROUP: + case PIN_MAP_TYPE_CONFIGS_PIN: + kfree(map[i].data.configs.configs); + break; + default: + break; + } + } + kfree(map); +} +EXPORT_SYMBOL_GPL(pinctrl_utils_free_map); |
