diff options
| author | Ali Ahmet Memis <ali@iusegentoo.com> | 2026-08-21 01:45:27 +0000 |
|---|---|---|
| committer | Stafford Horne <shorne@gmail.com> | 2026-08-29 07:32:26 +0100 |
| commit | 78004e9a87f240df03e2f73120d291763c32e0a7 (patch) | |
| tree | 137c22dd795222931b9d2bf2501d37cd766741aa /drivers/reset/reset-tn48m.c | |
| download | linux-stable-78004e9a87f240df03e2f73120d291763c32e0a7.tar.gz linux-stable-78004e9a87f240df03e2f73120d291763c32e0a7.zip | |
openrisc: fix arbitrary kernel memory access via or1k_atomic syscallgrafted
sys_or1k_atomic() (syscall 244 in the "or1k" ABI) takes two user
pointers, v1 and v2, and swaps the words they point to in hand-written
assembly.
l.lwz r29,0(r4)
l.lwz r27,0(r5)
l.sw 0(r4),r27
l.sw 0(r5),r29
The pointers are not checked with access_ok(). The four memory
accesses also have no exception table entries.
A caller passes a kernel address as either pointer, and the syscall
reads from and writes to it directly.
This gives an unprivileged process a kernel read/write primitive. It
overwrites kernel data such as the sys_call_table, gaining code
execution in kernel context.
Check both pointers before entering the critical section. Add fixups
for the four memory accesses so faults on valid but unmapped user
addresses return -EFAULT.
[shorne@gmail.com: fix comment style]
Fixes: 9d02a4283e9c ("OpenRISC: Boot code")
Cc: stable@vger.kernel.org
Signed-off-by: Ali Ahmet Memis <ali@iusegentoo.com>
Signed-off-by: Stafford Horne <shorne@gmail.com>
Diffstat (limited to 'drivers/reset/reset-tn48m.c')
| -rw-r--r-- | drivers/reset/reset-tn48m.c | 127 |
1 files changed, 127 insertions, 0 deletions
diff --git a/drivers/reset/reset-tn48m.c b/drivers/reset/reset-tn48m.c new file mode 100644 index 000000000..177ee5127 --- /dev/null +++ b/drivers/reset/reset-tn48m.c @@ -0,0 +1,127 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Delta TN48M CPLD reset driver + * + * Copyright (C) 2021 Sartura Ltd. + * + * Author: Robert Marko <robert.marko@sartura.hr> + */ + +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/reset-controller.h> + +#include <dt-bindings/reset/delta,tn48m-reset.h> + +#define TN48M_RESET_REG 0x10 + +#define TN48M_RESET_TIMEOUT_US 125000 +#define TN48M_RESET_SLEEP_US 10 + +struct tn48_reset_map { + u8 bit; +}; + +struct tn48_reset_data { + struct reset_controller_dev rcdev; + struct regmap *regmap; +}; + +static const struct tn48_reset_map tn48m_resets[] = { + [CPU_88F7040_RESET] = {0}, + [CPU_88F6820_RESET] = {1}, + [MAC_98DX3265_RESET] = {2}, + [PHY_88E1680_RESET] = {4}, + [PHY_88E1512_RESET] = {6}, + [POE_RESET] = {7}, +}; + +static inline struct tn48_reset_data *to_tn48_reset_data( + struct reset_controller_dev *rcdev) +{ + return container_of(rcdev, struct tn48_reset_data, rcdev); +} + +static int tn48m_control_reset(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct tn48_reset_data *data = to_tn48_reset_data(rcdev); + unsigned int val; + + regmap_update_bits(data->regmap, TN48M_RESET_REG, + BIT(tn48m_resets[id].bit), 0); + + return regmap_read_poll_timeout(data->regmap, + TN48M_RESET_REG, + val, + val & BIT(tn48m_resets[id].bit), + TN48M_RESET_SLEEP_US, + TN48M_RESET_TIMEOUT_US); +} + +static int tn48m_control_status(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct tn48_reset_data *data = to_tn48_reset_data(rcdev); + unsigned int regval; + int ret; + + ret = regmap_read(data->regmap, TN48M_RESET_REG, ®val); + if (ret < 0) + return ret; + + if (BIT(tn48m_resets[id].bit) & regval) + return 0; + else + return 1; +} + +static const struct reset_control_ops tn48_reset_ops = { + .reset = tn48m_control_reset, + .status = tn48m_control_status, +}; + +static int tn48m_reset_probe(struct platform_device *pdev) +{ + struct tn48_reset_data *data; + struct regmap *regmap; + + regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!regmap) + return -ENODEV; + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->regmap = regmap; + + data->rcdev.owner = THIS_MODULE; + data->rcdev.ops = &tn48_reset_ops; + data->rcdev.nr_resets = ARRAY_SIZE(tn48m_resets); + data->rcdev.of_node = pdev->dev.of_node; + + return devm_reset_controller_register(&pdev->dev, &data->rcdev); +} + +static const struct of_device_id tn48m_reset_of_match[] = { + { .compatible = "delta,tn48m-reset" }, + { } +}; +MODULE_DEVICE_TABLE(of, tn48m_reset_of_match); + +static struct platform_driver tn48m_reset_driver = { + .driver = { + .name = "delta-tn48m-reset", + .of_match_table = tn48m_reset_of_match, + }, + .probe = tn48m_reset_probe, +}; +module_platform_driver(tn48m_reset_driver); + +MODULE_AUTHOR("Robert Marko <robert.marko@sartura.hr>"); +MODULE_DESCRIPTION("Delta TN48M CPLD reset driver"); +MODULE_LICENSE("GPL"); |
