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/thunderbolt/ctl.h | |
| 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/thunderbolt/ctl.h')
| -rw-r--r-- | drivers/thunderbolt/ctl.h | 144 |
1 files changed, 144 insertions, 0 deletions
diff --git a/drivers/thunderbolt/ctl.h b/drivers/thunderbolt/ctl.h new file mode 100644 index 000000000..db1646eb4 --- /dev/null +++ b/drivers/thunderbolt/ctl.h @@ -0,0 +1,144 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Thunderbolt driver - control channel and configuration commands + * + * Copyright (c) 2014 Andreas Noever <andreas.noever@gmail.com> + * Copyright (C) 2018, Intel Corporation + */ + +#ifndef _TB_CFG +#define _TB_CFG + +#include <linux/kref.h> +#include <linux/thunderbolt.h> + +#include "nhi.h" +#include "tb_msgs.h" + +/* control channel */ +struct tb_ctl; + +typedef bool (*event_cb)(void *data, enum tb_cfg_pkg_type type, + const void *buf, size_t size); + +struct tb_ctl *tb_ctl_alloc(struct tb_nhi *nhi, int index, int timeout_msec, + event_cb cb, void *cb_data); +void tb_ctl_start(struct tb_ctl *ctl); +void tb_ctl_stop(struct tb_ctl *ctl); +void tb_ctl_free(struct tb_ctl *ctl); + +/* configuration commands */ + +struct tb_cfg_result { + u64 response_route; + u32 response_port; /* + * If err = 1 then this is the port that send the + * error. + * If err = 0 and if this was a cfg_read/write then + * this is the upstream port of the responding + * switch. + * Otherwise the field is set to zero. + */ + int err; /* negative errors, 0 for success, 1 for tb errors */ + enum tb_cfg_error tb_error; /* valid if err == 1 */ +}; + +struct ctl_pkg { + struct tb_ctl *ctl; + void *buffer; + struct ring_frame frame; +}; + +/** + * struct tb_cfg_request - Control channel request + * @kref: Reference count + * @ctl: Pointer to the control channel structure. Only set when the + * request is queued. + * @request: Request is stored here + * @request_size: Size of the request packet (in bytes) + * @request_type: Type of the request packet + * @response: Response is stored here + * @response_size: Maximum size of one response packet + * @response_type: Expected type of the response packet + * @npackets: Number of packets expected to be returned with this request + * @match: Function used to match the incoming packet + * @copy: Function used to copy the incoming packet to @response + * @callback: Callback called when the request is finished successfully + * @callback_data: Data to be passed to @callback + * @flags: Flags for the request + * @work: Work item used to complete the request + * @result: Result after the request has been completed + * @list: Requests are queued using this field + * + * An arbitrary request over Thunderbolt control channel. For standard + * control channel message, one should use tb_cfg_read/write() and + * friends if possible. + */ +struct tb_cfg_request { + struct kref kref; + struct tb_ctl *ctl; + const void *request; + size_t request_size; + enum tb_cfg_pkg_type request_type; + void *response; + size_t response_size; + enum tb_cfg_pkg_type response_type; + size_t npackets; + bool (*match)(const struct tb_cfg_request *req, + const struct ctl_pkg *pkg); + bool (*copy)(struct tb_cfg_request *req, const struct ctl_pkg *pkg); + void (*callback)(void *callback_data); + void *callback_data; + unsigned long flags; + struct work_struct work; + struct tb_cfg_result result; + struct list_head list; +}; + +#define TB_CFG_REQUEST_ACTIVE 0 +#define TB_CFG_REQUEST_CANCELED 1 + +struct tb_cfg_request *tb_cfg_request_alloc(void); +void tb_cfg_request_get(struct tb_cfg_request *req); +void tb_cfg_request_put(struct tb_cfg_request *req); +int tb_cfg_request(struct tb_ctl *ctl, struct tb_cfg_request *req, + void (*callback)(void *), void *callback_data); +void tb_cfg_request_cancel(struct tb_cfg_request *req, int err); +struct tb_cfg_result tb_cfg_request_sync(struct tb_ctl *ctl, + struct tb_cfg_request *req, int timeout_msec); + +static inline u64 tb_cfg_get_route(const struct tb_cfg_header *header) +{ + return (u64) header->route_hi << 32 | header->route_lo; +} + +static inline struct tb_cfg_header tb_cfg_make_header(u64 route) +{ + struct tb_cfg_header header = { + .route_hi = route >> 32, + .route_lo = route, + }; + /* check for overflow, route_hi is not 32 bits! */ + WARN_ON(tb_cfg_get_route(&header) != route); + return header; +} + +int tb_cfg_ack_notification(struct tb_ctl *ctl, u64 route, + const struct cfg_error_pkg *error); +int tb_cfg_ack_plug(struct tb_ctl *ctl, u64 route, u32 port, bool unplug); +struct tb_cfg_result tb_cfg_reset(struct tb_ctl *ctl, u64 route); +struct tb_cfg_result tb_cfg_read_raw(struct tb_ctl *ctl, void *buffer, + u64 route, u32 port, + enum tb_cfg_space space, u32 offset, + u32 length, int timeout_msec); +struct tb_cfg_result tb_cfg_write_raw(struct tb_ctl *ctl, const void *buffer, + u64 route, u32 port, + enum tb_cfg_space space, u32 offset, + u32 length, int timeout_msec); +int tb_cfg_read(struct tb_ctl *ctl, void *buffer, u64 route, u32 port, + enum tb_cfg_space space, u32 offset, u32 length); +int tb_cfg_write(struct tb_ctl *ctl, const void *buffer, u64 route, u32 port, + enum tb_cfg_space space, u32 offset, u32 length); +int tb_cfg_get_upstream_port(struct tb_ctl *ctl, u64 route); + +#endif |
