From 387a926ee166814611acecb960207fe2f3c4fd3e Mon Sep 17 00:00:00 2001 From: Amirreza Zarrabi Date: Mon, 16 Feb 2026 14:24:06 -0800 Subject: [PATCH 001/337] tee: optee: prevent use-after-free when the client exits before the supplicant Commit 70b0d6b0a199 ("tee: optee: Fix supplicant wait loop") made the client wait as killable so it can be interrupted during shutdown or after a supplicant crash. This changes the original lifetime expectations: the client task can now terminate while the supplicant is still processing its request. If the client exits first it removes the request from its queue and kfree()s it, while the request ID remains in supp->idr. A subsequent lookup on the supplicant path then dereferences freed memory, leading to a use-after-free. Serialise access to the request with supp->mutex: * Hold supp->mutex in optee_supp_recv() and optee_supp_send() while looking up and touching the request. * Let optee_supp_thrd_req() notice that the client has terminated and signal optee_supp_send() accordingly. With these changes the request cannot be freed while the supplicant still has a reference, eliminating the race. Fixes: 70b0d6b0a199 ("tee: optee: Fix supplicant wait loop") Signed-off-by: Amirreza Zarrabi Tested-by: Ox Yeh Reviewed-by: Sumit Garg Signed-off-by: Jens Wiklander --- drivers/tee/optee/supp.c | 107 +++++++++++++++++++++++++++------------ 1 file changed, 74 insertions(+), 33 deletions(-) diff --git a/drivers/tee/optee/supp.c b/drivers/tee/optee/supp.c index a3d11b1f90fa..06747e90c230 100644 --- a/drivers/tee/optee/supp.c +++ b/drivers/tee/optee/supp.c @@ -10,7 +10,11 @@ struct optee_supp_req { struct list_head link; + int id; + bool in_queue; + bool processed; + u32 func; u32 ret; size_t num_params; @@ -19,6 +23,9 @@ struct optee_supp_req { struct completion c; }; +/* It is temporary request used for revoked pending request in supp->idr. */ +#define INVALID_REQ_PTR ((struct optee_supp_req *)ERR_PTR(-EBADF)) + void optee_supp_init(struct optee_supp *supp) { memset(supp, 0, sizeof(*supp)); @@ -39,21 +46,23 @@ void optee_supp_release(struct optee_supp *supp) { int id; struct optee_supp_req *req; - struct optee_supp_req *req_tmp; mutex_lock(&supp->mutex); - /* Abort all request retrieved by supplicant */ + /* Abort all request */ idr_for_each_entry(&supp->idr, req, id) { idr_remove(&supp->idr, id); - req->ret = TEEC_ERROR_COMMUNICATION; - complete(&req->c); - } + /* Skip if request was already marked invalid */ + if (IS_ERR(req)) + continue; - /* Abort all queued requests */ - list_for_each_entry_safe(req, req_tmp, &supp->reqs, link) { - list_del(&req->link); - req->in_queue = false; + /* For queued requests where supplicant has not seen it */ + if (req->in_queue) { + list_del(&req->link); + req->in_queue = false; + } + + req->processed = true; req->ret = TEEC_ERROR_COMMUNICATION; complete(&req->c); } @@ -100,8 +109,16 @@ u32 optee_supp_thrd_req(struct tee_context *ctx, u32 func, size_t num_params, /* Insert the request in the request list */ mutex_lock(&supp->mutex); + req->id = idr_alloc(&supp->idr, req, 1, 0, GFP_KERNEL); + if (req->id < 0) { + mutex_unlock(&supp->mutex); + kfree(req); + return TEEC_ERROR_OUT_OF_MEMORY; + } + list_add_tail(&req->link, &supp->reqs); req->in_queue = true; + req->processed = false; mutex_unlock(&supp->mutex); /* Tell an eventual waiter there's a new request */ @@ -117,21 +134,43 @@ u32 optee_supp_thrd_req(struct tee_context *ctx, u32 func, size_t num_params, if (wait_for_completion_killable(&req->c)) { mutex_lock(&supp->mutex); if (req->in_queue) { + /* Supplicant has not seen this request yet. */ + idr_remove(&supp->idr, req->id); list_del(&req->link); req->in_queue = false; + + ret = TEEC_ERROR_COMMUNICATION; + } else if (req->processed) { + /* + * Supplicant has processed this request. Ignore the + * kill signal for now and submit the result. req is not + * in supp->reqs (removed by supp_pop_entry()) nor in + * supp->idr (removed by supp_pop_req()). + */ + ret = req->ret; + } else { + /* + * Supplicant is in the middle of processing this + * request. Replace req with INVALID_REQ_PTR so that + * the ID remains busy, causing optee_supp_send() to + * fail on the next call to supp_pop_req() with this ID. + */ + idr_replace(&supp->idr, INVALID_REQ_PTR, req->id); + ret = TEEC_ERROR_COMMUNICATION; } + mutex_unlock(&supp->mutex); - req->ret = TEEC_ERROR_COMMUNICATION; + } else { + ret = req->ret; } - ret = req->ret; kfree(req); return ret; } static struct optee_supp_req *supp_pop_entry(struct optee_supp *supp, - int num_params, int *id) + int num_params) { struct optee_supp_req *req; @@ -153,10 +192,6 @@ static struct optee_supp_req *supp_pop_entry(struct optee_supp *supp, return ERR_PTR(-EINVAL); } - *id = idr_alloc(&supp->idr, req, 1, 0, GFP_KERNEL); - if (*id < 0) - return ERR_PTR(-ENOMEM); - list_del(&req->link); req->in_queue = false; @@ -214,7 +249,6 @@ int optee_supp_recv(struct tee_context *ctx, u32 *func, u32 *num_params, struct optee *optee = tee_get_drvdata(teedev); struct optee_supp *supp = &optee->supp; struct optee_supp_req *req = NULL; - int id; size_t num_meta; int rc; @@ -224,15 +258,11 @@ int optee_supp_recv(struct tee_context *ctx, u32 *func, u32 *num_params, while (true) { mutex_lock(&supp->mutex); - req = supp_pop_entry(supp, *num_params - num_meta, &id); + req = supp_pop_entry(supp, *num_params - num_meta); + if (req) + break; /* Keep mutex held. */ mutex_unlock(&supp->mutex); - if (req) { - if (IS_ERR(req)) - return PTR_ERR(req); - break; - } - /* * If we didn't get a request we'll block in * wait_for_completion() to avoid needless spinning. @@ -245,6 +275,13 @@ int optee_supp_recv(struct tee_context *ctx, u32 *func, u32 *num_params, return -ERESTARTSYS; } + /* supp->mutex held and req != NULL. */ + + if (IS_ERR(req)) { + mutex_unlock(&supp->mutex); + return PTR_ERR(req); + } + if (num_meta) { /* * tee-supplicant support meta parameters -> requsts can be @@ -252,13 +289,11 @@ int optee_supp_recv(struct tee_context *ctx, u32 *func, u32 *num_params, */ param->attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT | TEE_IOCTL_PARAM_ATTR_META; - param->u.value.a = id; + param->u.value.a = req->id; param->u.value.b = 0; param->u.value.c = 0; } else { - mutex_lock(&supp->mutex); - supp->req_id = id; - mutex_unlock(&supp->mutex); + supp->req_id = req->id; } *func = req->func; @@ -266,6 +301,7 @@ int optee_supp_recv(struct tee_context *ctx, u32 *func, u32 *num_params, memcpy(param + num_meta, req->param, sizeof(struct tee_param) * req->num_params); + mutex_unlock(&supp->mutex); return 0; } @@ -297,12 +333,17 @@ static struct optee_supp_req *supp_pop_req(struct optee_supp *supp, if (!req) return ERR_PTR(-ENOENT); + /* optee_supp_thrd_req() already returned to optee. */ + if (IS_ERR(req)) + goto failed_req; + if ((num_params - nm) != req->num_params) return ERR_PTR(-EINVAL); + *num_meta = nm; +failed_req: idr_remove(&supp->idr, id); supp->req_id = -1; - *num_meta = nm; return req; } @@ -328,10 +369,9 @@ int optee_supp_send(struct tee_context *ctx, u32 ret, u32 num_params, mutex_lock(&supp->mutex); req = supp_pop_req(supp, num_params, param, &num_meta); - mutex_unlock(&supp->mutex); - if (IS_ERR(req)) { - /* Something is wrong, let supplicant restart. */ + mutex_unlock(&supp->mutex); + /* Something is wrong, let supplicant handel it. */ return PTR_ERR(req); } @@ -355,9 +395,10 @@ int optee_supp_send(struct tee_context *ctx, u32 ret, u32 num_params, } } req->ret = ret; - + req->processed = true; /* Let the requesting thread continue */ complete(&req->c); + mutex_unlock(&supp->mutex); return 0; } From 96fe420bebc159599fb8da1080e9ff207bdb650a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jingle=20Wu=20=E5=90=B3=E9=87=91=E5=9C=8B?= Date: Tue, 21 Apr 2026 07:00:10 +0000 Subject: [PATCH 002/337] Input: elan_i2c - add ic type 0x19 The 0x19 is valid 3000 serial ic type too. Signed-off-by: Jingle Wu Link: https://patch.msgid.link/KL1PR01MB511699853D1B66D137C06806DC2C2@KL1PR01MB5116.apcprd01.prod.exchangelabs.com Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/elan_i2c_core.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/input/mouse/elan_i2c_core.c b/drivers/input/mouse/elan_i2c_core.c index fee1796da3d0..7475803c6ce4 100644 --- a/drivers/input/mouse/elan_i2c_core.c +++ b/drivers/input/mouse/elan_i2c_core.c @@ -162,6 +162,9 @@ static int elan_get_fwinfo(u16 ic_type, u8 iap_version, u16 *validpage_count, case 0x15: *validpage_count = 1024; break; + case 0x19: + *validpage_count = 2032; + break; default: /* unknown ic type clear value */ *validpage_count = 0; From 8f9d6cd6d3916add4c47a9dd1622e4fc057f877b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jingle=20Wu=20=E5=90=B3=E9=87=91=E5=9C=8B?= Date: Tue, 21 Apr 2026 07:02:33 +0000 Subject: [PATCH 003/337] Input: elan_i2c - increase device reset wait timeout after update FW Extend wait_for_completion_timeout from 300ms to 700ms to ensure sufficient time for device reset after firmware update. Signed-off-by: Jingle Wu Link: https://patch.msgid.link/KL1PR01MB5116031986614B3214EF2F30DC2C2@KL1PR01MB5116.apcprd01.prod.exchangelabs.com Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/elan_i2c_i2c.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/mouse/elan_i2c_i2c.c b/drivers/input/mouse/elan_i2c_i2c.c index a9057d124a88..88d4070d4b44 100644 --- a/drivers/input/mouse/elan_i2c_i2c.c +++ b/drivers/input/mouse/elan_i2c_i2c.c @@ -690,7 +690,7 @@ static int elan_i2c_finish_fw_update(struct i2c_client *client, if (error) { dev_err(dev, "device reset failed: %d\n", error); } else if (!wait_for_completion_timeout(completion, - msecs_to_jiffies(300))) { + msecs_to_jiffies(700))) { dev_err(dev, "timeout waiting for device reset\n"); error = -ETIMEDOUT; } From 2905281cbda52ec9df540113b35b835feb5fafd3 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 20 Apr 2026 18:00:27 +0200 Subject: [PATCH 004/337] Input: usbtouchscreen - clamp NEXIO data_len/x_len to URB buffer size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit nexio_read_data() pulls data_len and x_len from a packed __be16 header in the device's interrupt packet and then walks packet->data[0..x_len) and packet->data[x_len..data_len) comparing each byte against a threshold. Both fields are 16-bit on the wire (max 65535). The existing adjustments shave at most 0x100 / 0x80 off, so the loop bound can still reach roughly 0xfeff. The URB transfer buffer for NEXIO is rept_size (1024) bytes from usb_alloc_coherent(), with the first 7 occupied by the packed header — so packet->data[] has 1017 valid bytes. read_data() callbacks are not given urb->actual_length, and nothing else bounds the walk. A device that lies about its length can get a ~64 KiB out-of-bounds read past the coherent DMA allocation. The first index whose byte exceeds NEXIO_THRESHOLD lands in begin_x / begin_y and from there into the reported touch coordinates, so adjacent kernel memory contents leak to userspace as ABS_X / ABS_Y events. Far enough out, the read can also hit an unmapped page and fault. Fix this all by clamping data_len to the buffer's data[] capacity and x_len to data_len. Cc: Dmitry Torokhov Fixes: 5197424cdccc ("Input: usbtouchscreen - add NEXIO (or iNexio) support") Cc: stable Assisted-by: gkh_clanker_t1000 Signed-off-by: Greg Kroah-Hartman Link: https://patch.msgid.link/2026042026-chlorine-epidermis-fd6d@gregkh Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/usbtouchscreen.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/input/touchscreen/usbtouchscreen.c b/drivers/input/touchscreen/usbtouchscreen.c index daa28135f887..0bbacb517c28 100644 --- a/drivers/input/touchscreen/usbtouchscreen.c +++ b/drivers/input/touchscreen/usbtouchscreen.c @@ -1067,6 +1067,11 @@ static int nexio_read_data(struct usbtouch_usb *usbtouch, unsigned char *pkt) if (x_len > 0xff) x_len -= 0x80; + if (data_len > usbtouch->data_size - sizeof(*packet)) + data_len = usbtouch->data_size - sizeof(*packet); + if (x_len > data_len) + x_len = data_len; + /* send ACK */ ret = usb_submit_urb(priv->ack, GFP_ATOMIC); if (ret) From 6cdc46b38cf146ce81d4831b6472dbf7731849a2 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sun, 26 Apr 2026 21:09:33 -0700 Subject: [PATCH 005/337] Input: xpad - fix out-of-bounds access for Share button xpadone_process_packet() receives len directly from urb->actual_length and uses it to index the share-button byte at data[len - 18] or data[len - 26]. Since both len and data[0] are under the device's control, a broken controller can send a GIP_CMD_INPUT packet with actual_length < 18 (e.g. 5 bytes) and reach this code path, causing accesses beyond the actual array. Fix this by calculating the offset and checking bounds against the packet length. Reported-by: Greg Kroah-Hartman Fixes: 4ef46367073b ("Input: xpad - fix Share button on Xbox One controllers") Cc: stable@vger.kernel.org Signed-off-by: Dmitry Torokhov --- drivers/input/joystick/xpad.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c index 0549fdc5a985..19ce90da89e9 100644 --- a/drivers/input/joystick/xpad.c +++ b/drivers/input/joystick/xpad.c @@ -1077,10 +1077,10 @@ static void xpadone_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char input_report_key(dev, BTN_START, data[4] & BIT(2)); input_report_key(dev, BTN_SELECT, data[4] & BIT(3)); if (xpad->mapping & MAP_SHARE_BUTTON) { - if (xpad->mapping & MAP_SHARE_OFFSET) - input_report_key(dev, KEY_RECORD, data[len - 26] & BIT(0)); - else - input_report_key(dev, KEY_RECORD, data[len - 18] & BIT(0)); + u32 offset = (xpad->mapping & MAP_SHARE_OFFSET) ? 26 : 18; + + if (len >= offset) + input_report_key(dev, KEY_RECORD, data[len - offset] & BIT(0)); } /* buttons A,B,X,Y */ From 852534744c2d35626a604f128ff0b8ec12805591 Mon Sep 17 00:00:00 2001 From: Christofer Jonason Date: Wed, 4 Mar 2026 10:07:27 +0100 Subject: [PATCH 006/337] iio: adc: xilinx-xadc: Fix sequencer mode in postdisable for dual mux MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit xadc_postdisable() unconditionally sets the sequencer to continuous mode. For dual external multiplexer configurations this is incorrect: simultaneous sampling mode is required so that ADC-A samples through the mux on VAUX[0-7] while ADC-B simultaneously samples through the mux on VAUX[8-15]. In continuous mode only ADC-A is active, so VAUX[8-15] channels return incorrect data. Since postdisable is also called from xadc_probe() to set the initial idle state, the wrong sequencer mode is active from the moment the driver loads. The preenable path already uses xadc_get_seq_mode() which returns SIMULTANEOUS for dual mux. Fix postdisable to do the same. Fixes: bdc8cda1d010 ("iio:adc: Add Xilinx XADC driver") Cc: stable@vger.kernel.org Signed-off-by: Christofer Jonason Reviewed-by: Andy Shevchenko Reviewed-by: Nuno Sá Reviewed-by: Salih Erim Signed-off-by: Jonathan Cameron --- drivers/iio/adc/xilinx-xadc-core.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/iio/adc/xilinx-xadc-core.c b/drivers/iio/adc/xilinx-xadc-core.c index e257c1b94a5f..3980dfacbcd7 100644 --- a/drivers/iio/adc/xilinx-xadc-core.c +++ b/drivers/iio/adc/xilinx-xadc-core.c @@ -817,6 +817,7 @@ static int xadc_postdisable(struct iio_dev *indio_dev) { struct xadc *xadc = iio_priv(indio_dev); unsigned long scan_mask; + int seq_mode; int ret; int i; @@ -824,6 +825,12 @@ static int xadc_postdisable(struct iio_dev *indio_dev) for (i = 0; i < indio_dev->num_channels; i++) scan_mask |= BIT(indio_dev->channels[i].scan_index); + /* + * Use the correct sequencer mode for the idle state: simultaneous + * mode for dual external mux configurations, continuous otherwise. + */ + seq_mode = xadc_get_seq_mode(xadc, scan_mask); + /* Enable all channels and calibration */ ret = xadc_write_adc_reg(xadc, XADC_REG_SEQ(0), scan_mask & 0xffff); if (ret) @@ -834,11 +841,11 @@ static int xadc_postdisable(struct iio_dev *indio_dev) return ret; ret = xadc_update_adc_reg(xadc, XADC_REG_CONF1, XADC_CONF1_SEQ_MASK, - XADC_CONF1_SEQ_CONTINUOUS); + seq_mode); if (ret) return ret; - return xadc_power_adc_b(xadc, XADC_CONF1_SEQ_CONTINUOUS); + return xadc_power_adc_b(xadc, seq_mode); } static int xadc_preenable(struct iio_dev *indio_dev) From 673478bc29cf72010faaf293c1c8c667393335a0 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Thu, 2 Apr 2026 13:40:15 +0800 Subject: [PATCH 007/337] iio: chemical: mhz19b: reject oversized serial replies mhz19b_receive_buf() appends each serdev chunk into the fixed MHZ19B_CMD_SIZE receive buffer and advances buf_idx by len without checking that the chunk fits in the remaining space. A large callback can therefore overflow st->buf before the command path validates the reply. Reset the reply state before each command and reject oversized serial replies before copying them into the fixed buffer. When an oversized reply is detected, wake the waiter and report -EMSGSIZE instead of overwriting st->buf. Fixes: 4572a70b3681 ("iio: chemical: Add support for Winsen MHZ19B CO2 sensor") Cc: stable@vger.kernel.org Signed-off-by: Pengpeng Hou Acked-by: Gyeyoung Baek Signed-off-by: Jonathan Cameron --- drivers/iio/chemical/mhz19b.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/drivers/iio/chemical/mhz19b.c b/drivers/iio/chemical/mhz19b.c index 3c64154918b1..9d4cf432919e 100644 --- a/drivers/iio/chemical/mhz19b.c +++ b/drivers/iio/chemical/mhz19b.c @@ -52,6 +52,8 @@ struct mhz19b_state { struct completion buf_ready; u8 buf_idx; + bool buf_overflow; + /* * Serdev receive buffer. * When data is received from the MH-Z19B, @@ -106,6 +108,10 @@ static int mhz19b_serdev_cmd(struct iio_dev *indio_dev, int cmd, u16 arg) cmd_buf[8] = mhz19b_get_checksum(cmd_buf); /* Write buf to uart ctrl synchronously */ + st->buf_idx = 0; + st->buf_overflow = false; + reinit_completion(&st->buf_ready); + ret = serdev_device_write(serdev, cmd_buf, MHZ19B_CMD_SIZE, 0); if (ret < 0) return ret; @@ -121,6 +127,9 @@ static int mhz19b_serdev_cmd(struct iio_dev *indio_dev, int cmd, u16 arg) if (!ret) return -ETIMEDOUT; + if (st->buf_overflow) + return -EMSGSIZE; + if (st->buf[8] != mhz19b_get_checksum(st->buf)) { dev_err(dev, "checksum err"); return -EINVAL; @@ -240,6 +249,14 @@ static size_t mhz19b_receive_buf(struct serdev_device *serdev, { struct iio_dev *indio_dev = dev_get_drvdata(&serdev->dev); struct mhz19b_state *st = iio_priv(indio_dev); + size_t remaining = MHZ19B_CMD_SIZE - st->buf_idx; + + if (len > remaining) { + st->buf_idx = 0; + st->buf_overflow = true; + complete(&st->buf_ready); + return len; + } memcpy(st->buf + st->buf_idx, data, len); st->buf_idx += len; From b66f922f6a4fa92840f662fbcfeb4f8a0f774bcc Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Fri, 27 Mar 2026 20:27:54 +0800 Subject: [PATCH 008/337] iio: light: veml6070: Fix resource leak in probe error path The driver calls i2c_new_dummy_device() to create a dummy device, then calls i2c_smbus_write_byte(). If i2c_smbus_write_byte() fails and returns, the cleanup via devm_add_action_or_reset() was never registered, so the dummy device leaks. Switch to devm_i2c_new_dummy_device() which registers cleanup atomically with device creation, eliminating the error-path window. Fixes: 7501bff87c3e ("iio: light: veml6070: add action for i2c_unregister_device") Reviewed-by: Andy Shevchenko Signed-off-by: Felix Gu Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/light/veml6070.c | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/drivers/iio/light/veml6070.c b/drivers/iio/light/veml6070.c index 74d7246e5225..4bbd86d0cb46 100644 --- a/drivers/iio/light/veml6070.c +++ b/drivers/iio/light/veml6070.c @@ -245,13 +245,6 @@ static const struct iio_info veml6070_info = { .write_raw = veml6070_write_raw, }; -static void veml6070_i2c_unreg(void *p) -{ - struct veml6070_data *data = p; - - i2c_unregister_device(data->client2); -} - static int veml6070_probe(struct i2c_client *client) { struct veml6070_data *data; @@ -281,7 +274,8 @@ static int veml6070_probe(struct i2c_client *client) if (ret < 0) return ret; - data->client2 = i2c_new_dummy_device(client->adapter, VEML6070_ADDR_DATA_LSB); + data->client2 = devm_i2c_new_dummy_device(&client->dev, client->adapter, + VEML6070_ADDR_DATA_LSB); if (IS_ERR(data->client2)) return dev_err_probe(&client->dev, PTR_ERR(data->client2), "i2c device for second chip address failed\n"); @@ -292,10 +286,6 @@ static int veml6070_probe(struct i2c_client *client) if (ret < 0) return ret; - ret = devm_add_action_or_reset(&client->dev, veml6070_i2c_unreg, data); - if (ret < 0) - return ret; - return devm_iio_device_register(&client->dev, indio_dev); } From 1a772719318c11e146f6fbe621fffd230a6f456a Mon Sep 17 00:00:00 2001 From: Radu Sabau Date: Wed, 8 Apr 2026 13:32:13 +0300 Subject: [PATCH 009/337] iio: adc: ad4695: Fix call ordering in offload buffer postenable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ad4695_enter_advanced_sequencer_mode() was called after spi_offload_trigger_enable(). That is wrong because ad4695_enter_advanced_sequencer_mode() issues regular SPI transfers to put the ADC into advanced sequencer mode, and not all SPI offload capable controllers support regular SPI transfers while offloading is enabled. Fix this by calling ad4695_enter_advanced_sequencer_mode() before spi_offload_trigger_enable(), so the ADC is fully configured before the first CNV pulse can occur. This is consistent with the same constraint that already applies to the BUSY_GP_EN write above it. Update the error unwind labels accordingly: add err_exit_conversion_mode so that a failure of spi_offload_trigger_enable() correctly exits conversion mode before clearing BUSY_GP_EN. Fixes: f09f140e3ea8 ("iio: adc: ad4695: Add support for SPI offload") Reviewed-by: Nuno Sá Reviewed-by: David Lechner Signed-off-by: Radu Sabau Cc: Stable@vger.kernel.org Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad4695.c | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/drivers/iio/adc/ad4695.c b/drivers/iio/adc/ad4695.c index cda419638d9a..53642de7330d 100644 --- a/drivers/iio/adc/ad4695.c +++ b/drivers/iio/adc/ad4695.c @@ -876,14 +876,14 @@ static int ad4695_offload_buffer_postenable(struct iio_dev *indio_dev) if (ret) goto err_unoptimize_message; - ret = spi_offload_trigger_enable(st->offload, st->offload_trigger, - &config); + ret = ad4695_enter_advanced_sequencer_mode(st, num_slots); if (ret) goto err_disable_busy_output; - ret = ad4695_enter_advanced_sequencer_mode(st, num_slots); + ret = spi_offload_trigger_enable(st->offload, st->offload_trigger, + &config); if (ret) - goto err_offload_trigger_disable; + goto err_exit_conversion_mode; mutex_lock(&st->cnv_pwm_lock); pwm_get_state(st->cnv_pwm, &state); @@ -895,23 +895,16 @@ static int ad4695_offload_buffer_postenable(struct iio_dev *indio_dev) ret = pwm_apply_might_sleep(st->cnv_pwm, &state); mutex_unlock(&st->cnv_pwm_lock); if (ret) - goto err_offload_exit_conversion_mode; + goto err_offload_trigger_disable; return 0; -err_offload_exit_conversion_mode: - /* - * We have to unwind in a different order to avoid triggering offload. - * ad4695_exit_conversion_mode() triggers a conversion, so it has to be - * done after spi_offload_trigger_disable(). - */ - spi_offload_trigger_disable(st->offload, st->offload_trigger); - ad4695_exit_conversion_mode(st); - goto err_disable_busy_output; - err_offload_trigger_disable: spi_offload_trigger_disable(st->offload, st->offload_trigger); +err_exit_conversion_mode: + ad4695_exit_conversion_mode(st); + err_disable_busy_output: regmap_clear_bits(st->regmap, AD4695_REG_GP_MODE, AD4695_REG_GP_MODE_BUSY_GP_EN); From 761e8b489e6cf166c574034b70637f8a7eadd0ee Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Tue, 31 Mar 2026 13:13:00 +0300 Subject: [PATCH 010/337] iio: gyro: adis16260: fix division by zero in write_raw MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a validation check for the sampling frequency value before using it as a divisor. A user writing zero to the sampling_frequency sysfs attribute triggers a division by zero in the kernel. Fixes: 089a41985c6c ("staging: iio: adis16260 digital gyro driver") Signed-off-by: Antoniu Miclaus Reviewed-by: Nuno Sá Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/gyro/adis16260.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/iio/gyro/adis16260.c b/drivers/iio/gyro/adis16260.c index 586e6cfa14a9..91b9c5f18ec4 100644 --- a/drivers/iio/gyro/adis16260.c +++ b/drivers/iio/gyro/adis16260.c @@ -287,6 +287,9 @@ static int adis16260_write_raw(struct iio_dev *indio_dev, addr = adis16260_addresses[chan->scan_index][1]; return adis_write_reg_16(adis, addr, val); case IIO_CHAN_INFO_SAMP_FREQ: + if (val <= 0) + return -EINVAL; + if (spi_get_device_id(adis->spi)->driver_data) t = 256 / val; else From bb21ee31f5753a7972148798fd7dfb841dd33bdb Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Thu, 16 Apr 2026 14:14:42 +0300 Subject: [PATCH 011/337] iio: Fix iio_multiply_value use in iio_read_channel_processed_scale The function iio_multiply_value returns IIO_VAL_INT (1) on success or a negative error number on failure, while iio_read_channel_processed_scale should return an error code or 0. This creates a situation where the expected result is treated as an error. Fix this by checking the iio_multiply_value result separately, instead of passing it as a return value. Fixes: 05f958d003c9 ("iio: Improve iio_read_channel_processed_scale() precision") Signed-off-by: Svyatoslav Ryhel Reviewed-by: Hans de Goede Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/inkern.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c index 0df0ab3de270..9ce20cb05a9b 100644 --- a/drivers/iio/inkern.c +++ b/drivers/iio/inkern.c @@ -738,7 +738,11 @@ int iio_read_channel_processed_scale(struct iio_channel *chan, int *val, if (ret < 0) return ret; - return iio_multiply_value(val, scale, ret, pval, pval2); + ret = iio_multiply_value(val, scale, ret, pval, pval2); + if (ret < 0) + return ret; + + return 0; } else { ret = iio_channel_read(chan, val, NULL, IIO_CHAN_INFO_RAW); if (ret < 0) From 7e5c0f97c66ad538b87c04a640573371fb434b4f Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 16 Apr 2026 11:01:22 +0200 Subject: [PATCH 012/337] iio: adc: nxp-sar-adc: Avoid division by zero When Common Clock Framework is disabled, clk_get_rate() returns 0. This is used as part of the divisor to perform nanosecond delays with help of ndelay(). When the above condition occurs the compiler, due to unspecified behaviour, is free to do what it wants to. Here it saturates the value, which is logical from mathematics point of view. However, the ndelay() implementation has set a reasonable upper threshold and refuses to provide anything for such a long delay. That's why code may not be linked under these circumstances. To solve the issue, provide a wrapper that calls ndelay() when the value is known not to be zero. Fixes: 4434072a893e ("iio: adc: Add the NXP SAR ADC support for the s32g2/3 platforms") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202603311958.ly6uROit-lkp@intel.com/ Signed-off-by: Andy Shevchenko Acked-by: Daniel Lezcano Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/adc/nxp-sar-adc.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/iio/adc/nxp-sar-adc.c b/drivers/iio/adc/nxp-sar-adc.c index 9d9f2c76bed4..705dd7da1bd2 100644 --- a/drivers/iio/adc/nxp-sar-adc.c +++ b/drivers/iio/adc/nxp-sar-adc.c @@ -198,6 +198,15 @@ static void nxp_sar_adc_irq_cfg(struct nxp_sar_adc *info, bool enable) writel(0, NXP_SAR_ADC_IMR(info->regs)); } +static void nxp_sar_adc_wait_for(struct nxp_sar_adc *info, unsigned int cycles) +{ + u64 rate; + + rate = clk_get_rate(info->clk); + if (rate) + ndelay(div64_u64(NSEC_PER_SEC, rate * cycles)); +} + static bool nxp_sar_adc_set_enabled(struct nxp_sar_adc *info, bool enable) { u32 mcr; @@ -221,7 +230,7 @@ static bool nxp_sar_adc_set_enabled(struct nxp_sar_adc *info, bool enable) * configuration of NCMR and the setting of NSTART. */ if (enable) - ndelay(div64_u64(NSEC_PER_SEC, clk_get_rate(info->clk) * 3)); + nxp_sar_adc_wait_for(info, 3); return pwdn; } @@ -469,7 +478,7 @@ static void nxp_sar_adc_stop_conversion(struct nxp_sar_adc *info) * only when the capture finishes. The delay will be very * short, usec-ish, which is acceptable in the atomic context. */ - ndelay(div64_u64(NSEC_PER_SEC, clk_get_rate(info->clk)) * 80); + nxp_sar_adc_wait_for(info, 80); } static int nxp_sar_adc_start_conversion(struct nxp_sar_adc *info, bool raw) From 0d42e2c0bd6ceb89e44c6e065f9bdf9b1df3ef0c Mon Sep 17 00:00:00 2001 From: David Carlier Date: Tue, 14 Apr 2026 13:30:06 +0100 Subject: [PATCH 013/337] iio: adc: npcm: fix unbalanced clk_disable_unprepare() The driver acquired the ADC clock with devm_clk_get() and read its rate, but never called clk_prepare_enable(). The probe error path and npcm_adc_remove() both called clk_disable_unprepare() unconditionally, causing the clk framework's enable/prepare counts to underflow on probe failure or module unbind. The issue went unnoticed because NPCM BMC firmware leaves the ADC clock enabled at boot, so the driver happened to work in practice. Switch to devm_clk_get_enabled() so the clock is properly enabled during probe and automatically released by the device-managed cleanup, and drop the now-redundant clk_disable_unprepare() from both the probe error path and remove(). While at it, drop the duplicate error message on devm_request_irq() failure since the IRQ core already logs it. Fixes: 9bf85fbc9d8f ("iio: adc: add NPCM ADC driver") Signed-off-by: David Carlier Reviewed-by: Andy Shevchenko Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/adc/npcm_adc.c | 25 ++++++++----------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/drivers/iio/adc/npcm_adc.c b/drivers/iio/adc/npcm_adc.c index ddabb9600d46..61c8b825bda1 100644 --- a/drivers/iio/adc/npcm_adc.c +++ b/drivers/iio/adc/npcm_adc.c @@ -231,7 +231,7 @@ static int npcm_adc_probe(struct platform_device *pdev) if (IS_ERR(info->reset)) return PTR_ERR(info->reset); - info->adc_clk = devm_clk_get(&pdev->dev, NULL); + info->adc_clk = devm_clk_get_enabled(&pdev->dev, NULL); if (IS_ERR(info->adc_clk)) { dev_warn(&pdev->dev, "ADC clock failed: can't read clk\n"); return PTR_ERR(info->adc_clk); @@ -244,17 +244,13 @@ static int npcm_adc_probe(struct platform_device *pdev) info->adc_sample_hz = clk_get_rate(info->adc_clk) / ((div + 1) * 2); irq = platform_get_irq(pdev, 0); - if (irq < 0) { - ret = irq; - goto err_disable_clk; - } + if (irq < 0) + return irq; ret = devm_request_irq(&pdev->dev, irq, npcm_adc_isr, 0, "NPCM_ADC", indio_dev); - if (ret < 0) { - dev_err(dev, "failed requesting interrupt\n"); - goto err_disable_clk; - } + if (ret < 0) + return ret; reg_con = ioread32(info->regs + NPCM_ADCCON); info->vref = devm_regulator_get_optional(&pdev->dev, "vref"); @@ -262,7 +258,7 @@ static int npcm_adc_probe(struct platform_device *pdev) ret = regulator_enable(info->vref); if (ret) { dev_err(&pdev->dev, "Can't enable ADC reference voltage\n"); - goto err_disable_clk; + return ret; } iowrite32(reg_con & ~NPCM_ADCCON_REFSEL, @@ -272,10 +268,8 @@ static int npcm_adc_probe(struct platform_device *pdev) * Any error which is not ENODEV indicates the regulator * has been specified and so is a failure case. */ - if (PTR_ERR(info->vref) != -ENODEV) { - ret = PTR_ERR(info->vref); - goto err_disable_clk; - } + if (PTR_ERR(info->vref) != -ENODEV) + return PTR_ERR(info->vref); /* Use internal reference */ iowrite32(reg_con | NPCM_ADCCON_REFSEL, @@ -314,8 +308,6 @@ static int npcm_adc_probe(struct platform_device *pdev) iowrite32(reg_con & ~NPCM_ADCCON_ADC_EN, info->regs + NPCM_ADCCON); if (!IS_ERR(info->vref)) regulator_disable(info->vref); -err_disable_clk: - clk_disable_unprepare(info->adc_clk); return ret; } @@ -332,7 +324,6 @@ static void npcm_adc_remove(struct platform_device *pdev) iowrite32(regtemp & ~NPCM_ADCCON_ADC_EN, info->regs + NPCM_ADCCON); if (!IS_ERR(info->vref)) regulator_disable(info->vref); - clk_disable_unprepare(info->adc_clk); } static struct platform_driver npcm_adc_driver = { From 5aba4f94b225617a55fed442a70329b2ee19c0a5 Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Wed, 1 Apr 2026 14:08:29 +0300 Subject: [PATCH 014/337] iio: chemical: scd30: fix division by zero in write_raw Add a zero check for val2 before using it as a divisor when setting the sampling frequency. A user writing a zero fractional part to the sampling_frequency sysfs attribute triggers a division by zero in the kernel. Fixes: 64b3d8b1b0f5 ("iio: chemical: scd30: add core driver") Signed-off-by: Antoniu Miclaus Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/chemical/scd30_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/chemical/scd30_core.c b/drivers/iio/chemical/scd30_core.c index a665fcb78806..11d6bc1b63e6 100644 --- a/drivers/iio/chemical/scd30_core.c +++ b/drivers/iio/chemical/scd30_core.c @@ -256,7 +256,7 @@ static int scd30_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const guard(mutex)(&state->lock); switch (mask) { case IIO_CHAN_INFO_SAMP_FREQ: - if (val) + if (val || !val2) return -EINVAL; val = 1000000000 / val2; From 76b0d0baa9ae9c60e726bbe1b6ff0bec2c993634 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sat, 25 Apr 2026 22:07:06 -0700 Subject: [PATCH 015/337] Input: elan_i2c - validate firmware size before use Ensure that the firmware file is large enough to contain the expected number of pages and the signature (which resides at the end of the firmware blob) before accessing them to prevent potential out-of-bounds reads. Cc: stable@vger.kernel.org Link: https://patch.msgid.link/ae2dOgiFvXRm4BHo@google.com Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/elan_i2c_core.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/input/mouse/elan_i2c_core.c b/drivers/input/mouse/elan_i2c_core.c index 7475803c6ce4..5cba02a156ce 100644 --- a/drivers/input/mouse/elan_i2c_core.c +++ b/drivers/input/mouse/elan_i2c_core.c @@ -648,6 +648,11 @@ static ssize_t elan_sysfs_update_fw(struct device *dev, return error; } + if (fw->size < data->fw_signature_address + sizeof(signature)) { + dev_err(dev, "firmware file too small\n"); + return -EBADF; + } + /* Firmware file must match signature data */ fw_signature = &fw->data[data->fw_signature_address]; if (memcmp(fw_signature, signature, sizeof(signature)) != 0) { From c897cf120696b94f56ed0f3197ba9a77071a59ec Mon Sep 17 00:00:00 2001 From: Dmitriy Zharov Date: Thu, 30 Apr 2026 22:35:22 +0400 Subject: [PATCH 016/337] Input: xpad - add support for ASUS ROG RAIKIRI II Add the VID/PIDs for the ASUS ROG RAIKIRI II controller to xpad_device and the VID to xpad_table. The controller has a physical PC/XBOX toggle which switches between XBOX360 and XBOXONE protocols. Signed-off-by: Dmitriy Zharov Link: https://patch.msgid.link/20260430183522.122151-1-contact@zharov.dev Cc: stable@vger.kernel.org Signed-off-by: Dmitry Torokhov --- drivers/input/joystick/xpad.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c index 19ce90da89e9..97daec39bfee 100644 --- a/drivers/input/joystick/xpad.c +++ b/drivers/input/joystick/xpad.c @@ -186,6 +186,10 @@ static const struct xpad_device { { 0x07ff, 0xffff, "Mad Catz GamePad", 0, XTYPE_XBOX360 }, { 0x0b05, 0x1a38, "ASUS ROG RAIKIRI", MAP_SHARE_BUTTON, XTYPE_XBOXONE }, { 0x0b05, 0x1abb, "ASUS ROG RAIKIRI PRO", 0, XTYPE_XBOXONE }, + { 0x0b05, 0x1c91, "ASUS ROG RAIKIRI II", 0, XTYPE_XBOX360 }, + { 0x0b05, 0x1c92, "ASUS ROG RAIKIRI II WIRELESS", 0, XTYPE_XBOX360 }, + { 0x0b05, 0x1c96, "ASUS ROG RAIKIRI II XBOX", MAP_SHARE_BUTTON, XTYPE_XBOXONE }, + { 0x0b05, 0x1d04, "ASUS ROG RAIKIRI II XBOX WIRELESS", MAP_SHARE_BUTTON, XTYPE_XBOXONE }, { 0x0c12, 0x0005, "Intec wireless", 0, XTYPE_XBOX }, { 0x0c12, 0x8801, "Nyko Xbox Controller", 0, XTYPE_XBOX }, { 0x0c12, 0x8802, "Zeroplus Xbox Controller", 0, XTYPE_XBOX }, @@ -507,6 +511,7 @@ static const struct usb_device_id xpad_table[] = { { USB_DEVICE(0x0738, 0x4540) }, /* Mad Catz Beat Pad */ XPAD_XBOXONE_VENDOR(0x0738), /* Mad Catz FightStick TE 2 */ XPAD_XBOX360_VENDOR(0x07ff), /* Mad Catz Gamepad */ + XPAD_XBOX360_VENDOR(0x0b05), /* ASUS controllers */ XPAD_XBOXONE_VENDOR(0x0b05), /* ASUS controllers */ XPAD_XBOX360_VENDOR(0x0c12), /* Zeroplus X-Box 360 controllers */ XPAD_XBOX360_VENDOR(0x0db0), /* Micro Star International X-Box 360 controllers */ From 1f6ac0f8441c48c4cc250141e1da8486c13512ba Mon Sep 17 00:00:00 2001 From: Qbeliw Tanaka Date: Thu, 30 Apr 2026 21:44:12 -0700 Subject: [PATCH 017/337] Input: xpad - add "Nova 2 Lite" from GameSir Add support for the gamepad "Nova 2 Lite" from GameSir, compatible with the Xbox 360 gamepad. Signed-off-by: Qbeliw Tanaka Link: https://patch.msgid.link/20260429.162040.930225048583399359.q.tanaka@gmx.com Cc: stable@vger.kernel.org Signed-off-by: Dmitry Torokhov --- drivers/input/joystick/xpad.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c index 97daec39bfee..feb8f368f834 100644 --- a/drivers/input/joystick/xpad.c +++ b/drivers/input/joystick/xpad.c @@ -395,6 +395,7 @@ static const struct xpad_device { { 0x3285, 0x0662, "Nacon Revolution5 Pro", 0, XTYPE_XBOX360 }, { 0x3285, 0x0663, "Nacon Evol-X", 0, XTYPE_XBOXONE }, { 0x3537, 0x1004, "GameSir T4 Kaleid", 0, XTYPE_XBOX360 }, + { 0x3537, 0x100f, "GameSir Nova 2 Lite", 0, XTYPE_XBOX360 }, { 0x3537, 0x1010, "GameSir G7 SE", 0, XTYPE_XBOXONE }, { 0x3651, 0x1000, "CRKD SG", 0, XTYPE_XBOX360 }, { 0x366c, 0x0005, "ByoWave Proteus Controller", MAP_SHARE_BUTTON, XTYPE_XBOXONE, FLAG_DELAY_INIT }, From d9eeb0ea0d2de658663bfaa9c26eccdd8fd64440 Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Mon, 13 Apr 2026 21:46:04 +0800 Subject: [PATCH 018/337] counter: Fix refcount leak in counter_alloc() error path After device_initialize(), the lifetime of the embedded struct device is expected to be managed through the device core reference counting. In counter_alloc(), if dev_set_name() fails after device_initialize(), the error path removes the chrdev, frees the ID, and frees the backing allocation directly instead of releasing the device reference with put_device(). This bypasses the normal device lifetime rules and may leave the reference count of the embedded struct device unbalanced, resulting in a refcount leak. The issue was identified by a static analysis tool I developed and confirmed by manual review. Fix this by using put_device() in the dev_set_name() failure path and let counter_device_release() handle the final cleanup. Fixes: 4da08477ea1f ("counter: Set counter device name") Cc: stable@vger.kernel.org Signed-off-by: Guangshuo Li Link: https://lore.kernel.org/r/20260413134604.2861772-1-lgs201920130244@gmail.com Signed-off-by: William Breathitt Gray --- drivers/counter/counter-core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/counter/counter-core.c b/drivers/counter/counter-core.c index 50bd30ba3d03..0b1dac61b7b5 100644 --- a/drivers/counter/counter-core.c +++ b/drivers/counter/counter-core.c @@ -124,7 +124,8 @@ struct counter_device *counter_alloc(size_t sizeof_priv) err_dev_set_name: - counter_chrdev_remove(counter); + put_device(dev); + return NULL; err_chrdev_add: ida_free(&counter_ida, dev->id); From 754d60ad1c91895be0bc7d771fbf9fb3c9448640 Mon Sep 17 00:00:00 2001 From: Alexander Dahl Date: Wed, 29 Apr 2026 14:59:30 +0200 Subject: [PATCH 019/337] memory: atmel-ebi: Allow deferred probing After removing of_platform_default_populate() calls the atmel-ebi driver was affected by deferred probing. platform_driver_probe() is incompatible with deferred probing. This led to atmel-ebi driver eventually not being probed on at91 sam9x60-curiosity and other sam9x60 based boards. Subsequently the nand-controller driver (nand-controller being a child node of ebi) on that platform was not probed and thus raw NAND flash was inaccessible, preventing devices to boot with rootfs on raw NAND flash (e.g. with UBI/UBIFS). Fixes: 0b0f7e6539a7 ("ARM: at91: remove unnecessary of_platform_default_populate calls") Cc: stable@vger.kernel.org Suggested-by: Miquel Raynal Signed-off-by: Alexander Dahl Link: https://patch.msgid.link/20260429125930.844790-1-ada@thorsis.com Signed-off-by: Krzysztof Kozlowski --- drivers/memory/atmel-ebi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/memory/atmel-ebi.c b/drivers/memory/atmel-ebi.c index 8db970da9af9..1e8e8aba2542 100644 --- a/drivers/memory/atmel-ebi.c +++ b/drivers/memory/atmel-ebi.c @@ -628,10 +628,11 @@ static __maybe_unused int atmel_ebi_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(atmel_ebi_pm_ops, NULL, atmel_ebi_resume); static struct platform_driver atmel_ebi_driver = { + .probe = atmel_ebi_probe, .driver = { .name = "atmel-ebi", .of_match_table = atmel_ebi_id_table, .pm = &atmel_ebi_pm_ops, }, }; -builtin_platform_driver_probe(atmel_ebi_driver, atmel_ebi_probe); +builtin_platform_driver(atmel_ebi_driver); From d25e5cbac4e2287c843fc8b45c50dd8e57e3a696 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 4 May 2026 12:00:28 +0200 Subject: [PATCH 020/337] auxdisplay: max6959: use regmap_assign_bits() in max6959_enable() Replace the ternary with a direct call to the regmap_assign_bits() helper and save a couple lines of code. Signed-off-by: Andy Shevchenko --- drivers/auxdisplay/max6959.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/auxdisplay/max6959.c b/drivers/auxdisplay/max6959.c index 6bbc8d48fb1b..3bdef099a225 100644 --- a/drivers/auxdisplay/max6959.c +++ b/drivers/auxdisplay/max6959.c @@ -86,10 +86,7 @@ static const struct linedisp_ops max6959_linedisp_ops = { static int max6959_enable(struct max6959_priv *priv, bool enable) { - u8 mask = REG_CONFIGURATION_S_BIT; - u8 value = enable ? mask : 0; - - return regmap_update_bits(priv->regmap, REG_CONFIGURATION, mask, value); + return regmap_assign_bits(priv->regmap, REG_CONFIGURATION, REG_CONFIGURATION_S_BIT, enable); } static void max6959_power_off(void *priv) From 2c6821657ce3b3c85f92719ea81ec9f9ff27df11 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Mon, 27 Apr 2026 09:01:48 +0800 Subject: [PATCH 021/337] soc: imx8m: Fix match data lookup for soc device The i.MX8M soc device is registered via platform_device_register_simple(), so it is not associated with a Device Tree node and the imx8m_soc_driver has no of_match_table. As a result, device_get_match_data() always returns NULL when probing the soc device. Retrieve the match data directly from the machine compatible using of_machine_get_match_data(imx8_soc_match), which provides the correct SoC data. Fixes: 2524b293a59e5 ("soc: imx8m: don't access of_root directly") Signed-off-by: Peng Fan Reviewed-by: Lucas Stach Signed-off-by: Frank Li --- drivers/soc/imx/soc-imx8m.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/soc/imx/soc-imx8m.c b/drivers/soc/imx/soc-imx8m.c index 77763a107edb..fc080e56f50d 100644 --- a/drivers/soc/imx/soc-imx8m.c +++ b/drivers/soc/imx/soc-imx8m.c @@ -247,7 +247,7 @@ static int imx8m_soc_probe(struct platform_device *pdev) if (ret) return ret; - data = device_get_match_data(dev); + data = of_machine_get_match_data(imx8_soc_match); if (data) { soc_dev_attr->soc_id = data->name; ret = imx8m_soc_prepare(pdev, data->ocotp_compatible); From 74d695fd6f9d70df849c555f358ddfd26e2d85bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Thu, 7 May 2026 18:00:51 +0200 Subject: [PATCH 022/337] Input: fm801-gp - simplify initialisation of pci_device_id array MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of assigning the pci_device_id members using a list (which is hard to read as you need to look at the order of the members in that struct in parallel) use the PCI_VDEVICE() convenience macro to compact the initialisation while improving readability. Also drop trailing zeros that the compiler will care about then. The change doesn't introduce binary changes to the compiled driver, verified on both ARCH=x86 and ARCH=arm64. Signed-off-by: Uwe Kleine-König (The Capable Hub) Link: https://patch.msgid.link/20260507160051.3315630-2-u.kleine-koenig@baylibre.com Signed-off-by: Dmitry Torokhov --- drivers/input/gameport/fm801-gp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/input/gameport/fm801-gp.c b/drivers/input/gameport/fm801-gp.c index 423cccdea34f..1e8c6c044844 100644 --- a/drivers/input/gameport/fm801-gp.c +++ b/drivers/input/gameport/fm801-gp.c @@ -125,8 +125,8 @@ static void fm801_gp_remove(struct pci_dev *pci) } static const struct pci_device_id fm801_gp_id_table[] = { - { PCI_VENDOR_ID_FORTEMEDIA, PCI_DEVICE_ID_FM801_GP, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { 0 } + { PCI_VDEVICE(FORTEMEDIA, PCI_DEVICE_ID_FM801_GP) }, + { } }; MODULE_DEVICE_TABLE(pci, fm801_gp_id_table); From baa0210fb6a9dc3882509a9411b6d284d88fe30e Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 4 May 2026 11:54:45 -0700 Subject: [PATCH 023/337] Input: atmel_mxt_ts - fix boundary check in mxt_prepare_cfg_mem When a configuration file provides an object size that is larger than the driver's known mxt_obj_size(object), the driver intends to discard the extra bytes. The loop iterates using for (i = 0; i < size; i++). Inside the loop, the condition to skip processing extra bytes is: if (i > mxt_obj_size(object)) continue; Since i is a 0-based index, the valid indices for the object are 0 through mxt_obj_size(object) - 1. When i == mxt_obj_size(object), the condition evaluates to false, and the code processes the byte instead of discarding it. This causes the code to calculate byte_offset = reg + i - cfg->start_ofs and writes the byte there, overwriting exactly one byte of the adjacent instance or object. Update the boundary check to skip extra bytes correctly by using >=. Fixes: 50a77c658b80 ("Input: atmel_mxt_ts - download device config using firmware loader") Cc: stable@vger.kernel.org Assisted-by: Gemini:gemini-3.1-pro Reviewed-by: Ricardo Ribalda Link: https://patch.msgid.link/20260504185448.4055973-1-dmitry.torokhov@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/atmel_mxt_ts.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 87c6a10381f2..fad1b3f4138b 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -1473,7 +1473,7 @@ static int mxt_prepare_cfg_mem(struct mxt_data *data, struct mxt_cfg *cfg) } cfg->raw_pos += offset; - if (i > mxt_obj_size(object)) + if (i >= mxt_obj_size(object)) continue; byte_offset = reg + i - cfg->start_ofs; From a5fd88a5d63f812422e69682f3cb663d9d7f3e9c Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 4 May 2026 11:54:46 -0700 Subject: [PATCH 024/337] Input: atmel_mxt_ts - check mem_size before calculating config memory size In mxt_update_cfg(), the driver calculates the memory size needed to store the configuration as data->mem_size - cfg.start_ofs. If data->mem_size is less than or equal to cfg.start_ofs, this calculation will underflow or result in a zero-size buffer, neither of which is valid for a configuration update. Add a check to return -EINVAL if data->mem_size is too small. While at it, change the types of start_ofs and mem_size in struct mxt_cfg to u16 to match the device address space. Assisted-by: Gemini:gemini-3.1-pro Link: https://patch.msgid.link/20260504185448.4055973-2-dmitry.torokhov@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/atmel_mxt_ts.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index fad1b3f4138b..f21bf2844112 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -275,8 +275,8 @@ struct mxt_cfg { off_t raw_pos; u8 *mem; - size_t mem_size; - int start_ofs; + u16 mem_size; + u16 start_ofs; struct mxt_info info; }; @@ -1627,6 +1627,13 @@ static int mxt_update_cfg(struct mxt_data *data, const struct firmware *fw) cfg.start_ofs = MXT_OBJECT_START + data->info->object_num * sizeof(struct mxt_object) + MXT_INFO_CHECKSUM_SIZE; + + if (data->mem_size <= cfg.start_ofs) { + dev_err(dev, "Memory size too small: %u < %u\n", + data->mem_size, cfg.start_ofs); + return -EINVAL; + } + cfg.mem_size = data->mem_size - cfg.start_ofs; u8 *mem_buf __free(kfree) = cfg.mem = kzalloc(cfg.mem_size, GFP_KERNEL); From 01deda0152066c6c955f0619114ea6afa070aaec Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Sun, 10 May 2026 19:16:56 -0400 Subject: [PATCH 025/337] thunderbolt: property: Reject u32 wrap in tb_property_entry_valid() entry->value is u32 and entry->length is u16; the sum is performed in u32 and wraps. A malicious XDomain peer can pick value = 0xffffff00, length = 0x100 so the sum 0x100000000 wraps to 0 and passes the > block_len check. tb_property_parse() then passes entry->value to parse_dwdata() as a dword offset into the property block, reading attacker-directed memory far past the allocation. For TEXT-typed entries with the "deviceid" or "vendorid" keys this lands in xd->device_name / xd->vendor_name and is readable back via the per-XDomain device_name / vendor_name sysfs attributes; the leak is NUL-bounded (kstrdup() stops at the first zero byte) and untargeted (the attacker picks a delta, not an absolute address). DATA-typed entries are parsed into property->value.data but not generically surfaced to userspace. Use check_add_overflow() so a wrapped sum is rejected. Fixes: cdae7c07e3e3 ("thunderbolt: Add support for XDomain properties") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-6 Assisted-by: Codex:gpt-5-4 Signed-off-by: Michael Bommarito Signed-off-by: Mika Westerberg --- drivers/thunderbolt/property.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/thunderbolt/property.c b/drivers/thunderbolt/property.c index 50cbfc92fe65..29cd60c11ac4 100644 --- a/drivers/thunderbolt/property.c +++ b/drivers/thunderbolt/property.c @@ -8,6 +8,7 @@ */ #include +#include #include #include #include @@ -52,13 +53,16 @@ static inline void format_dwdata(void *dst, const void *src, size_t dwords) static bool tb_property_entry_valid(const struct tb_property_entry *entry, size_t block_len) { + u32 end; + switch (entry->type) { case TB_PROPERTY_TYPE_DIRECTORY: case TB_PROPERTY_TYPE_DATA: case TB_PROPERTY_TYPE_TEXT: if (entry->length > block_len) return false; - if (entry->value + entry->length > block_len) + if (check_add_overflow(entry->value, entry->length, &end) || + end > block_len) return false; break; From de21b59c29e31c5108ddc04210631bbfab81b997 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Sun, 10 May 2026 19:16:57 -0400 Subject: [PATCH 026/337] thunderbolt: property: Reject dir_len < 4 to prevent size_t underflow On the non-root path, __tb_property_parse_dir() takes dir_len from entry->length (u16 widened to size_t). Two distinct OOB conditions follow when entry->length < 4: 1. The non-root path begins with kmemdup(&block[dir_offset], sizeof(*dir->uuid), ...) which always reads 4 dwords from dir_offset. tb_property_entry_valid() only enforces dir_offset + entry->length <= block_len, so a crafted entry with dir_offset close to the end of the property block and entry->length in 0..3 passes that gate but lets the UUID copy run off the block (e.g. dir_offset = 497, dir_len = 3 in a 500-dword block reads block[497..501]). 2. After the kmemdup, content_len = dir_len - 4 underflows size_t to ~SIZE_MAX, nentries becomes SIZE_MAX / 4, and the entry walk runs OOB on each iteration until an entry fails validation or the kernel oopses on an unmapped page. Reject dir_len < 4 on the non-root path *before* the UUID kmemdup, which closes both holes. Also move INIT_LIST_HEAD(&dir->properties) up to immediately after the dir allocation so the new error-return path (and the existing uuid-alloc failure path) calling tb_property_free_dir() sees a walkable list rather than the zero-initialized NULL next/prev that list_for_each_entry_safe() would oops on. Fixes: cdae7c07e3e3 ("thunderbolt: Add support for XDomain properties") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-6 Assisted-by: Codex:gpt-5-4 Signed-off-by: Michael Bommarito Signed-off-by: Mika Westerberg --- drivers/thunderbolt/property.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/thunderbolt/property.c b/drivers/thunderbolt/property.c index 29cd60c11ac4..74c92f9801ff 100644 --- a/drivers/thunderbolt/property.c +++ b/drivers/thunderbolt/property.c @@ -174,10 +174,16 @@ static struct tb_property_dir *__tb_property_parse_dir(const u32 *block, if (!dir) return NULL; + INIT_LIST_HEAD(&dir->properties); + if (is_root) { content_offset = dir_offset + 2; content_len = dir_len; } else { + if (dir_len < 4) { + tb_property_free_dir(dir); + return NULL; + } dir->uuid = kmemdup(&block[dir_offset], sizeof(*dir->uuid), GFP_KERNEL); if (!dir->uuid) { @@ -191,8 +197,6 @@ static struct tb_property_dir *__tb_property_parse_dir(const u32 *block, entries = (const struct tb_property_entry *)&block[content_offset]; nentries = content_len / (sizeof(*entries) / 4); - INIT_LIST_HEAD(&dir->properties); - for (i = 0; i < nentries; i++) { struct tb_property *property; From 928abe19fbf0127003abcb1ea69cabc1c897d0ab Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Sun, 10 May 2026 19:16:58 -0400 Subject: [PATCH 027/337] thunderbolt: property: Cap recursion depth in __tb_property_parse_dir() A DIRECTORY entry's value field is used as the dir_offset for a recursive call into __tb_property_parse_dir() with no depth counter. A crafted peer that chains DIRECTORY entries into a back-reference loop drives the parser until the kernel stack is exhausted and the guard page fires. Any untrusted XDomain peer (cable, dock, in-line inspector, adjacent host) that reaches the PROPERTIES_REQUEST control-plane exchange can trigger this without authentication. Thread a depth counter through tb_property_parse() and __tb_property_parse_dir(), and reject blocks that exceed TB_PROPERTY_MAX_DEPTH = 8. That is comfortably larger than any observed legitimate XDomain layout. Operators who do not need XDomain host-to-host discovery can disable the path entirely with thunderbolt.xdomain=0 on the kernel command line. Fixes: cdae7c07e3e3 ("thunderbolt: Add support for XDomain properties") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-6 Assisted-by: Codex:gpt-5-4 Signed-off-by: Michael Bommarito Signed-off-by: Mika Westerberg --- drivers/thunderbolt/property.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/drivers/thunderbolt/property.c b/drivers/thunderbolt/property.c index 74c92f9801ff..da2c59a17db5 100644 --- a/drivers/thunderbolt/property.c +++ b/drivers/thunderbolt/property.c @@ -35,10 +35,11 @@ struct tb_property_dir_entry { }; #define TB_PROPERTY_ROOTDIR_MAGIC 0x55584401 +#define TB_PROPERTY_MAX_DEPTH 8 static struct tb_property_dir *__tb_property_parse_dir(const u32 *block, size_t block_len, unsigned int dir_offset, size_t dir_len, - bool is_root); + bool is_root, unsigned int depth); static inline void parse_dwdata(void *dst, const void *src, size_t dwords) { @@ -97,7 +98,8 @@ tb_property_alloc(const char *key, enum tb_property_type type) } static struct tb_property *tb_property_parse(const u32 *block, size_t block_len, - const struct tb_property_entry *entry) + const struct tb_property_entry *entry, + unsigned int depth) { char key[TB_PROPERTY_KEY_SIZE + 1]; struct tb_property *property; @@ -118,7 +120,7 @@ static struct tb_property *tb_property_parse(const u32 *block, size_t block_len, switch (property->type) { case TB_PROPERTY_TYPE_DIRECTORY: dir = __tb_property_parse_dir(block, block_len, entry->value, - entry->length, false); + entry->length, false, depth + 1); if (!dir) { kfree(property); return NULL; @@ -163,13 +165,17 @@ static struct tb_property *tb_property_parse(const u32 *block, size_t block_len, } static struct tb_property_dir *__tb_property_parse_dir(const u32 *block, - size_t block_len, unsigned int dir_offset, size_t dir_len, bool is_root) + size_t block_len, unsigned int dir_offset, size_t dir_len, bool is_root, + unsigned int depth) { const struct tb_property_entry *entries; size_t i, content_len, nentries; unsigned int content_offset; struct tb_property_dir *dir; + if (depth > TB_PROPERTY_MAX_DEPTH) + return NULL; + dir = kzalloc_obj(*dir); if (!dir) return NULL; @@ -200,7 +206,7 @@ static struct tb_property_dir *__tb_property_parse_dir(const u32 *block, for (i = 0; i < nentries; i++) { struct tb_property *property; - property = tb_property_parse(block, block_len, &entries[i]); + property = tb_property_parse(block, block_len, &entries[i], depth); if (!property) { tb_property_free_dir(dir); return NULL; @@ -239,7 +245,7 @@ struct tb_property_dir *tb_property_parse_dir(const u32 *block, return NULL; return __tb_property_parse_dir(block, block_len, 0, rootdir->length, - true); + true, 0); } /** From e27264daac7d9ce892a2a5b4a864d6d9a3c9276a Mon Sep 17 00:00:00 2001 From: Harshal Dev Date: Thu, 16 Apr 2026 17:29:18 +0530 Subject: [PATCH 028/337] dt-bindings: crypto: qcom,ice: Fix missing power-domain and iface clk The DT bindings for inline-crypto engine do not specify the UFS_PHY_GDSC power-domain and iface clock. Without enabling the iface clock and the associated power-domain the ICE hardware cannot function correctly and leads to unclocked hardware accesses being observed during probe. Fix the DT bindings for inline-crypto engine to require the UFS_PHY_GDSC power-domain and iface clock for new devices (Eliza and Milos) introduced in the current release (7.1) with yet-to-stabilize ABI, while preserving backward compatibility for older devices. Fixes: 618195a7ac3df ("dt-bindings: crypto: qcom,inline-crypto-engine: Document the Eliza ICE") Fixes: 85faec1e85555 ("dt-bindings: crypto: qcom,inline-crypto-engine: document the Milos ICE") Reviewed-by: Kuldeep Singh Reviewed-by: Krzysztof Kozlowski Signed-off-by: Harshal Dev Link: https://lore.kernel.org/r/20260416-qcom_ice_power_and_clk_vote-v5-1-5ccf5d7e2846@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- .../crypto/qcom,inline-crypto-engine.yaml | 35 ++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/crypto/qcom,inline-crypto-engine.yaml b/Documentation/devicetree/bindings/crypto/qcom,inline-crypto-engine.yaml index 876bf90ed96e..ccb6b8dd8e11 100644 --- a/Documentation/devicetree/bindings/crypto/qcom,inline-crypto-engine.yaml +++ b/Documentation/devicetree/bindings/crypto/qcom,inline-crypto-engine.yaml @@ -30,6 +30,16 @@ properties: maxItems: 1 clocks: + minItems: 1 + maxItems: 2 + + clock-names: + minItems: 1 + items: + - const: core + - const: iface + + power-domains: maxItems: 1 operating-points-v2: true @@ -44,6 +54,25 @@ required: additionalProperties: false +allOf: + - if: + properties: + compatible: + contains: + enum: + - qcom,eliza-inline-crypto-engine + - qcom,milos-inline-crypto-engine + + then: + required: + - power-domains + - clock-names + properties: + clocks: + minItems: 2 + clock-names: + minItems: 2 + examples: - | #include @@ -52,7 +81,11 @@ examples: compatible = "qcom,sm8550-inline-crypto-engine", "qcom,inline-crypto-engine"; reg = <0x01d88000 0x8000>; - clocks = <&gcc GCC_UFS_PHY_ICE_CORE_CLK>; + clocks = <&gcc GCC_UFS_PHY_ICE_CORE_CLK>, + <&gcc GCC_UFS_PHY_AHB_CLK>; + clock-names = "core", + "iface"; + power-domains = <&gcc UFS_PHY_GDSC>; operating-points-v2 = <&ice_opp_table>; From 0d5dc5818191b55e4364d04b1b898a14a2ccac38 Mon Sep 17 00:00:00 2001 From: Harshal Dev Date: Thu, 16 Apr 2026 17:29:19 +0530 Subject: [PATCH 029/337] soc: qcom: ice: Allow explicit votes on 'iface' clock for ICE Since Qualcomm inline-crypto engine (ICE) is now a dedicated driver de-coupled from the QCOM UFS driver, it explicitly votes for its required clocks during probe. For scenarios where the 'clk_ignore_unused' flag is not passed on the kernel command line, to avoid potential unclocked ICE hardware register access during probe the ICE driver should additionally vote on the 'iface' clock. Also update the suspend and resume callbacks to handle un-voting and voting on the 'iface' clock. Fixes: 2afbf43a4aec6 ("soc: qcom: Make the Qualcomm UFS/SDCC ICE a dedicated driver") Reviewed-by: Manivannan Sadhasivam Reviewed-by: Kuldeep Singh Reviewed-by: Konrad Dybcio Signed-off-by: Harshal Dev Link: https://lore.kernel.org/r/20260416-qcom_ice_power_and_clk_vote-v5-2-5ccf5d7e2846@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/ice.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/drivers/soc/qcom/ice.c b/drivers/soc/qcom/ice.c index b203bc685cad..bf4ab2d9e5c0 100644 --- a/drivers/soc/qcom/ice.c +++ b/drivers/soc/qcom/ice.c @@ -108,6 +108,7 @@ struct qcom_ice { void __iomem *base; struct clk *core_clk; + struct clk *iface_clk; bool use_hwkm; bool hwkm_init_complete; u8 hwkm_version; @@ -312,8 +313,13 @@ int qcom_ice_resume(struct qcom_ice *ice) err = clk_prepare_enable(ice->core_clk); if (err) { - dev_err(dev, "failed to enable core clock (%d)\n", - err); + dev_err(dev, "Failed to enable core clock: %d\n", err); + return err; + } + + err = clk_prepare_enable(ice->iface_clk); + if (err) { + dev_err(dev, "Failed to enable iface clock: %d\n", err); return err; } qcom_ice_hwkm_init(ice); @@ -323,6 +329,7 @@ EXPORT_SYMBOL_GPL(qcom_ice_resume); int qcom_ice_suspend(struct qcom_ice *ice) { + clk_disable_unprepare(ice->iface_clk); clk_disable_unprepare(ice->core_clk); ice->hwkm_init_complete = false; @@ -579,11 +586,17 @@ static struct qcom_ice *qcom_ice_create(struct device *dev, engine->core_clk = devm_clk_get_optional_enabled(dev, "ice_core_clk"); if (!engine->core_clk) engine->core_clk = devm_clk_get_optional_enabled(dev, "ice"); + if (!engine->core_clk) + engine->core_clk = devm_clk_get_optional_enabled(dev, "core"); if (!engine->core_clk) engine->core_clk = devm_clk_get_enabled(dev, NULL); if (IS_ERR(engine->core_clk)) return ERR_CAST(engine->core_clk); + engine->iface_clk = devm_clk_get_optional_enabled(dev, "iface"); + if (IS_ERR(engine->iface_clk)) + return ERR_CAST(engine->iface_clk); + if (!qcom_ice_check_supported(engine)) return ERR_PTR(-EOPNOTSUPP); From 5a52c5701a67d5176eb1afbf1bdaf7d6dfeec597 Mon Sep 17 00:00:00 2001 From: Kamal Dasu Date: Thu, 23 Apr 2026 15:18:55 -0400 Subject: [PATCH 030/337] mmc: core: Fix host controller programming for fixed driver type When using the fixed-emmc-driver-type device tree property, the MMC core correctly selects the driver strength for the card but fails to program the host controller accordingly. This causes a mismatch where the card uses the specified driver type while the host controller defaults to Type B (since ios->drv_type remains zero). Split the driver type programming logic to handle both fixed and dynamic driver type selection paths. For fixed driver types, program the host controller with the selected drive_strength value. For dynamic selection, use the existing drv_type as before. This ensures both the eMMC device and host controller use matching driver strengths, preventing potential signal integrity issues. Fixes: 6186d06c519e ("mmc: parse new binding for eMMC fixed driver type") Signed-off-by: Kamal Dasu Reviewed-by: Shawn Lin Cc: stable@vger.kernel.org Signed-off-by: Ulf Hansson --- drivers/mmc/core/mmc.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 8846550a8892..05444ecf3909 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -1371,7 +1371,9 @@ static void mmc_select_driver_type(struct mmc_card *card) card->drive_strength = drive_strength; - if (drv_type) + if (fixed_drv_type >= 0 && drive_strength) + mmc_set_driver_type(card->host, drive_strength); + else if (drv_type) mmc_set_driver_type(card->host, drv_type); } From d15cd40cb1858f75846eaafa9a6bca841b790a92 Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Fri, 10 Apr 2026 18:19:31 +0100 Subject: [PATCH 031/337] serial: zs: Fix swapped RI/DSR modem line transition counting Fix a thinko in the status interrupt handler that has caused counters for the RI and DSR modem line transitions to be used for the other line each. Fixes: 8b4a40809e53 ("zs: move to the serial subsystem") Cc: stable Signed-off-by: Maciej W. Rozycki Link: https://patch.msgid.link/alpine.DEB.2.21.2604101747110.29980@angie.orcam.me.uk Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/zs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/zs.c b/drivers/tty/serial/zs.c index 72a3c0d90f40..31e12645f66b 100644 --- a/drivers/tty/serial/zs.c +++ b/drivers/tty/serial/zs.c @@ -680,9 +680,9 @@ static void zs_status_handle(struct zs_port *zport, struct zs_port *zport_a) uart_handle_dcd_change(uport, zport->mctrl & TIOCM_CAR); if (delta & TIOCM_RNG) - uport->icount.dsr++; - if (delta & TIOCM_DSR) uport->icount.rng++; + if (delta & TIOCM_DSR) + uport->icount.dsr++; if (delta) wake_up_interruptible(&uport->state->port.delta_msr_wait); From 6fe472c1bbbe238e91141f7cabc1226e96a60d43 Mon Sep 17 00:00:00 2001 From: Zhaoyang Yu <2426767509@qq.com> Date: Thu, 9 Apr 2026 13:41:58 +0800 Subject: [PATCH 032/337] tty: serial: pch_uart: add check for dma_alloc_coherent() Add a check for dma_alloc_coherent() failure to prevent a potential NULL pointer dereference in dma_handle_rx(). Properly release DMA channels and the PCI device reference using a goto ladder if the allocation fails. Fixes: 3c6a483275f4 ("Serial: EG20T: add PCH_UART driver") Cc: stable Signed-off-by: Zhaoyang Yu <2426767509@qq.com> Reviewed-by: Andy Shevchenko Link: https://patch.msgid.link/tencent_E328416B7CFD436F6029F2DF02AD7ED89C08@qq.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/pch_uart.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c index 6729d8e83c3c..ba1fcd663fe2 100644 --- a/drivers/tty/serial/pch_uart.c +++ b/drivers/tty/serial/pch_uart.c @@ -689,8 +689,7 @@ static void pch_request_dma(struct uart_port *port) if (!chan) { dev_err(priv->port.dev, "%s:dma_request_channel FAILS(Tx)\n", __func__); - pci_dev_put(dma_dev); - return; + goto err_pci_get; } priv->chan_tx = chan; @@ -704,18 +703,26 @@ static void pch_request_dma(struct uart_port *port) if (!chan) { dev_err(priv->port.dev, "%s:dma_request_channel FAILS(Rx)\n", __func__); - dma_release_channel(priv->chan_tx); - priv->chan_tx = NULL; - pci_dev_put(dma_dev); - return; + goto err_req_tx; } /* Get Consistent memory for DMA */ priv->rx_buf_virt = dma_alloc_coherent(port->dev, port->fifosize, &priv->rx_buf_dma, GFP_KERNEL); + if (!priv->rx_buf_virt) + goto err_req_rx; priv->chan_rx = chan; pci_dev_put(dma_dev); + return; + +err_req_rx: + dma_release_channel(chan); +err_req_tx: + dma_release_channel(priv->chan_tx); + priv->chan_tx = NULL; +err_pci_get: + pci_dev_put(dma_dev); } static void pch_dma_rx_complete(void *arg) From 92b1ea22454b08a39baef3a7290fb3ec50366616 Mon Sep 17 00:00:00 2001 From: Hongling Zeng Date: Tue, 21 Apr 2026 14:57:37 +0800 Subject: [PATCH 033/337] serial: sh-sci: fix memory region release in error path The sci_request_port() function uses request_mem_region() to reserve I/O memory, but in the error path when sci_remap_port() fails, it incorrectly calls release_resource() instead of release_mem_region(). This mismatch can cause resource accounting issues. Fix it by using the correct release function, consistent with sci_release_port(). Fixes: e2651647080930a1 ("serial: sh-sci: Handle port memory region reservations.") Cc: stable Reported-by: kernel test robot Reported-by: Dan Carpenter Closes: https://lore.kernel.org/r/202604032356.SzEjYkBC-lkp@intel.com/ Signed-off-by: Hongling Zeng Reviewed-by: Geert Uytterhoeven Link: https://patch.msgid.link/20260421065737.724187-1-zenghongling@kylinos.cn Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 6c819b6b2425..54db019a5bfc 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -3025,7 +3025,7 @@ int sci_request_port(struct uart_port *port) ret = sci_remap_port(port); if (unlikely(ret != 0)) { - release_resource(res); + release_mem_region(port->mapbase, sport->reg_size); return ret; } From ca2584d841b69391ffc4144840563d2e1a0018df Mon Sep 17 00:00:00 2001 From: Prasanna S Date: Tue, 28 Apr 2026 09:56:13 +0530 Subject: [PATCH 034/337] serial: qcom-geni: fix UART_RX_PAR_EN bit position UART_RX_PAR_EN is incorrectly defined as bit 3, which triggers false framing errors (S_GP_IRQ_1_EN) and causes received data to be dropped when parity is enabled and the parity bit is 0. Define UART_RX_PAR_EN as bit 4 of the SE_UART_RX_TRANS_CFG register, as specified in the reference manual. Fixes: c4f528795d1a ("tty: serial: msm_geni_serial: Add serial driver support for GENI based QUP") Cc: stable Signed-off-by: Prasanna S Reviewed-by: Konrad Dybcio Link: https://patch.msgid.link/20260428-serial-bit-correct-v1-1-9131ad5b97d8@oss.qualcomm.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/qcom_geni_serial.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c index b365dd5da3cb..5139a9d21b2b 100644 --- a/drivers/tty/serial/qcom_geni_serial.c +++ b/drivers/tty/serial/qcom_geni_serial.c @@ -50,7 +50,7 @@ #define TX_STOP_BIT_LEN_2 2 /* SE_UART_RX_TRANS_CFG */ -#define UART_RX_PAR_EN BIT(3) +#define UART_RX_PAR_EN BIT(4) /* SE_UART_RX_WORD_LEN */ #define RX_WORD_LEN_MASK GENMASK(9, 0) From 1e5b50c78d10119be08bf8f7a11d8ea333dd113a Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 6 May 2026 14:43:23 +0200 Subject: [PATCH 035/337] tty: add missing tty_driver include to tty_port.h Include the definition of struct tty_driver in tty_port.h to keep the header self-contained and avoid build breakage in case anyone includes it before tty_driver.h. Fixes: eb3b0d92c9c3 ("tty: tty_port: add workqueue to flip TTY buffer") Cc: Xin Zhao Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260506124323.186703-1-johan@kernel.org Signed-off-by: Greg Kroah-Hartman --- include/linux/tty_port.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/tty_port.h b/include/linux/tty_port.h index d2a7882c0b58..23cad403bb8f 100644 --- a/include/linux/tty_port.h +++ b/include/linux/tty_port.h @@ -6,10 +6,10 @@ #include #include #include +#include #include struct attribute_group; -struct tty_driver; struct tty_port; struct tty_struct; From 9a9254c4a2a3ca2b3da16d173f3b0dd01f397ff6 Mon Sep 17 00:00:00 2001 From: Shitalkumar Gandhi Date: Mon, 20 Apr 2026 19:29:03 +0530 Subject: [PATCH 036/337] serial: fsl_lpuart: fix rx buffer and DMA map leaks in start_rx_dma lpuart_start_rx_dma() allocates sport->rx_ring.buf with kzalloc() and then maps a scatterlist via dma_map_sg(). On three subsequent error paths the function returns directly without releasing those resources: - when dma_map_sg() returns 0 (-EINVAL): ring->buf is leaked. - when dmaengine_slave_config() fails: ring->buf and the DMA mapping are leaked. - when dmaengine_prep_dma_cyclic() returns NULL: ring->buf and the DMA mapping are leaked. The sole cleanup path, lpuart_dma_rx_free(), is only reached when lpuart_dma_rx_use is set, and the caller lpuart_rx_dma_startup() clears that flag on failure of lpuart_start_rx_dma(). So these resources are permanently leaked on every failure in this function. Repeated port open/close or termios changes under error conditions will slowly consume memory and leave stale streaming DMA mappings behind. Fix it by introducing two error labels that unmap the scatterlist and free the ring buffer as appropriate. While here, replace the misleading -EFAULT (bad userspace pointer) returned when dmaengine_prep_dma_cyclic() fails with the more accurate -ENOMEM, matching how other dmaengine users in the tree treat this failure. No functional change on the success path. Fixes: 5887ad43ee02 ("tty: serial: fsl_lpuart: Use cyclic DMA for Rx") Cc: stable Signed-off-by: Shitalkumar Gandhi Reviewed-by: Frank Li Link: https://patch.msgid.link/20260420135903.2062024-1-shitalkumar.gandhi@cambiumnetworks.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/fsl_lpuart.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index 1bd7ec9c81ea..b7919c05f0fb 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -1379,7 +1379,8 @@ static inline int lpuart_start_rx_dma(struct lpuart_port *sport) if (!nent) { dev_err(sport->port.dev, "DMA Rx mapping error\n"); - return -EINVAL; + ret = -EINVAL; + goto err_free_buf; } dma_rx_sconfig.src_addr = lpuart_dma_datareg_addr(sport); @@ -1391,7 +1392,7 @@ static inline int lpuart_start_rx_dma(struct lpuart_port *sport) if (ret < 0) { dev_err(sport->port.dev, "DMA Rx slave config failed, err = %d\n", ret); - return ret; + goto err_unmap_sg; } sport->dma_rx_desc = dmaengine_prep_dma_cyclic(chan, @@ -1402,7 +1403,8 @@ static inline int lpuart_start_rx_dma(struct lpuart_port *sport) DMA_PREP_INTERRUPT); if (!sport->dma_rx_desc) { dev_err(sport->port.dev, "Cannot prepare cyclic DMA\n"); - return -EFAULT; + ret = -ENOMEM; + goto err_unmap_sg; } sport->dma_rx_desc->callback = lpuart_dma_rx_complete; @@ -1426,6 +1428,13 @@ static inline int lpuart_start_rx_dma(struct lpuart_port *sport) } return 0; + +err_unmap_sg: + dma_unmap_sg(chan->device->dev, &sport->rx_sgl, 1, DMA_FROM_DEVICE); +err_free_buf: + kfree(ring->buf); + ring->buf = NULL; + return ret; } static void lpuart_dma_rx_free(struct uart_port *port) From 452d6fa37ae9b021f4f6d397dbae077f7296f6f4 Mon Sep 17 00:00:00 2001 From: Viken Dadhaniya Date: Wed, 6 May 2026 10:15:21 +0530 Subject: [PATCH 037/337] serial: qcom_geni: fix kfifo underflow when flush precedes DMA completion IRQ When uart_flush_buffer() runs before the DMA completion IRQ is delivered, the following race can occur (all steps serialized by uart_port_lock): 1. DMA starts: tx_remaining = N, kfifo contains N bytes 2. DMA completes in hardware; IRQ is pending but not yet delivered 3. uart_flush_buffer() acquires the port lock and calls kfifo_reset(), making kfifo_len() = 0 while tx_remaining remains N 4. uart_flush_buffer() releases the port lock 5. DMA IRQ fires; handle_tx_dma() acquires the port lock and calls uart_xmit_advance(uport, tx_remaining) on an empty kfifo uart_xmit_advance() increments kfifo->out by tx_remaining. Since kfifo_reset() already set both in and out to 0, out wraps past in, causing kfifo_len() to return UART_XMIT_SIZE - tx_remaining. The next start_tx_dma() call then submits a DMA transfer of stale buffer data. Fix this by snapshotting kfifo_len() at the start of handle_tx_dma() and skipping uart_xmit_advance() when fifo_len < tx_remaining, which indicates the kfifo was reset by a preceding flush. Fixes: 2aaa43c70778 ("tty: serial: qcom-geni-serial: add support for serial engine DMA") Cc: stable Signed-off-by: Viken Dadhaniya Reviewed-by: Bartosz Golaszewski Link: https://patch.msgid.link/20260506-serial-dma-stale-tx-buf-v1-1-e3ccb360d719@oss.qualcomm.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/qcom_geni_serial.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c index 5139a9d21b2b..17da115b1e78 100644 --- a/drivers/tty/serial/qcom_geni_serial.c +++ b/drivers/tty/serial/qcom_geni_serial.c @@ -1031,8 +1031,20 @@ static void qcom_geni_serial_handle_tx_dma(struct uart_port *uport) { struct qcom_geni_serial_port *port = to_dev_port(uport); struct tty_port *tport = &uport->state->port; + unsigned int fifo_len = kfifo_len(&tport->xmit_fifo); + + /* + * Only advance the kfifo if it still contains the bytes that were + * transferred. uart_flush_buffer() may have run before this IRQ + * fired: it calls kfifo_reset() under the port lock, making + * fifo_len = 0 while tx_remaining remains non-zero. Calling + * uart_xmit_advance() in that case would underflow kfifo->out past + * kfifo->in, making kfifo_len() wrap to UART_XMIT_SIZE - tx_remaining + * and triggering a spurious large DMA transfer of stale data. + */ + if (fifo_len >= port->tx_remaining) + uart_xmit_advance(uport, port->tx_remaining); - uart_xmit_advance(uport, port->tx_remaining); geni_se_tx_dma_unprep(&port->se, port->tx_dma_addr, port->tx_remaining); port->tx_dma_addr = 0; port->tx_remaining = 0; From 12c97d1c15f926cd430bf5cdf8ffe878cb478165 Mon Sep 17 00:00:00 2001 From: Abel Vesa Date: Tue, 14 Apr 2026 20:05:51 +0300 Subject: [PATCH 038/337] arm64: dts: qcom: glymur: Drop RPMh CXO clocks from QMP PHYs On Glymur, all QMP PHYs except the one used by USB SS0 take their reference clock from the TCSR clock controller. Since these TCSR clocks already derive from RPMH_CXO_CLK as their sole parent, there is no need to provide an extra `clkref` clock to the PHY nodes. Drop the extra RPMh CXO clock inputs and use the TCSR clocks as the PHY reference clocks instead. This also fixes the devicetree schema validation, as the bindings do not allow a separate `clkref` clock. Fixes: 4eee57dd4df9 ("arm64: dts: qcom: glymur: Add USB related nodes") Reported-by: Krzysztof Kozlowski Reported-by: Rob Herring Closes: https://lore.kernel.org/r/20260410145205.GA554754-robh@kernel.org/ Signed-off-by: Abel Vesa Reviewed-by: Konrad Dybcio Reviewed-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20260414-dts-glymur-drop-rpmh-cxo-clk-from-qmpphys-v1-1-ab12d77c4aec@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- arch/arm64/boot/dts/qcom/glymur.dtsi | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/glymur.dtsi b/arch/arm64/boot/dts/qcom/glymur.dtsi index f23cf81ddb77..82436984485d 100644 --- a/arch/arm64/boot/dts/qcom/glymur.dtsi +++ b/arch/arm64/boot/dts/qcom/glymur.dtsi @@ -2314,11 +2314,9 @@ usb_mp_qmpphy0: phy@fa3000 { clocks = <&gcc GCC_USB3_MP_PHY_AUX_CLK>, <&tcsr TCSR_USB3_0_CLKREF_EN>, - <&rpmhcc RPMH_CXO_CLK>, <&gcc GCC_USB3_MP_PHY_COM_AUX_CLK>, <&gcc GCC_USB3_MP_PHY_PIPE_0_CLK>; clock-names = "aux", - "clkref", "ref", "com_aux", "pipe"; @@ -2343,11 +2341,9 @@ usb_mp_qmpphy1: phy@fa5000 { clocks = <&gcc GCC_USB3_MP_PHY_AUX_CLK>, <&tcsr TCSR_USB3_1_CLKREF_EN>, - <&rpmhcc RPMH_CXO_CLK>, <&gcc GCC_USB3_MP_PHY_COM_AUX_CLK>, <&gcc GCC_USB3_MP_PHY_PIPE_1_CLK>; clock-names = "aux", - "clkref", "ref", "com_aux", "pipe"; @@ -2482,15 +2478,13 @@ usb_1_qmpphy: phy@fde000 { reg = <0x0 0x00fde000 0x0 0x8000>; clocks = <&gcc GCC_USB3_SEC_PHY_AUX_CLK>, - <&rpmhcc RPMH_CXO_CLK>, + <&tcsr TCSR_USB4_1_CLKREF_EN>, <&gcc GCC_USB3_SEC_PHY_COM_AUX_CLK>, - <&gcc GCC_USB3_SEC_PHY_PIPE_CLK>, - <&tcsr TCSR_USB4_1_CLKREF_EN>; + <&gcc GCC_USB3_SEC_PHY_PIPE_CLK>; clock-names = "aux", "ref", "com_aux", - "usb3_pipe", - "clkref"; + "usb3_pipe"; power-domains = <&gcc GCC_USB_1_PHY_GDSC>; @@ -3750,15 +3744,13 @@ usb_2_qmpphy: phy@88e1000 { reg = <0x0 0x088e1000 0x0 0x8000>; clocks = <&gcc GCC_USB3_TERT_PHY_AUX_CLK>, - <&rpmhcc RPMH_CXO_CLK>, + <&tcsr TCSR_USB4_2_CLKREF_EN>, <&gcc GCC_USB3_TERT_PHY_COM_AUX_CLK>, - <&gcc GCC_USB3_TERT_PHY_PIPE_CLK>, - <&tcsr TCSR_USB4_2_CLKREF_EN>; + <&gcc GCC_USB3_TERT_PHY_PIPE_CLK>; clock-names = "aux", "ref", "com_aux", - "usb3_pipe", - "clkref"; + "usb3_pipe"; power-domains = <&gcc GCC_USB_2_PHY_GDSC>; From 6f89d96fff65aec1ff12bc566fca0eb1bb59e16e Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 12 May 2026 18:26:54 +0200 Subject: [PATCH 039/337] Input: atlas - check ACPI_COMPANION() against NULL Every platform driver can be forced to match a device that doesn't match its list of device IDs because of device_match_driver_override(), so platform drivers that rely on the existence of a device's ACPI companion object need to verify its presence. Accordingly, add a requisite ACPI_COMPANION() check against NULL to the atlas_btns driver. Fixes: b8303880b641 ("Input: atlas - convert ACPI driver to a platform one") Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/8696590.T7Z3S40VBb@rafael.j.wysocki Signed-off-by: Dmitry Torokhov --- drivers/input/misc/atlas_btns.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/input/misc/atlas_btns.c b/drivers/input/misc/atlas_btns.c index 47b31725e850..835ad45a9d65 100644 --- a/drivers/input/misc/atlas_btns.c +++ b/drivers/input/misc/atlas_btns.c @@ -60,11 +60,15 @@ static acpi_status acpi_atlas_button_handler(u32 function, static int atlas_acpi_button_probe(struct platform_device *pdev) { - struct acpi_device *device = ACPI_COMPANION(&pdev->dev); + struct acpi_device *device; acpi_status status; int i; int err; + device = ACPI_COMPANION(&pdev->dev); + if (!device) + return -ENODEV; + input_dev = input_allocate_device(); if (!input_dev) { pr_err("unable to allocate input device\n"); From 4b15b03166cc5d28e9912287b1f9b6607c8710ec Mon Sep 17 00:00:00 2001 From: Val Packett Date: Wed, 11 Mar 2026 21:53:37 -0300 Subject: [PATCH 040/337] arm64: dts: qcom: x1-dell-thena: remove i2c20 (battery SMBus) and reserve its pins i2c20 is used by the battmgr service on the ADSP to communicate with the SBS interface of the battery. Initializing it from Linux would break the battmgr functionality when booted in EL2. Mark those pins as reserved. Fixes: e7733b42111c ("arm64: dts: qcom: Add support for Dell Inspiron 7441 / Latitude 7455") Reviewed-by: Konrad Dybcio Reviewed-by: Abel Vesa Signed-off-by: Val Packett Link: https://lore.kernel.org/r/20260312005731.12488-2-val@packett.cool Signed-off-by: Bjorn Andersson --- arch/arm64/boot/dts/qcom/x1-dell-thena.dtsi | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/x1-dell-thena.dtsi b/arch/arm64/boot/dts/qcom/x1-dell-thena.dtsi index 0d9a324cc6cc..db291730130c 100644 --- a/arch/arm64/boot/dts/qcom/x1-dell-thena.dtsi +++ b/arch/arm64/boot/dts/qcom/x1-dell-thena.dtsi @@ -982,12 +982,6 @@ &i2c8 { status = "okay"; }; -&i2c20 { - clock-frequency = <400000>; - - status = "okay"; -}; - &lpass_tlmm { spkr_01_sd_n_active: spkr-01-sd-n-active-state { pins = "gpio12"; @@ -1308,6 +1302,7 @@ right_tweeter: speaker@0,1 { &tlmm { gpio-reserved-ranges = <44 4>, /* SPI11 (TPM) */ <76 4>, /* SPI19 (TZ Protected) */ + <80 2>, /* I2C20 (Battery SMBus) */ <238 1>; /* UFS Reset */ cam_rgb_default: cam-rgb-default-state { From 16ca52bc209fa4bf9239cd9e5643e95533476b58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Bazaes?= Date: Wed, 13 May 2026 21:35:49 -0400 Subject: [PATCH 041/337] Input: synaptics - add LEN2058 to SMBus passlist for ThinkPad E490 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Lenovo ThinkPad E490 (PNP ID: LEN2058) has a Synaptics TM3471-020 touchpad that supports SMBus/RMI4 mode but is not listed in smbus_pnp_ids[]. Without this entry, RMI4 over SMBus is not enabled by default, and the touchpad falls back to PS/2 mode. Adding LEN2058 to the passlist enables automatic RMI4 detection without requiring the psmouse.synaptics_intertouch parameter, and matches the behavior of similar ThinkPad models already in the list (E480/LEN2054, E580/LEN2055). Tested on ThinkPad E490 with kernel 7.0.5-zen1 and Arch Linux. RMI4 over SMBus is confirmed working without any kernel parameters. Signed-off-by: Nicolás Bazaes Assisted-by: Claude:claude-sonnet-4-6 Link: https://patch.msgid.link/20260514013552.14234-1-contacto@bazaes.cl Cc: stable@vger.kernel.org Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/synaptics.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index 26071128f43a..c70502e24031 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -190,6 +190,7 @@ static const char * const smbus_pnp_ids[] = { "LEN2044", /* L470 */ "LEN2054", /* E480 */ "LEN2055", /* E580 */ + "LEN2058", /* E490 */ "LEN2068", /* T14 Gen 1 */ "SYN1221", /* TUXEDO InfinityBook Pro 14 v5 */ "SYN3003", /* HP EliteBook 850 G1 */ From f133bd4b5daf71bccdde0ad1a4f47fac76a6bfb1 Mon Sep 17 00:00:00 2001 From: Tudor Ambarus Date: Tue, 5 May 2026 13:12:58 +0000 Subject: [PATCH 042/337] firmware: samsung: acpm: Fix cross-thread RX length corruption Sashiko identified a cross-thread RX length corruption bug when reviewing the thermal addition to ACPM [1]. When multiple threads concurrently send IPC requests, the ACPM polling mechanism can encounter responses belonging to other threads. To drain the queue, the driver saves these concurrent responses into an internal cache (`rx_data->cmd`) to be retrieved later by the owning thread. Previously, the driver incorrectly used `xfer->rxcnt` (the expected receive length of the *current* polling thread) when copying data for *other* threads into this cache. If the threads expected responses of different lengths, this resulted in buffer underflows (leading to reads of uninitialized memory) or potential buffer overflows. Fix this by replacing the boolean `response` flag in `struct acpm_rx_data` with `rxcnt`, caching the exact expected receive length for each specific transaction during transfer preparation. Use this cached length when saving concurrent responses. Consequently, ensure that `xfer->rxcnt` is explicitly zeroed in driver helpers (e.g., `acpm_dvfs_set_xfer`) for fire-and-forget messages to prevent uninitialized stack garbage from being interpreted as a massive expected receive length. Cc: stable@vger.kernel.org Fixes: a88927b534ba ("firmware: add Exynos ACPM protocol driver") Closes: https://sashiko.dev/#/patchset/20260420-acpm-tmu-v3-0-3dc8e93f0b26%40linaro.org [1] Reported-by: Titouan Ameline de Cadeville Closes: https://lore.kernel.org/r/20260426210255.73674-1-titouan.ameline@gmail.com/ Signed-off-by: Tudor Ambarus Link: https://patch.msgid.link/20260505-acpm-fixes-sashiko-reports-v5-1-43b5ee7f1674@linaro.org Signed-off-by: Krzysztof Kozlowski --- drivers/firmware/samsung/exynos-acpm-dvfs.c | 3 +++ drivers/firmware/samsung/exynos-acpm.c | 15 ++++++++------- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/drivers/firmware/samsung/exynos-acpm-dvfs.c b/drivers/firmware/samsung/exynos-acpm-dvfs.c index 06bdf62dea1f..fdea7aa24ca0 100644 --- a/drivers/firmware/samsung/exynos-acpm-dvfs.c +++ b/drivers/firmware/samsung/exynos-acpm-dvfs.c @@ -31,6 +31,9 @@ static void acpm_dvfs_set_xfer(struct acpm_xfer *xfer, u32 *cmd, size_t cmdlen, if (response) { xfer->rxcnt = cmdlen; xfer->rxd = cmd; + } else { + xfer->rxcnt = 0; + xfer->rxd = NULL; } } diff --git a/drivers/firmware/samsung/exynos-acpm.c b/drivers/firmware/samsung/exynos-acpm.c index 16c46ed60837..e95edc350efa 100644 --- a/drivers/firmware/samsung/exynos-acpm.c +++ b/drivers/firmware/samsung/exynos-acpm.c @@ -104,12 +104,12 @@ struct acpm_queue { * * @cmd: pointer to where the data shall be saved. * @n_cmd: number of 32-bit commands. - * @response: true if the client expects the RX data. + * @rxcnt: expected length of the response in 32-bit words. */ struct acpm_rx_data { u32 *cmd; size_t n_cmd; - bool response; + size_t rxcnt; }; #define ACPM_SEQNUM_MAX 64 @@ -199,7 +199,7 @@ static void acpm_get_saved_rx(struct acpm_chan *achan, const struct acpm_rx_data *rx_data = &achan->rx_data[tx_seqnum - 1]; u32 rx_seqnum; - if (!rx_data->response) + if (!rx_data->rxcnt) return; rx_seqnum = FIELD_GET(ACPM_PROTOCOL_SEQNUM, rx_data->cmd[0]); @@ -256,7 +256,7 @@ static int acpm_get_rx(struct acpm_chan *achan, const struct acpm_xfer *xfer) seqnum = rx_seqnum - 1; rx_data = &achan->rx_data[seqnum]; - if (rx_data->response) { + if (rx_data->rxcnt) { if (rx_seqnum == tx_seqnum) { __ioread32_copy(xfer->rxd, addr, xfer->rxcnt); rx_set = true; @@ -268,7 +268,8 @@ static int acpm_get_rx(struct acpm_chan *achan, const struct acpm_xfer *xfer) * clear yet the bitmap. It will be cleared * after the response is copied to the request. */ - __ioread32_copy(rx_data->cmd, addr, xfer->rxcnt); + __ioread32_copy(rx_data->cmd, addr, + rx_data->rxcnt); } } else { clear_bit(seqnum, achan->bitmap_seqnum); @@ -380,8 +381,8 @@ static void acpm_prepare_xfer(struct acpm_chan *achan, /* Clear data for upcoming responses */ rx_data = &achan->rx_data[achan->seqnum - 1]; memset(rx_data->cmd, 0, sizeof(*rx_data->cmd) * rx_data->n_cmd); - if (xfer->rxd) - rx_data->response = true; + /* zero means no response expected */ + rx_data->rxcnt = xfer->rxcnt; /* Flag the index based on seqnum. (seqnum: 1~63, bitmap: 0~62) */ set_bit(achan->seqnum - 1, achan->bitmap_seqnum); From b66829b17f6385cc9ffbcbe2476d532d2e3121ad Mon Sep 17 00:00:00 2001 From: Tudor Ambarus Date: Tue, 5 May 2026 13:12:59 +0000 Subject: [PATCH 043/337] firmware: samsung: acpm: Fix mailbox channel leak on probe error Sashiko identified the leak at [1]. The ACPM driver allocates hardware mailbox channels using `mbox_request_channel()` during `acpm_channels_init()`. However, the driver lacked a `.remove` callback and did not free these channels on subsequent error paths inside `acpm_probe()`. Additionally, if `acpm_achan_alloc_cmds()` failed during the channel initialization loop, the function returned immediately, bypassing the manual cleanup and permanently leaking any channels successfully requested in previous loop iterations. Fix this by modifying `acpm_free_mbox_chans()` to match the `devres` action signature and registering it via `devm_add_action_or_reset()`. Cc: stable@vger.kernel.org Fixes: a88927b534ba ("firmware: add Exynos ACPM protocol driver") Closes: https://sashiko.dev/#/patchset/20260420-acpm-tmu-v3-0-3dc8e93f0b26%40linaro.org [1] Signed-off-by: Tudor Ambarus Link: https://patch.msgid.link/20260505-acpm-fixes-sashiko-reports-v5-2-43b5ee7f1674@linaro.org Signed-off-by: Krzysztof Kozlowski --- drivers/firmware/samsung/exynos-acpm.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/firmware/samsung/exynos-acpm.c b/drivers/firmware/samsung/exynos-acpm.c index e95edc350efa..9766425a44ab 100644 --- a/drivers/firmware/samsung/exynos-acpm.c +++ b/drivers/firmware/samsung/exynos-acpm.c @@ -527,10 +527,11 @@ static int acpm_achan_alloc_cmds(struct acpm_chan *achan) /** * acpm_free_mbox_chans() - free mailbox channels. - * @acpm: pointer to driver data. + * @data: pointer to driver data. */ -static void acpm_free_mbox_chans(struct acpm_info *acpm) +static void acpm_free_mbox_chans(void *data) { + struct acpm_info *acpm = data; int i; for (i = 0; i < acpm->num_chans; i++) @@ -558,6 +559,10 @@ static int acpm_channels_init(struct acpm_info *acpm) if (!acpm->chans) return -ENOMEM; + ret = devm_add_action_or_reset(dev, acpm_free_mbox_chans, acpm); + if (ret) + return dev_err_probe(dev, ret, "Failed to add mbox free action.\n"); + chans_shmem = acpm->sram_base + readl(&shmem->chans); for (i = 0; i < acpm->num_chans; i++) { @@ -579,10 +584,8 @@ static int acpm_channels_init(struct acpm_info *acpm) cl->dev = dev; achan->chan = mbox_request_channel(cl, 0); - if (IS_ERR(achan->chan)) { - acpm_free_mbox_chans(acpm); + if (IS_ERR(achan->chan)) return PTR_ERR(achan->chan); - } } return 0; From a9aba21a539c668a66b58eeb08ad3909e5a54c2a Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Wed, 1 Apr 2026 18:29:24 +0300 Subject: [PATCH 044/337] iio: adc: nxp-sar-adc: fix division by zero in write_raw Add a validation check for the sampling frequency value before using it as a divisor. A user writing zero or a negative value to the sampling_frequency sysfs attribute triggers a division by zero in the kernel. Also prevent unsigned integer underflow when the computed cycle count is smaller than NXP_SAR_ADC_CONV_TIME, which would wrap the u32 inpsamp to a huge value. Fixes: 4434072a893e ("iio: adc: Add the NXP SAR ADC support for the s32g2/3 platforms") Signed-off-by: Antoniu Miclaus Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/adc/nxp-sar-adc.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/iio/adc/nxp-sar-adc.c b/drivers/iio/adc/nxp-sar-adc.c index 705dd7da1bd2..1711cae7d872 100644 --- a/drivers/iio/adc/nxp-sar-adc.c +++ b/drivers/iio/adc/nxp-sar-adc.c @@ -569,6 +569,9 @@ static int nxp_sar_adc_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec switch (mask) { case IIO_CHAN_INFO_SAMP_FREQ: + if (val <= 0) + return -EINVAL; + /* * Configures the sample period duration in terms of the SAR * controller clock. The minimum acceptable value is 8. @@ -577,7 +580,11 @@ static int nxp_sar_adc_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec * sampling timing which gives us the number of cycles expected. * The value is 8-bit wide, consequently the max value is 0xFF. */ - inpsamp = clk_get_rate(info->clk) / val - NXP_SAR_ADC_CONV_TIME; + inpsamp = clk_get_rate(info->clk) / val; + if (inpsamp < NXP_SAR_ADC_CONV_TIME) + return -EINVAL; + + inpsamp -= NXP_SAR_ADC_CONV_TIME; nxp_sar_adc_conversion_timing_set(info, inpsamp); return 0; From a093999355084bdbfe6e97f1dd232e58a1525f0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Monin?= Date: Wed, 1 Apr 2026 17:24:58 +0200 Subject: [PATCH 045/337] iio: buffer: Fix DMA fence leak in iio_buffer_enqueue_dmabuf() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit iio_buffer_enqueue_dmabuf() allocates a struct iio_dma_fence (104 bytes, kmalloc-128) via kmalloc_obj()+dma_fence_init(), which sets the initial kref to 1. It then calls dma_resv_add_fence() which takes a second reference (kref=2), and stores a raw pointer in block->fence. On the success path the function returns without calling dma_fence_put() to release the initial reference, so every buffer enqueue permanently leaks one kmalloc-128 allocation. The iio_buffer_cleanup() work item only releases the temporary reference taken during completion signalling by iio_buffer_signal_dmabuf_done(); the initial reference from dma_fence_init() is never released. With four iio_rwdev instances at 240kHz and 512 samples per buffer, this produces ~1875 kmalloc-128 allocations per second matching the observed slab growth exactly. A test with ftrace confirmed that the dma_fence_destroy event was never triggered. Fix by calling dma_fence_put() after dma_resv_add_fence(), transferring ownership of the fence to the DMA reservation object. The DMA fence then gets properly discarded after being signalled. Fixes: 3e26d9f08fbe0 ("iio: core: Add new DMABUF interface infrastructure") Originally-by: James Nuss Signed-off-by: Benoît Monin Reviewed-by: Paul Cercueil Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-buffer.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index 46f36a6ed271..5c3df993bea2 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -1909,6 +1909,7 @@ static int iio_buffer_enqueue_dmabuf(struct iio_dev_buffer_pair *ib, dma_resv_add_fence(dmabuf->resv, &fence->base, dma_to_ram ? DMA_RESV_USAGE_WRITE : DMA_RESV_USAGE_READ); + dma_fence_put(&fence->base); dma_resv_unlock(dmabuf->resv); cookie = dma_fence_begin_signalling(); From 49f79cd28f1e3333cbe0d616ce59ead0b24bf34e Mon Sep 17 00:00:00 2001 From: Advait Dhamorikar Date: Tue, 7 Apr 2026 12:50:59 +0530 Subject: [PATCH 046/337] iio: magnetometer: st_magn: fix default DRDY pin selection for LIS2MDL The device tree binding for st,lis2mdl does not support st,drdy-int-pin property. However, when no platform data is provided and the property is absent, the driver falls back to default_magn_pdata which hardcodes drdy_int_pin = 2. This causes `st_sensors_set_drdy_int_pin` to fail with -EINVAL because the LIS2MDL sensor settings have no INT2 DRDY mask defined. Fix this by checking the sensor's INT2 DRDY mask availability at probe time and selecting the appropriate default pin. Sensors that do not support INT2 DRDY will default to INT1, while all others retain the existing default of INT2. Fixes: 38934daf7b5c ("iio: magnetometer: st_magn: Provide default platform data") Signed-off-by: Advait Dhamorikar Reviewed-by: Andy Shevchenko Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/st_magn_core.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/iio/magnetometer/st_magn_core.c b/drivers/iio/magnetometer/st_magn_core.c index ef348d316c00..7644bd04654b 100644 --- a/drivers/iio/magnetometer/st_magn_core.c +++ b/drivers/iio/magnetometer/st_magn_core.c @@ -506,6 +506,11 @@ static const struct st_sensors_platform_data default_magn_pdata = { .drdy_int_pin = 2, }; +/* LIS2MDL only supports DRDY on INT1 */ +static const struct st_sensors_platform_data alt_magn_pdata = { + .drdy_int_pin = 1, +}; + static int st_magn_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *ch, int *val, int *val2, long mask) @@ -628,8 +633,12 @@ int st_magn_common_probe(struct iio_dev *indio_dev) mdata->current_fullscale = &mdata->sensor_settings->fs.fs_avl[0]; mdata->odr = mdata->sensor_settings->odr.odr_avl[0].hz; - if (!pdata) - pdata = (struct st_sensors_platform_data *)&default_magn_pdata; + if (!pdata) { + if (mdata->sensor_settings->drdy_irq.int2.mask) + pdata = (struct st_sensors_platform_data *)&default_magn_pdata; + else + pdata = (struct st_sensors_platform_data *)&alt_magn_pdata; + } err = st_sensors_init_sensor(indio_dev, pdata); if (err < 0) From 1f4f0bcc5255dec5c4c3a1551bf49d8c33b69b20 Mon Sep 17 00:00:00 2001 From: Aldo Conte Date: Tue, 7 Apr 2026 17:17:01 +0200 Subject: [PATCH 047/337] iio: light: cm3323: fix reg_conf not being initialized correctly The code stores the return value of i2c_smbus_write_word_data() in data->reg_conf; however, this value represents the result of the write operation and not the value actually written to the configuration register. This meant that the contents of data->reg_conf did not truly reflect the contents of the hardware register. Instead, save the value of the register before the write and use this value in the I2C write. The bug was found by code inspection: i2c_smbus_write_word_data() returns 0 on success, not the value written to the register. Tested using i2c-stub on a Raspberry Pi 3B running a custom 6.19.10 kernel. Before loading the driver, the configuration register 0x00 CM3323_CMD_CONF was populated with 0x0030 using `i2cset -y 11 0x10 0x00 0x0030 w`, encoding an integration time of 320ms in bits[6:4]. Due to incorrect initialization of data->reg_conf in cm3323_init(), the print of integration_time returns 0.040000 instead of the expected 0.320000. This happens because the read of the integration_time depends on cm3323_get_it_bits() that is based on the value of data->reg_conf, which is erroneously set to 0. With this fix applied, data->reg_conf correctly saves 0x0030 after init and the successive integration_time reports 0.320000 as expected. Fixes: 8b0544263761 ("iio: light: Add support for Capella CM3323 color sensor") Cc: stable@vger.kernel.org Signed-off-by: Aldo Conte Signed-off-by: Jonathan Cameron --- drivers/iio/light/cm3323.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/iio/light/cm3323.c b/drivers/iio/light/cm3323.c index 79ad6e2209ca..0fe61b8a7029 100644 --- a/drivers/iio/light/cm3323.c +++ b/drivers/iio/light/cm3323.c @@ -89,15 +89,14 @@ static int cm3323_init(struct iio_dev *indio_dev) /* enable sensor and set auto force mode */ ret &= ~(CM3323_CONF_SD_BIT | CM3323_CONF_AF_BIT); + data->reg_conf = ret; - ret = i2c_smbus_write_word_data(data->client, CM3323_CMD_CONF, ret); + ret = i2c_smbus_write_word_data(data->client, CM3323_CMD_CONF, data->reg_conf); if (ret < 0) { dev_err(&data->client->dev, "Error writing reg_conf\n"); return ret; } - data->reg_conf = ret; - return 0; } From 8ce176501f836634f9c0419c0820140f968e9dc5 Mon Sep 17 00:00:00 2001 From: Shuvam Pandey Date: Mon, 6 Apr 2026 15:38:24 +0545 Subject: [PATCH 048/337] iio: adc: nxp-sar-adc: zero-initialize dma_slave_config nxp_sar_adc_start_cyclic_dma() only fills the RX-side members of dma_slave_config before passing it to dmaengine_slave_config(). Zero-initialize the structure so unused members do not contain stack garbage. Some DMA engines consult optional dma_slave_config fields, so leaving them uninitialized can cause DMA setup failures. Fixes: 4434072a893e ("iio: adc: Add the NXP SAR ADC support for the s32g2/3 platforms") Signed-off-by: Shuvam Pandey Reviewed-by: David Lechner Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/adc/nxp-sar-adc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/adc/nxp-sar-adc.c b/drivers/iio/adc/nxp-sar-adc.c index 1711cae7d872..8f4ed3db94f0 100644 --- a/drivers/iio/adc/nxp-sar-adc.c +++ b/drivers/iio/adc/nxp-sar-adc.c @@ -676,7 +676,7 @@ static void nxp_sar_adc_dma_cb(void *data) static int nxp_sar_adc_start_cyclic_dma(struct iio_dev *indio_dev) { struct nxp_sar_adc *info = iio_priv(indio_dev); - struct dma_slave_config config; + struct dma_slave_config config = { }; struct dma_async_tx_descriptor *desc; int ret; From 387c86b582e0782ab332e7bfcd4e6e3f93922961 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 9 Apr 2026 15:40:47 +0200 Subject: [PATCH 049/337] iio: pressure: bmp280: fix stack leak in bmp580 trigger handler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit bmp580_trigger_handler() declares its scan buffer on the stack without an initializer and then memcpy()s 3 bytes of 24-bit sensor data into each 4-byte __le32 field. The high byte of comp_temp and comp_press is left uninitialized, and the channel storagebits is 32, so two bytes of stack are pushed to userspace per scan. This is a regression from when the buffer lived in the private data, the move to a stack-local struct dropped the implicit zeroing. bme280_trigger_handler() was fixed up to handle this bug, but this driver was not fixed because there was no padding hole, but rather a short-fill issue. Fix this all by just zero-initializing the structure on the stack. Cc: Jonathan Cameron Cc: David Lechner Cc: "Nuno Sá" Cc: Andy Shevchenko Fixes: 872c8014e05e ("iio: pressure: bmp280: drop sensor_data array") Cc: stable Assisted-by: gregkh_clanker_t1000 Signed-off-by: Greg Kroah-Hartman Reviewed-by: David Lechner Signed-off-by: Jonathan Cameron --- drivers/iio/pressure/bmp280-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/pressure/bmp280-core.c b/drivers/iio/pressure/bmp280-core.c index d983ce9c0b99..9b489766e457 100644 --- a/drivers/iio/pressure/bmp280-core.c +++ b/drivers/iio/pressure/bmp280-core.c @@ -2616,7 +2616,7 @@ static irqreturn_t bmp580_trigger_handler(int irq, void *p) __le32 comp_temp; __le32 comp_press; aligned_s64 timestamp; - } buffer; + } buffer = { }; int ret; guard(mutex)(&data->lock); From c9d8e9adaa63150ef7e833480b799d0bab83a276 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 9 Apr 2026 15:40:48 +0200 Subject: [PATCH 050/337] iio: imu: st_lsm6dsx: fix stack leak in tagged FIFO buffer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The tagged FIFO path declares iio_buff on the stack with __aligned(8) but no initializer, but there is a hole in the structure, which will then leak to userspace as ST_LSM6DSX_SAMPLE_SIZE bytes (6) will be copied, but the space between that and the timestamp are not initialized. Commit c14edb4d0bdc ("iio:imu:st_lsm6dsx Fix alignment and data leak issues") moved the untagged FIFO path to a kzalloc'd buffer in hw->scan, but for the tagged path it only added the alignment qualifier and not the initializer :( Fix this by just zero-initializing the structure on the stack. Cc: Lorenzo Bianconi Cc: Jonathan Cameron Cc: David Lechner Cc: "Nuno Sá" Cc: Andy Shevchenko Fixes: c14edb4d0bdc ("iio:imu:st_lsm6dsx Fix alignment and data leak issues") Cc: stable Assisted-by: gregkh_clanker_t1000 Signed-off-by: Greg Kroah-Hartman Reviewed-by: David Lechner Signed-off-by: Jonathan Cameron --- drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c index 5b28a3ffcc3d..48291203d1cd 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c @@ -609,7 +609,7 @@ int st_lsm6dsx_read_tagged_fifo(struct st_lsm6dsx_hw *hw) * must be passed a buffer that is aligned to 8 bytes so * as to allow insertion of a naturally aligned timestamp. */ - u8 iio_buff[ST_LSM6DSX_IIO_BUFF_SIZE] __aligned(8); + u8 iio_buff[ST_LSM6DSX_IIO_BUFF_SIZE] __aligned(8) = { }; u8 tag; bool reset_ts = false; int i, err, read_len; From 474f8928d50b09f7dcf507049f08732640b88b49 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 9 Apr 2026 15:40:49 +0200 Subject: [PATCH 051/337] iio: imu: adis16550: fix stack leak in trigger handler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit adis16550_trigger_handler() declares the scan data array on the stack without initializing it. The memcpy() at the bottom fills only the first 28 bytes (TEMP + 6 channels of GYRO/ACCEL data), and iio_push_to_buffers_with_timestamp() writes the s64 timestamp at the 8-byte-aligned offset 32. Bytes 28-31 remain uninitialized stack data which leaks to userspace on ever trigger. Fix this all by just zero-initializing the structure on the stack. Cc: Lars-Peter Clausen Cc: Michael Hennerich Cc: Jonathan Cameron Cc: David Lechner Cc: "Nuno Sá" Cc: Andy Shevchenko Fixes: e4570f4bb231 ("iio: imu: adis16550: align buffers for timestamp") Cc: stable Assisted-by: gregkh_clanker_t1000 Signed-off-by: Greg Kroah-Hartman Reviewed-by: David Lechner Signed-off-by: Jonathan Cameron --- drivers/iio/imu/adis16550.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/imu/adis16550.c b/drivers/iio/imu/adis16550.c index 1f2af506f4bd..75679612052f 100644 --- a/drivers/iio/imu/adis16550.c +++ b/drivers/iio/imu/adis16550.c @@ -836,7 +836,7 @@ static irqreturn_t adis16550_trigger_handler(int irq, void *p) u16 dummy; bool valid; struct iio_poll_func *pf = p; - __be32 data[ADIS16550_MAX_SCAN_DATA] __aligned(8); + __be32 data[ADIS16550_MAX_SCAN_DATA] __aligned(8) = { }; struct iio_dev *indio_dev = pf->indio_dev; struct adis16550 *st = iio_priv(indio_dev); struct adis *adis = iio_device_get_drvdata(indio_dev); From 5ace794c3ded38038a1f97f9ea26b9a8c835c111 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 10 Apr 2026 13:12:13 +0300 Subject: [PATCH 052/337] iio: adc: qcom-spmi-adc5-gen3: Fix off by one in adc5_gen3_get_fw_channel_data() The > in "if (chan > ADC5_MAX_CHANNEL)" should be >= to prevent an out of bound read of the adc->data->adc_chans[] array. Fixes: baff45179e90 ("iio: adc: Add support for QCOM PMIC5 Gen3 ADC") Signed-off-by: Dan Carpenter Reviewed-by: Konrad Dybcio Signed-off-by: Jonathan Cameron --- drivers/iio/adc/qcom-spmi-adc5-gen3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/adc/qcom-spmi-adc5-gen3.c b/drivers/iio/adc/qcom-spmi-adc5-gen3.c index f8168a14b907..48c793b18d11 100644 --- a/drivers/iio/adc/qcom-spmi-adc5-gen3.c +++ b/drivers/iio/adc/qcom-spmi-adc5-gen3.c @@ -482,7 +482,7 @@ static int adc5_gen3_get_fw_channel_data(struct adc5_chip *adc, sid = FIELD_GET(ADC5_GEN3_VIRTUAL_SID_MASK, chan); chan = FIELD_GET(ADC5_GEN3_CHANNEL_MASK, chan); - if (chan > ADC5_MAX_CHANNEL) + if (chan >= ADC5_MAX_CHANNEL) return dev_err_probe(dev, -EINVAL, "%s invalid channel number %d\n", name, chan); From f9bbd943c34a9ad60e593a4b99ce2394e4e2381b Mon Sep 17 00:00:00 2001 From: Salah Triki Date: Mon, 27 Apr 2026 21:12:38 +0100 Subject: [PATCH 053/337] iio: adc: mt6359: fix unchecked return value in mt6358_read_imp In mt6358_read_imp(), the variable val_v is passed to regmap_read() but the return value is not checked. If the read fails, val_v remains uninitialized and its random stack content is subsequently reported as a measurement result. Initialize val_v to zero to ensure a predictable value is reported in case of bus failure and to prevent potential stack data leakage. This also satisfies static analyzers that might otherwise flag the variable as used uninitialized. Fixes: 3587914bf61d ("iio: adc: Add support for MediaTek MT6357/8/9 Auxiliary ADC") Signed-off-by: Salah Triki Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/adc/mt6359-auxadc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/iio/adc/mt6359-auxadc.c b/drivers/iio/adc/mt6359-auxadc.c index 6b9ed9b1fde2..1d9724ef0983 100644 --- a/drivers/iio/adc/mt6359-auxadc.c +++ b/drivers/iio/adc/mt6359-auxadc.c @@ -497,6 +497,7 @@ static int mt6358_read_imp(struct mt6359_auxadc *adc_dev, return ret; /* Read the params before stopping */ + val_v = 0; regmap_read(regmap, reg_adc0 + (cinfo->imp_adc_num << 1), &val_v); mt6358_stop_imp_conv(adc_dev); From d0a228d903425e653f18a4341e60c0538afb6d41 Mon Sep 17 00:00:00 2001 From: Salah Triki Date: Mon, 27 Apr 2026 22:33:19 +0100 Subject: [PATCH 054/337] iio: dac: max5821: fix return value check in powerdown sync The function max5821_sync_powerdown_mode() returned the result of i2c_master_send() directly. If a partial transfer occurred, it would be incorrectly treated as a success by the caller. While the caller currently handles the positive return value of 2 as success, this patch refactors the function to return 0 on full success and -EIO on short writes. This ensures robust error handling for incomplete transfers and improves code maintainability by using sizeof(outbuf). Fixes: 472988972737 ("iio: add support of the max5821") Signed-off-by: Salah Triki Reviewed-by: Andy Shevchenko Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/dac/max5821.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/iio/dac/max5821.c b/drivers/iio/dac/max5821.c index e7e29359f8fe..dd4e35460195 100644 --- a/drivers/iio/dac/max5821.c +++ b/drivers/iio/dac/max5821.c @@ -90,6 +90,7 @@ static int max5821_sync_powerdown_mode(struct max5821_data *data, const struct iio_chan_spec *chan) { u8 outbuf[2]; + int ret; outbuf[0] = MAX5821_EXTENDED_COMMAND_MODE; @@ -103,7 +104,13 @@ static int max5821_sync_powerdown_mode(struct max5821_data *data, else outbuf[1] |= MAX5821_EXTENDED_POWER_UP; - return i2c_master_send(data->client, outbuf, 2); + ret = i2c_master_send(data->client, outbuf, sizeof(outbuf)); + if (ret < 0) + return ret; + if (ret != sizeof(outbuf)) + return -EIO; + + return 0; } static ssize_t max5821_write_dac_powerdown(struct iio_dev *indio_dev, From ba121d7582361fe74405f32724976aeff5c35177 Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Mon, 27 Apr 2026 19:26:31 +0800 Subject: [PATCH 055/337] iio: adc: meson-saradc: fix calibration buffer leak on error meson_sar_adc_temp_sensor_init() allocates a buffer with nvmem_cell_read(), but the old code leaked it if syscon_regmap_lookup_by_phandle() failed. Fix this by adding missing kfree(buf). Fixes: d6f2eac64403 ("iio: adc: meson: no devm for nvmem_cell_get") Signed-off-by: Felix Gu Signed-off-by: Jonathan Cameron --- drivers/iio/adc/meson_saradc.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/iio/adc/meson_saradc.c b/drivers/iio/adc/meson_saradc.c index 23991a3612bd..000e39ca5c62 100644 --- a/drivers/iio/adc/meson_saradc.c +++ b/drivers/iio/adc/meson_saradc.c @@ -817,9 +817,11 @@ static int meson_sar_adc_temp_sensor_init(struct iio_dev *indio_dev) } priv->tsc_regmap = syscon_regmap_lookup_by_phandle(dev->of_node, "amlogic,hhi-sysctrl"); - if (IS_ERR(priv->tsc_regmap)) + if (IS_ERR(priv->tsc_regmap)) { + kfree(buf); return dev_err_probe(dev, PTR_ERR(priv->tsc_regmap), "failed to get amlogic,hhi-sysctrl regmap\n"); + } trimming_bits = priv->param->temperature_trimming_bits; trimming_mask = BIT(trimming_bits) - 1; From eedf7602fbd929e97e0c480da501dc7a34beb2a8 Mon Sep 17 00:00:00 2001 From: Sanjay Chitroda Date: Sun, 26 Apr 2026 14:47:04 +0530 Subject: [PATCH 056/337] iio: ssp_sensors: cancel delayed work_refresh on remove The work_refresh may still be pending or running when the device is removed, cancel the delayed work_refresh in remove path. Fixes: 50dd64d57eee ("iio: common: ssp_sensors: Add sensorhub driver") Signed-off-by: Sanjay Chitroda Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/common/ssp_sensors/ssp_dev.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/iio/common/ssp_sensors/ssp_dev.c b/drivers/iio/common/ssp_sensors/ssp_dev.c index da09c9f3ceb6..e2538a84c812 100644 --- a/drivers/iio/common/ssp_sensors/ssp_dev.c +++ b/drivers/iio/common/ssp_sensors/ssp_dev.c @@ -590,6 +590,7 @@ static void ssp_remove(struct spi_device *spi) ssp_clean_pending_list(data); free_irq(data->spi->irq, data); + cancel_delayed_work_sync(&data->work_refresh); timer_delete_sync(&data->wdt_timer); cancel_work_sync(&data->work_wdt); From ecae2ae606d493cf11457946436335bd0e726663 Mon Sep 17 00:00:00 2001 From: Rodrigo Alencar Date: Fri, 1 May 2026 10:14:54 +0100 Subject: [PATCH 057/337] iio: dac: ad5686: fix ref bit initialization for single-channel parts The reference bit position was ignored when writing the register at the probe() function (!!val was used). When such bit is 1, internal voltage reference is disabled so that an external one can be used. For multi-channel devices, bit 0 of the Internal Reference Setup command behaves the same way, so AD5686_REF_BIT_MSK is created. The issue exists since support for single-channel devices were first introduced. Fixes: be1b24d24541 ("iio:dac:ad5686: Add AD5691R/AD5692R/AD5693/AD5693R support") Reviewed-by: Andy Shevchenko Signed-off-by: Rodrigo Alencar Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ad5686.c | 6 +++--- drivers/iio/dac/ad5686.h | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c index 4b18498aa074..b85d5c5a864b 100644 --- a/drivers/iio/dac/ad5686.c +++ b/drivers/iio/dac/ad5686.c @@ -509,7 +509,7 @@ int ad5686_probe(struct device *dev, break; case AD5686_REGMAP: cmd = AD5686_CMD_INTERNAL_REFER_SETUP; - ref_bit_msk = 0; + ref_bit_msk = AD5686_REF_BIT_MSK; break; case AD5693_REGMAP: cmd = AD5686_CMD_CONTROL_REG; @@ -520,9 +520,9 @@ int ad5686_probe(struct device *dev, return -EINVAL; } - val = (has_external_vref | ref_bit_msk); + val = has_external_vref ? ref_bit_msk : 0; - ret = st->write(st, cmd, 0, !!val); + ret = st->write(st, cmd, 0, val); if (ret) return ret; diff --git a/drivers/iio/dac/ad5686.h b/drivers/iio/dac/ad5686.h index e7d36bae3e59..36e16c5c4581 100644 --- a/drivers/iio/dac/ad5686.h +++ b/drivers/iio/dac/ad5686.h @@ -46,6 +46,7 @@ #define AD5310_REF_BIT_MSK BIT(8) #define AD5683_REF_BIT_MSK BIT(12) +#define AD5686_REF_BIT_MSK BIT(0) #define AD5693_REF_BIT_MSK BIT(12) /** From d01220ee5e43c65a206df827b39bf5cf5f7b9dce Mon Sep 17 00:00:00 2001 From: Rodrigo Alencar Date: Fri, 1 May 2026 10:14:55 +0100 Subject: [PATCH 058/337] iio: dac: ad5686: fix input raw value check Fix range check for input raw value, which is off by one, i.e., for a 10-bit DAC the max valid value is 1023, but 1 << 10 equals 1024, which passes the previous check, allowing an out-of-range write. The issue exists since the ad5686 driver was first introduced. Fixes: c2f37c8dcadc ("iio: dac: New driver for AD5686R, AD5685R, AD5684R Digital to analog converters") Reviewed-by: Andy Shevchenko Signed-off-by: Rodrigo Alencar Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ad5686.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c index b85d5c5a864b..27878a6318ff 100644 --- a/drivers/iio/dac/ad5686.c +++ b/drivers/iio/dac/ad5686.c @@ -154,7 +154,7 @@ static int ad5686_write_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_RAW: - if (val > (1 << chan->scan_type.realbits) || val < 0) + if (val >= (1 << chan->scan_type.realbits) || val < 0) return -EINVAL; mutex_lock(&st->lock); From 6f5ed4f2c7c83f33344e0ba179f72a12e5dad4a4 Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Thu, 30 Apr 2026 21:29:06 +0800 Subject: [PATCH 059/337] iio: buffer: hw-consumer: fix use-after-free in error path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the err_put_buffers cleanup path of iio_hw_consumer_alloc(), the code was using list_for_each_entry() to iterate through buffers while calling iio_buffer_put() which can free the current buffer if refcount drops to 0. The list_for_each_entry() loop macro then evaluates buf->head.next to continue iteration, accessing the freed buffer. Fix this by using list_for_each_entry_safe(). Fixes: 48b66f8f936f ("iio: Add hardware consumer buffer support") Reported-by: sashiko Closes: https://sashiko.dev/#/patchset/20260427-iio_buf-v1-1-2bbdac844647%40gmail.com Signed-off-by: Felix Gu Reviewed-by: Andy Shevchenko Reviewed-by: Nuno Sá Reviewed-by: Maxwell Doose Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/buffer/industrialio-hw-consumer.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/iio/buffer/industrialio-hw-consumer.c b/drivers/iio/buffer/industrialio-hw-consumer.c index 24d7df603760..700528c9a0a4 100644 --- a/drivers/iio/buffer/industrialio-hw-consumer.c +++ b/drivers/iio/buffer/industrialio-hw-consumer.c @@ -85,7 +85,7 @@ static struct hw_consumer_buffer *iio_hw_consumer_get_buffer( */ struct iio_hw_consumer *iio_hw_consumer_alloc(struct device *dev) { - struct hw_consumer_buffer *buf; + struct hw_consumer_buffer *buf, *tmp; struct iio_hw_consumer *hwc; struct iio_channel *chan; int ret; @@ -116,7 +116,7 @@ struct iio_hw_consumer *iio_hw_consumer_alloc(struct device *dev) return hwc; err_put_buffers: - list_for_each_entry(buf, &hwc->buffers, head) + list_for_each_entry_safe(buf, tmp, &hwc->buffers, head) iio_buffer_put(&buf->buffer); iio_channel_release_all(hwc->channels); err_free_hwc: From ebd250c2581ec46c64c73fdfa918c9a7f757505e Mon Sep 17 00:00:00 2001 From: Kim Seer Paller Date: Tue, 5 May 2026 12:34:32 +0800 Subject: [PATCH 060/337] iio: dac: ad3530r: Fix AD3531/AD3531R powerdown mode strings The AD3531/AD3531R has different output operating modes from the AD3530/AD3530R. According to the AD3531/AD3531R datasheet, the powerdown modes are: 01: 500 Ohm output impedance 10: 3.85 kOhm output impedance 11: 16 kOhm output impedance The driver currently uses the AD3530R modes (1k, 7.7k, 32k) for all variants, which is incorrect for AD3531/AD3531R. Add AD3531R-specific powerdown mode strings and assign them to the AD3531/AD3531R chip variants. Fixes: 93583174a3df ("iio: dac: ad3530r: Add driver for AD3530R and AD3531R") Signed-off-by: Kim Seer Paller Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ad3530r.c | 54 +++++++++++++++++++++++++++++---------- 1 file changed, 40 insertions(+), 14 deletions(-) diff --git a/drivers/iio/dac/ad3530r.c b/drivers/iio/dac/ad3530r.c index b97b46090d80..d9db3226ecd6 100644 --- a/drivers/iio/dac/ad3530r.c +++ b/drivers/iio/dac/ad3530r.c @@ -105,6 +105,12 @@ static const char * const ad3530r_powerdown_modes[] = { "32kohm_to_gnd", }; +static const char * const ad3531r_powerdown_modes[] = { + "500ohm_to_gnd", + "3.85kohm_to_gnd", + "16kohm_to_gnd", +}; + static int ad3530r_get_powerdown_mode(struct iio_dev *indio_dev, const struct iio_chan_spec *chan) { @@ -133,6 +139,13 @@ static const struct iio_enum ad3530r_powerdown_mode_enum = { .set = ad3530r_set_powerdown_mode, }; +static const struct iio_enum ad3531r_powerdown_mode_enum = { + .items = ad3531r_powerdown_modes, + .num_items = ARRAY_SIZE(ad3531r_powerdown_modes), + .get = ad3530r_get_powerdown_mode, + .set = ad3530r_set_powerdown_mode, +}; + static ssize_t ad3530r_get_dac_powerdown(struct iio_dev *indio_dev, uintptr_t private, const struct iio_chan_spec *chan, @@ -276,7 +289,20 @@ static const struct iio_chan_spec_ext_info ad3530r_ext_info[] = { { } }; -#define AD3530R_CHAN(_chan) \ +static const struct iio_chan_spec_ext_info ad3531r_ext_info[] = { + { + .name = "powerdown", + .shared = IIO_SEPARATE, + .read = ad3530r_get_dac_powerdown, + .write = ad3530r_set_dac_powerdown, + }, + IIO_ENUM("powerdown_mode", IIO_SEPARATE, &ad3531r_powerdown_mode_enum), + IIO_ENUM_AVAILABLE("powerdown_mode", IIO_SHARED_BY_TYPE, + &ad3531r_powerdown_mode_enum), + { } +}; + +#define AD3530R_CHAN(_chan, _ext_info) \ { \ .type = IIO_VOLTAGE, \ .indexed = 1, \ @@ -284,25 +310,25 @@ static const struct iio_chan_spec_ext_info ad3530r_ext_info[] = { .output = 1, \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ BIT(IIO_CHAN_INFO_SCALE), \ - .ext_info = ad3530r_ext_info, \ + .ext_info = _ext_info, \ } static const struct iio_chan_spec ad3530r_channels[] = { - AD3530R_CHAN(0), - AD3530R_CHAN(1), - AD3530R_CHAN(2), - AD3530R_CHAN(3), - AD3530R_CHAN(4), - AD3530R_CHAN(5), - AD3530R_CHAN(6), - AD3530R_CHAN(7), + AD3530R_CHAN(0, ad3530r_ext_info), + AD3530R_CHAN(1, ad3530r_ext_info), + AD3530R_CHAN(2, ad3530r_ext_info), + AD3530R_CHAN(3, ad3530r_ext_info), + AD3530R_CHAN(4, ad3530r_ext_info), + AD3530R_CHAN(5, ad3530r_ext_info), + AD3530R_CHAN(6, ad3530r_ext_info), + AD3530R_CHAN(7, ad3530r_ext_info), }; static const struct iio_chan_spec ad3531r_channels[] = { - AD3530R_CHAN(0), - AD3530R_CHAN(1), - AD3530R_CHAN(2), - AD3530R_CHAN(3), + AD3530R_CHAN(0, ad3531r_ext_info), + AD3530R_CHAN(1, ad3531r_ext_info), + AD3530R_CHAN(2, ad3531r_ext_info), + AD3530R_CHAN(3, ad3531r_ext_info), }; static const struct ad3530r_chip_info ad3530_chip = { From 4701e471c16866e7aa8f5e6a3a6b0d31e097e2c9 Mon Sep 17 00:00:00 2001 From: Salah Triki Date: Tue, 5 May 2026 08:10:24 +0100 Subject: [PATCH 061/337] iio: temperature: tsys01: fix broken PROM checksum validation The current implementation of tsys01_crc_valid() incorrectly sums the first word (n_prom[0]) repeatedly instead of iterating over the 8 words retrieved from the PROM. This leads to a checksum mismatch and probe failure on hardware. According to the TSYS01 datasheet, the PROM consists of 8 words. A valid check must iterate through all 8 words to verify the integrity of the calibration data. The current driver only checks the first word 8 times. Note: This fix was identified during a code audit and is based on datasheet specifications. It has not been tested on real hardware. Fixes: 43e53407f680 ("Add tsys01 meas-spec driver support") Signed-off-by: Salah Triki Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/temperature/tsys01.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/temperature/tsys01.c b/drivers/iio/temperature/tsys01.c index 334bba6fdae6..104dd45598b0 100644 --- a/drivers/iio/temperature/tsys01.c +++ b/drivers/iio/temperature/tsys01.c @@ -119,7 +119,7 @@ static bool tsys01_crc_valid(u16 *n_prom) u8 sum = 0; for (cnt = 0; cnt < TSYS01_PROM_WORDS_NB; cnt++) - sum += ((n_prom[0] >> 8) + (n_prom[0] & 0xFF)); + sum += ((n_prom[cnt] >> 8) + (n_prom[cnt] & 0xFF)); return (sum == 0); } From 5237c3175cae5ab05f18878cec3301a04403859e Mon Sep 17 00:00:00 2001 From: Rodrigo Alencar Date: Tue, 5 May 2026 13:35:04 +0100 Subject: [PATCH 062/337] iio: dac: ad5686: acquire lock when doing powerdown control Protect access of pwr_down_mode and pwr_down_mask fields with existing mutex lock. Each channel exposes their own attributes for controlling powerdown modes and powerdown state. This fixes potential race conditions as those the write functions perform non-atomic read-modify-write operations to those pwr_down_* fields. This issue exists since the ad5686 driver was first introduced. Fixes: c2f37c8dcadc ("iio: dac: New driver for AD5686R, AD5685R, AD5684R Digital to analog converters") Signed-off-by: Rodrigo Alencar Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ad5686.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c index 27878a6318ff..2e443fcfeb39 100644 --- a/drivers/iio/dac/ad5686.c +++ b/drivers/iio/dac/ad5686.c @@ -30,6 +30,8 @@ static int ad5686_get_powerdown_mode(struct iio_dev *indio_dev, { struct ad5686_state *st = iio_priv(indio_dev); + guard(mutex)(&st->lock); + return ((st->pwr_down_mode >> (chan->channel * 2)) & 0x3) - 1; } @@ -39,6 +41,8 @@ static int ad5686_set_powerdown_mode(struct iio_dev *indio_dev, { struct ad5686_state *st = iio_priv(indio_dev); + guard(mutex)(&st->lock); + st->pwr_down_mode &= ~(0x3 << (chan->channel * 2)); st->pwr_down_mode |= ((mode + 1) << (chan->channel * 2)); @@ -57,6 +61,8 @@ static ssize_t ad5686_read_dac_powerdown(struct iio_dev *indio_dev, { struct ad5686_state *st = iio_priv(indio_dev); + guard(mutex)(&st->lock); + return sysfs_emit(buf, "%d\n", !!(st->pwr_down_mask & (0x3 << (chan->channel * 2)))); } @@ -77,6 +83,8 @@ static ssize_t ad5686_write_dac_powerdown(struct iio_dev *indio_dev, if (ret) return ret; + guard(mutex)(&st->lock); + if (readin) st->pwr_down_mask |= (0x3 << (chan->channel * 2)); else From 8aeaf25a85263a7a43357e16ad78ab969f6f8aeb Mon Sep 17 00:00:00 2001 From: Rodrigo Alencar Date: Tue, 5 May 2026 13:35:05 +0100 Subject: [PATCH 063/337] iio: dac: ad5686: fix powerdown control on dual-channel devices Fix powerdown control by using a proper bit shift for the powerdown mask values. During initialization, powerdown bits are initialized so that unused bits are set to 1 and the correct bit shift is used. Dual-channel devices use one-hot encoding in the address and that reflects on the position of the powerdown bits, which are not channel-index based for that case. Quad-channel devices also use one-hot encoding for the channel address but the result of log2(address) coincides with the channel index value. Mask as 0x3U is used rather than 0x3, because shift can reach value of 30 (last channel of a 16-channel device), which would mess with the sign bit. The issue was introduced when first adding support for dual-channel devices, which overlooked powerdown control differences. Fixes: 7dc8faeab3e3 ("iio: dac: ad5686: add support for AD5338R") Signed-off-by: Rodrigo Alencar Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ad5686.c | 40 ++++++++++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c index 2e443fcfeb39..a7213bc6b156 100644 --- a/drivers/iio/dac/ad5686.c +++ b/drivers/iio/dac/ad5686.c @@ -25,26 +25,37 @@ static const char * const ad5686_powerdown_modes[] = { "three_state" }; +static inline unsigned int ad5686_pd_mask_shift(const struct iio_chan_spec *chan) +{ + if (chan->channel == chan->address) + return chan->channel * 2; + + /* one-hot encoding is used in dual/quad channel devices */ + return __ffs(chan->address) * 2; +} + static int ad5686_get_powerdown_mode(struct iio_dev *indio_dev, const struct iio_chan_spec *chan) { + unsigned int shift = ad5686_pd_mask_shift(chan); struct ad5686_state *st = iio_priv(indio_dev); guard(mutex)(&st->lock); - return ((st->pwr_down_mode >> (chan->channel * 2)) & 0x3) - 1; + return ((st->pwr_down_mode >> shift) & 0x3U) - 1; } static int ad5686_set_powerdown_mode(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, unsigned int mode) { + unsigned int shift = ad5686_pd_mask_shift(chan); struct ad5686_state *st = iio_priv(indio_dev); guard(mutex)(&st->lock); - st->pwr_down_mode &= ~(0x3 << (chan->channel * 2)); - st->pwr_down_mode |= ((mode + 1) << (chan->channel * 2)); + st->pwr_down_mode &= ~(0x3U << shift); + st->pwr_down_mode |= (mode + 1) << shift; return 0; } @@ -59,12 +70,12 @@ static const struct iio_enum ad5686_powerdown_mode_enum = { static ssize_t ad5686_read_dac_powerdown(struct iio_dev *indio_dev, uintptr_t private, const struct iio_chan_spec *chan, char *buf) { + unsigned int shift = ad5686_pd_mask_shift(chan); struct ad5686_state *st = iio_priv(indio_dev); guard(mutex)(&st->lock); - return sysfs_emit(buf, "%d\n", !!(st->pwr_down_mask & - (0x3 << (chan->channel * 2)))); + return sysfs_emit(buf, "%d\n", !!(st->pwr_down_mask & (0x3U << shift))); } static ssize_t ad5686_write_dac_powerdown(struct iio_dev *indio_dev, @@ -86,9 +97,9 @@ static ssize_t ad5686_write_dac_powerdown(struct iio_dev *indio_dev, guard(mutex)(&st->lock); if (readin) - st->pwr_down_mask |= (0x3 << (chan->channel * 2)); + st->pwr_down_mask |= 0x3U << ad5686_pd_mask_shift(chan); else - st->pwr_down_mask &= ~(0x3 << (chan->channel * 2)); + st->pwr_down_mask &= ~(0x3U << ad5686_pd_mask_shift(chan)); switch (st->chip_info->regmap_type) { case AD5310_REGMAP: @@ -468,7 +479,7 @@ int ad5686_probe(struct device *dev, { struct ad5686_state *st; struct iio_dev *indio_dev; - unsigned int val, ref_bit_msk; + unsigned int val, ref_bit_msk, shift; bool has_external_vref; u8 cmd; int ret, i; @@ -492,9 +503,18 @@ int ad5686_probe(struct device *dev, has_external_vref = ret != -ENODEV; st->vref_mv = has_external_vref ? ret / 1000 : st->chip_info->int_vref_mv; + /* Initialize masks to all ones provided the max shift (last channel) */ + shift = ad5686_pd_mask_shift(&st->chip_info->channels[st->chip_info->num_channels - 1]); + st->pwr_down_mask = GENMASK(shift + 1, 0); + st->pwr_down_mode = GENMASK(shift + 1, 0); + /* Set all the power down mode for all channels to 1K pulldown */ - for (i = 0; i < st->chip_info->num_channels; i++) - st->pwr_down_mode |= (0x01 << (i * 2)); + for (i = 0; i < st->chip_info->num_channels; i++) { + shift = ad5686_pd_mask_shift(&st->chip_info->channels[i]); + st->pwr_down_mask &= ~(0x3U << shift); /* powered up state */ + st->pwr_down_mode &= ~(0x3U << shift); + st->pwr_down_mode |= 0x01U << shift; + } indio_dev->name = name; indio_dev->info = &ad5686_info; From 6bdc3023d62ed5c7d591f0eb27a5adb37fb892ae Mon Sep 17 00:00:00 2001 From: David Carlier Date: Tue, 5 May 2026 14:37:48 +0100 Subject: [PATCH 064/337] iio: gyro: itg3200: fix i2c read into the wrong stack location itg3200_read_all_channels() takes `__be16 *buf' as a parameter and fills the i2c_msg destination as `(char *)&buf'. Since `buf' is the parameter (a pointer), `&buf' is the address of the local pointer slot on the stack of itg3200_read_all_channels(), not the address of the caller's scan buffer. The (char *) cast hides the type mismatch. i2c_transfer() therefore writes ITG3200_SCAN_ELEMENTS * sizeof(s16) = 8 bytes into the parameter's stack slot, which is discarded when the function returns. The caller's scan buffer in itg3200_trigger_handler() is never written to, so iio_push_to_buffers_with_timestamp() pushes uninitialised stack contents to userspace via /dev/iio:deviceX every scan -- both a functional bug (no actual gyroscope or temperature data is delivered through the triggered buffer) and an information leak. The non-buffered read_raw() path is unaffected: it goes through itg3200_read_reg_s16() which uses `&out' on a local s16 value, where that is correct. Drop the spurious `&' so the i2c read writes into the caller's buffer. Fixes: 9dbf091da080 ("iio: gyro: Add itg3200") Cc: stable@vger.kernel.org Signed-off-by: David Carlier Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/gyro/itg3200_buffer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/gyro/itg3200_buffer.c b/drivers/iio/gyro/itg3200_buffer.c index cf97adfa9727..87efa2c74ca4 100644 --- a/drivers/iio/gyro/itg3200_buffer.c +++ b/drivers/iio/gyro/itg3200_buffer.c @@ -34,7 +34,7 @@ static int itg3200_read_all_channels(struct i2c_client *i2c, __be16 *buf) .addr = i2c->addr, .flags = i2c->flags | I2C_M_RD, .len = ITG3200_SCAN_ELEMENTS * sizeof(s16), - .buf = (char *)&buf, + .buf = (char *)buf, }, }; From 422b5bbf333f75fb486855ad0eedc23cf21f3277 Mon Sep 17 00:00:00 2001 From: Salah Triki Date: Thu, 7 May 2026 20:07:51 +0100 Subject: [PATCH 065/337] iio: adc: viperboard: Fix error handling in vprbrd_iio_read_raw MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver proceeds to the reception phase even if the preceding transmission fails. This uses a goto error label for an early bail out and ensures the mutex is properly unlocked in case of failure. Fixes: ffd8a6e7a778 ("iio: adc: Add viperboard adc driver") Signed-off-by: Salah Triki Reviewed-by: Joshua Crofts Reviewed-by: Maxwell Doose Reviewed-by: Nuno Sá Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/adc/viperboard_adc.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/iio/adc/viperboard_adc.c b/drivers/iio/adc/viperboard_adc.c index 9bb0b83c8f67..6efe1c618ef7 100644 --- a/drivers/iio/adc/viperboard_adc.c +++ b/drivers/iio/adc/viperboard_adc.c @@ -70,8 +70,10 @@ static int vprbrd_iio_read_raw(struct iio_dev *iio_dev, VPRBRD_USB_TYPE_OUT, 0x0000, 0x0000, admsg, sizeof(struct vprbrd_adc_msg), VPRBRD_USB_TIMEOUT_MS); if (ret != sizeof(struct vprbrd_adc_msg)) { - dev_err(&iio_dev->dev, "usb send error on adc read\n"); + mutex_unlock(&vb->lock); error = -EREMOTEIO; + dev_err(&iio_dev->dev, "usb send error on adc read\n"); + goto error; } ret = usb_control_msg(vb->usb_dev, From 765aaba18413a66f6c8fe8416336ca9b3dd98a79 Mon Sep 17 00:00:00 2001 From: Mihai Sain Date: Mon, 9 Mar 2026 09:53:29 +0200 Subject: [PATCH 066/337] ARM: dts: microchip: sam9x7: fix GMAC clock configuration The GMAC node incorrectly listed four clocks, including a separate tx_clk and a TSU GCK clock sourced from ID 67. According to the SAM9X7 clocking scheme, the GMAC uses only three clocks: HCLK, PCLK, and the TSU GCK derived from the GMAC peripheral clock (ID 24). Remove the unused tx_clk, update the clock-names accordingly, and correct the assigned clock to use GCK 24 instead of GCK 67. This aligns the device tree with the actual hardware clock topology and prevents misconfiguration of the GMAC clock tree. [root@SAM9X75 ~]$ cat /sys/kernel/debug/clk/clk_summary | grep gmac gmac_gclk 1 1 1 266666666 0 0 50000 Y f802c000.ethernet tsu_clk f802c000.ethernet tsu_clk gmac_clk 2 2 0 266666666 0 0 50000 Y f802c000.ethernet hclk f802c000.ethernet pclk Fixes: 41af45af8bc3 ("ARM: dts: at91: sam9x7: add device tree for SoC") Signed-off-by: Mihai Sain Link: https://lore.kernel.org/r/20260309075329.1528-5-mihai.sain@microchip.com [claudiu.beznea: massaged the patch description] Signed-off-by: Claudiu Beznea --- arch/arm/boot/dts/microchip/sam9x7.dtsi | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/arm/boot/dts/microchip/sam9x7.dtsi b/arch/arm/boot/dts/microchip/sam9x7.dtsi index d242d7a934d0..c680a5033b6b 100644 --- a/arch/arm/boot/dts/microchip/sam9x7.dtsi +++ b/arch/arm/boot/dts/microchip/sam9x7.dtsi @@ -990,9 +990,9 @@ gmac: ethernet@f802c000 { <62 IRQ_TYPE_LEVEL_HIGH 3>, /* Queue 3 */ <63 IRQ_TYPE_LEVEL_HIGH 3>, /* Queue 4 */ <64 IRQ_TYPE_LEVEL_HIGH 3>; /* Queue 5 */ - clocks = <&pmc PMC_TYPE_PERIPHERAL 24>, <&pmc PMC_TYPE_PERIPHERAL 24>, <&pmc PMC_TYPE_GCK 24>, <&pmc PMC_TYPE_GCK 67>; - clock-names = "hclk", "pclk", "tx_clk", "tsu_clk"; - assigned-clocks = <&pmc PMC_TYPE_GCK 67>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 24>, <&pmc PMC_TYPE_PERIPHERAL 24>, <&pmc PMC_TYPE_GCK 24>; + clock-names = "hclk", "pclk", "tsu_clk"; + assigned-clocks = <&pmc PMC_TYPE_GCK 24>; assigned-clock-rates = <266666666>; status = "disabled"; }; From a7511dcd9dd4bc55d123f9b800c8a4ed2662e5c6 Mon Sep 17 00:00:00 2001 From: Stepan Ionichev Date: Thu, 14 May 2026 22:43:42 +0500 Subject: [PATCH 067/337] auxdisplay: line-display: fix OOB read on zero-length message_store() linedisp_display() unconditionally reads msg[count - 1] before checking whether count is zero, so a write of zero bytes to the message sysfs attribute hits msg[-1]: write(fd, "", 0); -> message_store(..., buf, count=0) -> linedisp_display(linedisp, buf, count=0) -> msg[count - 1] == '\n' ; OOB read The kernfs write buffer for that store is a 1-byte allocation (kernfs_fop_write_iter() does kmalloc(len + 1) with len == 0), so msg[-1] is a 1-byte read before the slab object. On a KASAN-enabled kernel this trips an out-of-bounds report and panics; on stock kernels it silently reads adjacent slab data and, if that byte happens to be '\n', the following count-- wraps ssize_t 0 to -1 and is then passed to kmemdup_nul(). linedisp_display() is reached from the message_store() sysfs callback (drivers/auxdisplay/line-display.c message attribute, mode 0644) and from the in-tree initial-message setup with count == -1, so the OOB path is only userspace-triggerable via zero-byte writes; vfs_write() does not short-circuit on count == 0 and kernfs_fop_write_iter() dispatches the store callback regardless. Guard the trailing-newline trim with a count check. The existing if (!count) block then takes the clear-display path unchanged. Affects every auxdisplay driver that registers via linedisp_register() / linedisp_attach(): ht16k33, max6959, img-ascii-lcd, seg-led-gpio. Fixes: 7e76aece6f03 ("auxdisplay: Extract character line display core support") Signed-off-by: Stepan Ionichev Signed-off-by: Andy Shevchenko --- drivers/auxdisplay/line-display.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/auxdisplay/line-display.c b/drivers/auxdisplay/line-display.c index fb6d9294140d..915eb5cd96b2 100644 --- a/drivers/auxdisplay/line-display.c +++ b/drivers/auxdisplay/line-display.c @@ -173,7 +173,7 @@ static int linedisp_display(struct linedisp *linedisp, const char *msg, count = strlen(msg); /* if the string ends with a newline, trim it */ - if (msg[count - 1] == '\n') + if (count && msg[count - 1] == '\n') count--; if (!count) { From 4dc76c305a73bacaf330bebf723a181427bb4540 Mon Sep 17 00:00:00 2001 From: Stepan Ionichev Date: Fri, 15 May 2026 18:30:04 +0500 Subject: [PATCH 068/337] auxdisplay: Kconfig: drop unneeded quotes in PANEL_BOOT_MESSAGE dep The PANEL_BOOT_MESSAGE dependency uses a quoted-string comparison against the PANEL_CHANGE_MESSAGE bool symbol: depends on PANEL_CHANGE_MESSAGE="y" This is the only such pattern under drivers/auxdisplay/ (grep shows no other Kconfig file in the tree uses depends on FOO="y" with quotes for a plain bool symbol). The quoted form is parsed by Kconfig but is not idiomatic; the common form for the same intent is the unquoted tristate-style dependency: depends on PANEL_CHANGE_MESSAGE which evaluates true when PANEL_CHANGE_MESSAGE is y or m. Since PANEL_CHANGE_MESSAGE is declared as bool (not tristate), there is no behaviour change in practice: y is the only enabled value either form can match. Drop the quoted comparison so the dependency matches the prevailing kernel Kconfig style and so it is obvious to readers that the comparison works. Suggested-by: Andy Shevchenko Link: https://lore.kernel.org/r/CAHp75VfsA_LsbEKjxoeMdbhPbWj7OHZ7=0SYNA3c=ZLj_M94Bw@mail.gmail.com Signed-off-by: Stepan Ionichev Signed-off-by: Andy Shevchenko --- drivers/auxdisplay/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/auxdisplay/Kconfig b/drivers/auxdisplay/Kconfig index bedc6133f970..1ea7c039160c 100644 --- a/drivers/auxdisplay/Kconfig +++ b/drivers/auxdisplay/Kconfig @@ -327,7 +327,7 @@ config PANEL_CHANGE_MESSAGE say 'N' and keep the default message with the version. config PANEL_BOOT_MESSAGE - depends on PANEL_CHANGE_MESSAGE="y" + depends on PANEL_CHANGE_MESSAGE string "New initialization message" default "" help From b02900c85a6423cf9b3dcc6b47bf060c85075e69 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 12 May 2026 13:14:59 +0300 Subject: [PATCH 069/337] usb: typec: tipd: Fix error code in tps6598x_probe() Set the error code on these two error paths. The existing code returns success. Fixes: 77ed2f4538da ("usb: typec: tipd: Use read_power_status function in probe") Fixes: 04041fd7d6ec ("usb: typec: tipd: Read data status in probe and cache its value") Cc: stable Signed-off-by: Dan Carpenter Reviewed-by: Heikki Krogerus Link: https://patch.msgid.link/agL9o7wUK1dOVBTy@stanley.mountain Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tipd/core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/typec/tipd/core.c b/drivers/usb/typec/tipd/core.c index 43faec794b95..d0b769333bd9 100644 --- a/drivers/usb/typec/tipd/core.c +++ b/drivers/usb/typec/tipd/core.c @@ -1835,6 +1835,7 @@ static int tps6598x_probe(struct i2c_client *client) goto err_role_put; if (status & TPS_STATUS_PLUG_PRESENT) { + ret = -EINVAL; if (!tps6598x_read_power_status(tps)) goto err_unregister_port; if (!tps->data->read_data_status(tps)) From 6c5dbc104dadd79fc2923497c20bae759a18758c Mon Sep 17 00:00:00 2001 From: Jeremy Erazo Date: Tue, 12 May 2026 16:05:30 +0000 Subject: [PATCH 070/337] usb: gadget: composite: fix integer underflow in WebUSB GET_URL handling The WebUSB GET_URL handler in composite_setup() narrows landing_page_length to fit the host-supplied wLength using landing_page_length = w_length - WEBUSB_URL_DESCRIPTOR_HEADER_LENGTH + landing_page_offset; If wLength is smaller than WEBUSB_URL_DESCRIPTOR_HEADER_LENGTH the unsigned subtraction wraps, and the subsequent memcpy(url_descriptor->URL, cdev->landing_page + landing_page_offset, landing_page_length - landing_page_offset); ends up copying close to UINT_MAX bytes from cdev->landing_page into cdev->req->buf. KASAN reports a slab-out-of-bounds in composite_setup on the kmalloc-2k gadget_info allocation, and FORTIFY_SOURCE traps the memcpy as a 4294967293-byte field-spanning write into url_descriptor->URL (size 252). A USB host can reach this from a single SETUP packet against any gadget that has webusb/use=1 and a landingPage configured. Handle the small-wLength case before the math: when the host requested fewer bytes than the URL descriptor header, only the header is meaningful and no URL bytes need to be copied. Setting landing_page_length to landing_page_offset makes the existing memcpy a no-op and leaves the descriptor returned to the host unchanged for all larger wLength values. Fixes: 93c473948c58 ("usb: gadget: add WebUSB landing page support") Cc: stable Signed-off-by: Jeremy Erazo Link: https://patch.msgid.link/20260512160530.352318-1-mendozayt13@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/composite.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index a902184bdf82..dc3664374596 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -2172,7 +2172,10 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) sizeof(url_descriptor->URL) - WEBUSB_URL_DESCRIPTOR_HEADER_LENGTH + landing_page_offset); - if (w_length < WEBUSB_URL_DESCRIPTOR_HEADER_LENGTH + landing_page_length) + if (w_length < WEBUSB_URL_DESCRIPTOR_HEADER_LENGTH) + landing_page_length = landing_page_offset; + else if (w_length < + WEBUSB_URL_DESCRIPTOR_HEADER_LENGTH + landing_page_length) landing_page_length = w_length - WEBUSB_URL_DESCRIPTOR_HEADER_LENGTH + landing_page_offset; From d7486952bf74e546ee3748fb14b2d07881fa6273 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 14 May 2026 19:10:06 +0200 Subject: [PATCH 071/337] usb: typec: ucsi: ccg: reject firmware images without a ':' record header do_flash() locates the first .cyacd record with p = strnchr(fw->data, fw->size, ':'); while (p < eof) { s = strnchr(p + 1, eof - p - 1, ':'); ... } If the firmware image contains no ':' byte, strnchr() returns NULL. NULL compares less than the valid kernel pointer eof, so the loop body runs and strnchr() is called with p + 1 == (void *)1 and a length of roughly (unsigned long)eof, causing a wonderful crash. The not_signed_fw fallthrough earlier in do_flash() and the chip-state branches in ccg_fw_update_needed() allow an unsigned blob to reach this loop, so a root user who can place a crafted file under /lib/firmware and write the do_flash sysfs attribute can trigger the oops. Bail out with -EINVAL when the initial strnchr() returns NULL. Assisted-by: gkh_clanker_t1000 Cc: stable Cc: Heikki Krogerus Reviewed-by: Heikki Krogerus Signed-off-by: Greg Kroah-Hartman Link: https://patch.msgid.link/2026051405-posture-shrill-7884@gregkh Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/ucsi_ccg.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/usb/typec/ucsi/ucsi_ccg.c b/drivers/usb/typec/ucsi/ucsi_ccg.c index 199799b319c2..4463c1ae96bd 100644 --- a/drivers/usb/typec/ucsi/ucsi_ccg.c +++ b/drivers/usb/typec/ucsi/ucsi_ccg.c @@ -1243,6 +1243,11 @@ static int do_flash(struct ucsi_ccg *uc, enum enum_flash_mode mode) *****************************************************************/ p = strnchr(fw->data, fw->size, ':'); + if (!p) { + dev_err(dev, "Bad FW format: no ':' record header found\n"); + err = -EINVAL; + goto release_mem; + } while (p < eof) { s = strnchr(p + 1, eof - p - 1, ':'); From d1e280334b7f0a1df441e08bd1f6a1bcc36b3bbb Mon Sep 17 00:00:00 2001 From: Michal Pecio Date: Mon, 18 May 2026 07:31:21 +0200 Subject: [PATCH 072/337] usb: core: Fix SuperSpeed root hub wMaxPacketSize There is no good reason to have wBytesPerInterval < wMaxPacketSize - either one is too low or the other too high, and we may want to warn about such descriptors. Start with cleaning up our own root hubs. USB 3.2 section 10.15.1 sets wMaxPacketSize and wBytesPerInterval of SuperSpeed hub status endpoints at 2 bytes, so reduce wMaxPacketSize from its former value of 4, which was derived from USB 2.0 spec and the kernel's USB_MAXCHILDREN limit. They don't apply because USB 3.2 10.15.2.1 specifies SuperSpeed hubs to have up to 15 ports. Suggested-by: Mathias Nyman Signed-off-by: Michal Pecio Link: https://patch.msgid.link/20260518073121.7bc1da0f.michal.pecio@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 89221f1ce769..b181b43a35dc 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -328,9 +328,7 @@ static const u8 ss_rh_config_descriptor[] = { USB_DT_ENDPOINT, /* __u8 ep_bDescriptorType; Endpoint */ 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */ 0x03, /* __u8 ep_bmAttributes; Interrupt */ - /* __le16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8) - * see hub.c:hub_configure() for details. */ - (USB_MAXCHILDREN + 1 + 7) / 8, 0x00, + 0x02, 0x00, /* __le16 ep_wMaxPacketSize; 2 bytes per USB3 10.15.1 */ 0x0c, /* __u8 ep_bInterval; (256ms -- usb 2.0 spec) */ /* one SuperSpeed endpoint companion descriptor */ From 727d045d064b7c9a24db3bce9c0485a382cb768b Mon Sep 17 00:00:00 2001 From: Michal Pecio Date: Mon, 18 May 2026 07:32:07 +0200 Subject: [PATCH 073/337] usb: core: Fix up Interrupt IN endpoints with bogus wBytesPerInterval Tao Xue found that some common devices violate USB 3.x section 9.6.7 by reporting wBytesPerInterval lower than the size of packets they actually send. I confirmed that AX88179 may set it to 0 and RTL8153 CDC configuration sets it to 8 but sends both 8 and 16 byte packets: S Ii:11:007:3 -115:128 16 < C Ii:11:007:3 0:128 8 = a1000000 01000000 S Ii:11:007:3 -115:128 16 < C Ii:11:007:3 0:128 16 = a12a0000 01000800 00000000 00000000 Most xHCI host controllers neglect interrupt bandwidth reservations and let such devices exceed theirs, some fail the URB with EOVERFLOW. Assume that wBytesPerInterval lower than wMaxPacketSize is bogus and increase it to the worst case maximum on interrupt IN endpoints. This solves xHCI problems and appears to have no other effect. Interrupt transfers are not limited to one interval and drivers submit URBs of class defined size without looking at wBytesPerInterval. Any multi- interval transfer is considered terminated by a packet shorter than wMaxPacketSize regardless of wBytesPerInterval - see USB3 8.10.3. Stay in spec on OUT endpoints and isochronous. No buggy devices are known and we don't want to risk sending more data than the device is prepared to handle or confusing isoc drivers regarding altsetting capacities guaranteed by the device itself. And don't complain when wMaxPacketSize <= wBytesPerInterval < wMaxPacketSize * (bMaxBurst+1) because enabling this seems to be the exact goal of the spec. Reported-and-tested-by: Tao Xue Closes: https://lore.kernel.org/linux-usb/20260402021400.28853-1-xuetao09@huawei.com/ Cc: stable@vger.kernel.org Signed-off-by: Michal Pecio Link: https://patch.msgid.link/20260518073207.5b7d26e7.michal.pecio@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/config.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c index 417140b012bb..d9171bf7bc88 100644 --- a/drivers/usb/core/config.c +++ b/drivers/usb/core/config.c @@ -191,7 +191,14 @@ static void usb_parse_ss_endpoint_companion(struct device *ddev, int cfgno, (desc->bMaxBurst + 1); else max_tx = 999999; - if (le16_to_cpu(desc->wBytesPerInterval) > max_tx) { + /* + * wBytesPerInterval > max_tx is bogus, but USB3 spec doesn't forbid the opposite. + * Experience shows that wBytesPerInterval < wMaxPacketSize on common interrupt IN + * endpoints is usually bogus too, and recent HCs enforce interrupt BW limits. + */ + if (le16_to_cpu(desc->wBytesPerInterval) > max_tx || + (le16_to_cpu(desc->wBytesPerInterval) < usb_endpoint_maxp(&ep->desc) && + usb_endpoint_is_int_in(&ep->desc))) { dev_notice(ddev, "%s endpoint with wBytesPerInterval of %d in " "config %d interface %d altsetting %d ep %d: " "setting to %d\n", From af8c5aa7a9c6f503d81f103d7ab4f8d759521de3 Mon Sep 17 00:00:00 2001 From: Michal Pecio Date: Mon, 18 May 2026 07:32:58 +0200 Subject: [PATCH 074/337] usb: core: Clean up SuperSpeed/eUSB2 descriptor validation logging Core usually prints endpoint addresses with 0x%X format. Change this code to use it too, instead of just %d. Particularly for IN, 0x83 seems more readable than 131. While at that, fix checkpatch warnings about multi-line quoted strings, as well as missing or doubled whitespace in those strings. Signed-off-by: Michal Pecio Link: https://patch.msgid.link/20260518073258.6532bdd5.michal.pecio@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/config.c | 37 +++++++++++-------------------------- 1 file changed, 11 insertions(+), 26 deletions(-) diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c index d9171bf7bc88..45e20c6d76c0 100644 --- a/drivers/usb/core/config.c +++ b/drivers/usb/core/config.c @@ -56,8 +56,7 @@ static void usb_parse_ssp_isoc_endpoint_companion(struct device *ddev, desc = (struct usb_ssp_isoc_ep_comp_descriptor *) buffer; if (size < USB_DT_SSP_ISOC_EP_COMP_SIZE || desc->bDescriptorType != USB_DT_SSP_ISOC_ENDPOINT_COMP) { - dev_notice(ddev, "Invalid SuperSpeedPlus isoc endpoint companion" - "for config %d interface %d altsetting %d ep %d.\n", + dev_notice(ddev, "Invalid SuperSpeedPlus isoc endpoint companion for config %d interface %d altsetting %d ep 0x%X.\n", cfgno, inum, asnum, ep->desc.bEndpointAddress); return; } @@ -91,7 +90,7 @@ static void usb_parse_eusb2_isoc_endpoint_companion(struct device *ddev, size -= h->bLength; } - dev_notice(ddev, "No eUSB2 isoc ep %d companion for config %d interface %d altsetting %d\n", + dev_notice(ddev, "No eUSB2 isoc ep 0x%X companion for config %d interface %d altsetting %d\n", ep->desc.bEndpointAddress, cfgno, inum, asnum); } @@ -115,9 +114,7 @@ static void usb_parse_ss_endpoint_companion(struct device *ddev, int cfgno, } if (desc->bDescriptorType != USB_DT_SS_ENDPOINT_COMP) { - dev_notice(ddev, "No SuperSpeed endpoint companion for config %d " - " interface %d altsetting %d ep %d: " - "using minimum values\n", + dev_notice(ddev, "No SuperSpeed endpoint companion for config %d interface %d altsetting %d ep 0x%X: using minimum values\n", cfgno, inum, asnum, ep->desc.bEndpointAddress); /* Fill in some default values. @@ -141,42 +138,32 @@ static void usb_parse_ss_endpoint_companion(struct device *ddev, int cfgno, /* Check the various values */ if (usb_endpoint_xfer_control(&ep->desc) && desc->bMaxBurst != 0) { - dev_notice(ddev, "Control endpoint with bMaxBurst = %d in " - "config %d interface %d altsetting %d ep %d: " - "setting to zero\n", desc->bMaxBurst, - cfgno, inum, asnum, ep->desc.bEndpointAddress); + dev_notice(ddev, "Control endpoint with bMaxBurst = %d in config %d interface %d altsetting %d ep 0x%X: setting to zero\n", + desc->bMaxBurst, cfgno, inum, asnum, ep->desc.bEndpointAddress); ep->ss_ep_comp.bMaxBurst = 0; } else if (desc->bMaxBurst > 15) { - dev_notice(ddev, "Endpoint with bMaxBurst = %d in " - "config %d interface %d altsetting %d ep %d: " - "setting to 15\n", desc->bMaxBurst, - cfgno, inum, asnum, ep->desc.bEndpointAddress); + dev_notice(ddev, "Endpoint with bMaxBurst = %d in config %d interface %d altsetting %d ep 0x%X: setting to 15\n", + desc->bMaxBurst, cfgno, inum, asnum, ep->desc.bEndpointAddress); ep->ss_ep_comp.bMaxBurst = 15; } if ((usb_endpoint_xfer_control(&ep->desc) || usb_endpoint_xfer_int(&ep->desc)) && desc->bmAttributes != 0) { - dev_notice(ddev, "%s endpoint with bmAttributes = %d in " - "config %d interface %d altsetting %d ep %d: " - "setting to zero\n", + dev_notice(ddev, "%s endpoint with bmAttributes = %d in config %d interface %d altsetting %d ep 0x%X: setting to zero\n", usb_endpoint_xfer_control(&ep->desc) ? "Control" : "Bulk", desc->bmAttributes, cfgno, inum, asnum, ep->desc.bEndpointAddress); ep->ss_ep_comp.bmAttributes = 0; } else if (usb_endpoint_xfer_bulk(&ep->desc) && desc->bmAttributes > 16) { - dev_notice(ddev, "Bulk endpoint with more than 65536 streams in " - "config %d interface %d altsetting %d ep %d: " - "setting to max\n", + dev_notice(ddev, "Bulk endpoint with more than 65536 streams in config %d interface %d altsetting %d ep 0x%X: setting to max\n", cfgno, inum, asnum, ep->desc.bEndpointAddress); ep->ss_ep_comp.bmAttributes = 16; } else if (usb_endpoint_xfer_isoc(&ep->desc) && !USB_SS_SSP_ISOC_COMP(desc->bmAttributes) && USB_SS_MULT(desc->bmAttributes) > 3) { - dev_notice(ddev, "Isoc endpoint has Mult of %d in " - "config %d interface %d altsetting %d ep %d: " - "setting to 3\n", + dev_notice(ddev, "Isoc endpoint has Mult of %d in config %d interface %d altsetting %d ep 0x%X: setting to 3\n", USB_SS_MULT(desc->bmAttributes), cfgno, inum, asnum, ep->desc.bEndpointAddress); ep->ss_ep_comp.bmAttributes = 2; @@ -199,9 +186,7 @@ static void usb_parse_ss_endpoint_companion(struct device *ddev, int cfgno, if (le16_to_cpu(desc->wBytesPerInterval) > max_tx || (le16_to_cpu(desc->wBytesPerInterval) < usb_endpoint_maxp(&ep->desc) && usb_endpoint_is_int_in(&ep->desc))) { - dev_notice(ddev, "%s endpoint with wBytesPerInterval of %d in " - "config %d interface %d altsetting %d ep %d: " - "setting to %d\n", + dev_notice(ddev, "%s endpoint with wBytesPerInterval of %d in config %d interface %d altsetting %d ep 0x%X: setting to %d\n", usb_endpoint_xfer_isoc(&ep->desc) ? "Isoc" : "Int", le16_to_cpu(desc->wBytesPerInterval), cfgno, inum, asnum, ep->desc.bEndpointAddress, From d922113ef91e6e7e8065e9070f349365341ba32e Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Mon, 18 May 2026 19:22:17 +0530 Subject: [PATCH 075/337] soc: qcom: ice: Fix race between qcom_ice_probe() and of_qcom_ice_get() The current platform driver design causes probe ordering races with consumers (UFS, eMMC) due to ICE's dependency on SCM firmware calls. If ICE probe fails (missing ICE SCM or DT registers), devm_of_qcom_ice_get() loops with -EPROBE_DEFER, leaving consumers non-functional even when ICE should be gracefully disabled. devm_of_qcom_ice_get() doesn't know if the ICE driver probe has failed due to above reasons or it is waiting for the SCM driver. Moreover, there is no devlink dependency between ICE and consumer drivers as 'qcom,ice' is not considered as a DT 'supplier'. So the consumer drivers have no idea of when the ICE driver is going to probe. To address these issues, store the error pointer in a global xarray with ice node phandle as a key during probe in addition to the valid ice pointer and synchronize both qcom_ice_probe() and of_qcom_ice_get() using a mutex. If the xarray entry is NULL, then it implies that the driver is not probed yet, so return -EPROBE_DEFER. If it has any error pointer, return that error pointer directly. Otherwise, add the devlink as usual and return the valid pointer to the consumer. Xarray is used instead of platform drvdata, since driver core frees the drvdata during probe failure. So it cannot be used to pass the error pointer to the consumers. Note that this change only fixes the standalone ICE DT node bindings and not the ones with 'ice' range embedded in the consumer nodes, where there is no issue. Fixes: 2afbf43a4aec ("soc: qcom: Make the Qualcomm UFS/SDCC ICE a dedicated driver") Reported-by: Sumit Garg Tested-by: Sumit Garg # OP-TEE as TZ Acked-by: Sumit Garg Cc: stable@vger.kernel.org # 6.4 Signed-off-by: Manivannan Sadhasivam Link: https://lore.kernel.org/r/20260518-qcom-ice-fix-v7-1-2a595382185b@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/ice.c | 38 +++++++++++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/drivers/soc/qcom/ice.c b/drivers/soc/qcom/ice.c index b203bc685cad..91991864b4a3 100644 --- a/drivers/soc/qcom/ice.c +++ b/drivers/soc/qcom/ice.c @@ -16,6 +16,7 @@ #include #include #include +#include #include @@ -113,6 +114,9 @@ struct qcom_ice { u8 hwkm_version; }; +static DEFINE_XARRAY(ice_handles); +static DEFINE_MUTEX(ice_mutex); + static bool qcom_ice_check_supported(struct qcom_ice *ice) { u32 regval = qcom_ice_readl(ice, QCOM_ICE_REG_VERSION); @@ -631,6 +635,8 @@ static struct qcom_ice *of_qcom_ice_get(struct device *dev) return qcom_ice_create(&pdev->dev, base); } + guard(mutex)(&ice_mutex); + /* * If the consumer node does not provider an 'ice' reg range * (legacy DT binding), then it must at least provide a phandle @@ -647,12 +653,13 @@ static struct qcom_ice *of_qcom_ice_get(struct device *dev) return ERR_PTR(-EPROBE_DEFER); } - ice = platform_get_drvdata(pdev); - if (!ice) { - dev_err(dev, "Cannot get ice instance from %s\n", - dev_name(&pdev->dev)); + ice = xa_load(&ice_handles, pdev->dev.of_node->phandle); + if (IS_ERR_OR_NULL(ice)) { platform_device_put(pdev); - return ERR_PTR(-EPROBE_DEFER); + if (!ice) + return ERR_PTR(-EPROBE_DEFER); + else + return ice; } link = device_link_add(dev, &pdev->dev, DL_FLAG_AUTOREMOVE_SUPPLIER); @@ -716,24 +723,40 @@ EXPORT_SYMBOL_GPL(devm_of_qcom_ice_get); static int qcom_ice_probe(struct platform_device *pdev) { + unsigned long phandle = pdev->dev.of_node->phandle; struct qcom_ice *engine; void __iomem *base; + guard(mutex)(&ice_mutex); + base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) { dev_warn(&pdev->dev, "ICE registers not found\n"); + /* Store the error pointer for devm_of_qcom_ice_get() */ + xa_store(&ice_handles, phandle, (__force void *)base, GFP_KERNEL); return PTR_ERR(base); } engine = qcom_ice_create(&pdev->dev, base); - if (IS_ERR(engine)) + if (IS_ERR(engine)) { + /* Store the error pointer for devm_of_qcom_ice_get() */ + xa_store(&ice_handles, phandle, engine, GFP_KERNEL); return PTR_ERR(engine); + } - platform_set_drvdata(pdev, engine); + xa_store(&ice_handles, phandle, engine, GFP_KERNEL); return 0; } +static void qcom_ice_remove(struct platform_device *pdev) +{ + unsigned long phandle = pdev->dev.of_node->phandle; + + guard(mutex)(&ice_mutex); + xa_store(&ice_handles, phandle, NULL, GFP_KERNEL); +} + static const struct of_device_id qcom_ice_of_match_table[] = { { .compatible = "qcom,inline-crypto-engine" }, { }, @@ -742,6 +765,7 @@ MODULE_DEVICE_TABLE(of, qcom_ice_of_match_table); static struct platform_driver qcom_ice_driver = { .probe = qcom_ice_probe, + .remove = qcom_ice_remove, .driver = { .name = "qcom-ice", .of_match_table = qcom_ice_of_match_table, From 5a4dc805a80e6fe303d6a4748cd451ea15987ffd Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Mon, 18 May 2026 19:22:18 +0530 Subject: [PATCH 076/337] soc: qcom: ice: Return -ENODEV if the ICE platform device is not found By the time the consumer driver calls devm_of_qcom_ice_get(), all the platform devices for ICE nodes would've been created by of_platform_default_populate(). So for the absence of any platform device, -ENODEV should not returned, not -EPROBE_DEFER. Fixes: 2afbf43a4aec ("soc: qcom: Make the Qualcomm UFS/SDCC ICE a dedicated driver") Tested-by: Sumit Garg # OP-TEE as TZ Acked-by: Sumit Garg Signed-off-by: Manivannan Sadhasivam Link: https://lore.kernel.org/r/20260518-qcom-ice-fix-v7-2-2a595382185b@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/ice.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/soc/qcom/ice.c b/drivers/soc/qcom/ice.c index 91991864b4a3..85deb9ea4a68 100644 --- a/drivers/soc/qcom/ice.c +++ b/drivers/soc/qcom/ice.c @@ -650,7 +650,7 @@ static struct qcom_ice *of_qcom_ice_get(struct device *dev) pdev = of_find_device_by_node(node); if (!pdev) { dev_err(dev, "Cannot find device node %s\n", node->name); - return ERR_PTR(-EPROBE_DEFER); + return ERR_PTR(-ENODEV); } ice = xa_load(&ice_handles, pdev->dev.of_node->phandle); From b9ab7217dd7d567c50311afa94d6d6746cb77e04 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Mon, 18 May 2026 19:22:19 +0530 Subject: [PATCH 077/337] soc: qcom: ice: Return proper error codes from devm_of_qcom_ice_get() instead of NULL devm_of_qcom_ice_get() currently returns NULL if ICE SCM is not available or "qcom,ice" property is not found in DT. But this confuses the clients since NULL doesn't convey the reason for failure. So return proper error codes instead of NULL. Reported-by: Sumit Garg Reviewed-by: Konrad Dybcio Tested-by: Sumit Garg # OP-TEE as TZ Acked-by: Sumit Garg Signed-off-by: Manivannan Sadhasivam Link: https://lore.kernel.org/r/20260518-qcom-ice-fix-v7-3-2a595382185b@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/ice.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/soc/qcom/ice.c b/drivers/soc/qcom/ice.c index 85deb9ea4a68..2b592aa42941 100644 --- a/drivers/soc/qcom/ice.c +++ b/drivers/soc/qcom/ice.c @@ -563,7 +563,7 @@ static struct qcom_ice *qcom_ice_create(struct device *dev, if (!qcom_scm_ice_available()) { dev_warn(dev, "ICE SCM interface not found\n"); - return NULL; + return ERR_PTR(-EOPNOTSUPP); } engine = devm_kzalloc(dev, sizeof(*engine), GFP_KERNEL); @@ -645,7 +645,7 @@ static struct qcom_ice *of_qcom_ice_get(struct device *dev) struct device_node *node __free(device_node) = of_parse_phandle(dev->of_node, "qcom,ice", 0); if (!node) - return NULL; + return ERR_PTR(-ENODEV); pdev = of_find_device_by_node(node); if (!pdev) { @@ -698,8 +698,7 @@ static void devm_of_qcom_ice_put(struct device *dev, void *res) * phandle via 'qcom,ice' property to an ICE DT, the ICE instance will already * be created and so this function will return that instead. * - * Return: ICE pointer on success, NULL if there is no ICE data provided by the - * consumer or ERR_PTR() on error. + * Return: ICE pointer on success, ERR_PTR() on error. */ struct qcom_ice *devm_of_qcom_ice_get(struct device *dev) { @@ -710,7 +709,7 @@ struct qcom_ice *devm_of_qcom_ice_get(struct device *dev) return ERR_PTR(-ENOMEM); ice = of_qcom_ice_get(dev); - if (!IS_ERR_OR_NULL(ice)) { + if (!IS_ERR(ice)) { *dr = ice; devres_add(dev, dr); } else { From 2ccbb3fa5cf47d05849cf6722aad1b4cc14df6d9 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Mon, 18 May 2026 19:22:20 +0530 Subject: [PATCH 078/337] mmc: sdhci-msm: Remove NULL check from devm_of_qcom_ice_get() Now since the devm_of_qcom_ice_get() API never returns NULL, remove the NULL check and also simplify the error handling. Reviewed-by: Konrad Dybcio Acked-by: Ulf Hansson Acked-by: Adrian Hunter Tested-by: Sumit Garg # OP-TEE as TZ Acked-by: Sumit Garg Signed-off-by: Manivannan Sadhasivam Link: https://lore.kernel.org/r/20260518-qcom-ice-fix-v7-4-2a595382185b@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/mmc/host/sdhci-msm.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 633462c0be5f..0882ce74e0c9 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -1918,13 +1918,13 @@ static int sdhci_msm_ice_init(struct sdhci_msm_host *msm_host, return 0; ice = devm_of_qcom_ice_get(dev); - if (ice == ERR_PTR(-EOPNOTSUPP)) { - dev_warn(dev, "Disabling inline encryption support\n"); - ice = NULL; - } + if (IS_ERR(ice)) { + if (ice != ERR_PTR(-EOPNOTSUPP)) + return PTR_ERR(ice); - if (IS_ERR_OR_NULL(ice)) - return PTR_ERR_OR_ZERO(ice); + dev_warn(dev, "Disabling inline encryption support\n"); + return 0; + } msm_host->ice = ice; From 4ac19b36bf4108706238cbc4f300b17dba8b881e Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Mon, 18 May 2026 19:22:21 +0530 Subject: [PATCH 079/337] scsi: ufs: ufs-qcom: Remove NULL check from devm_of_qcom_ice_get() Now since the devm_of_qcom_ice_get() API never returns NULL, remove the NULL check and also simplify the error handling. Reviewed-by: Konrad Dybcio Acked-by: Martin K. Petersen # UFS Tested-by: Sumit Garg # OP-TEE as TZ Acked-by: Sumit Garg Signed-off-by: Manivannan Sadhasivam Link: https://lore.kernel.org/r/20260518-qcom-ice-fix-v7-5-2a595382185b@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/ufs/host/ufs-qcom.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/ufs/host/ufs-qcom.c b/drivers/ufs/host/ufs-qcom.c index bc037db46624..9c0973a7ffc3 100644 --- a/drivers/ufs/host/ufs-qcom.c +++ b/drivers/ufs/host/ufs-qcom.c @@ -177,13 +177,13 @@ static int ufs_qcom_ice_init(struct ufs_qcom_host *host) int i; ice = devm_of_qcom_ice_get(dev); - if (ice == ERR_PTR(-EOPNOTSUPP)) { - dev_warn(dev, "Disabling inline encryption support\n"); - ice = NULL; - } + if (IS_ERR(ice)) { + if (ice != ERR_PTR(-EOPNOTSUPP)) + return PTR_ERR(ice); - if (IS_ERR_OR_NULL(ice)) - return PTR_ERR_OR_ZERO(ice); + dev_warn(dev, "Disabling inline encryption support\n"); + return 0; + } host->ice = ice; From b73953af9bbd5c721c9d92b805a8aea8b0db74b1 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Thu, 14 May 2026 12:20:17 +0530 Subject: [PATCH 080/337] arm64: defconfig: Enable PCI M.2 power sequencing driver POWER_SEQUENCING_PCIE_M2 driver handles power supply to the PCIe M.2 connectors and is required on wide variety of ARM64 platforms such as Qcom Snapdragon X Elite laptops and Mediatek Dojo Chromebooks. Reviewed-by: Dmitry Baryshkov Reviewed-by: Krzysztof Kozlowski Signed-off-by: Manivannan Sadhasivam Link: https://lore.kernel.org/r/20260514065017.11305-1-manivannan.sadhasivam@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- arch/arm64/configs/defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig index d905a0777f93..96ce783f24e7 100644 --- a/arch/arm64/configs/defconfig +++ b/arch/arm64/configs/defconfig @@ -260,6 +260,7 @@ CONFIG_PCI_ENDPOINT=y CONFIG_PCI_ENDPOINT_CONFIGFS=y CONFIG_PCI_EPF_TEST=m CONFIG_PCI_PWRCTRL_GENERIC=m +CONFIG_POWER_SEQUENCING_PCIE_M2=m CONFIG_DEVTMPFS=y CONFIG_DEVTMPFS_MOUNT=y CONFIG_FW_LOADER_USER_HELPER=y From 4af7ad0e6d7aa4403dbb1dac7b9659b0421efcaa Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 13 May 2026 17:52:48 +0200 Subject: [PATCH 081/337] usb: typec: wcove: don't write past struct pd_message in wcove_read_rx_buffer() wcove_read_rx_buffer() copies the PD RX FIFO into the caller's struct pd_message with for (i = 0; i < USBC_RXINFO_RXBYTES(info); i++) regmap_read(wcove->regmap, USBC_RX_DATA + i, msg + i); which has two problems: USBC_RXINFO_RXBYTES() is a 5-bit field (max 31) while struct pd_message is 30 bytes (__le16 header + __le32 payload[PD_MAX_PAYLOAD], packed). The byte count latched in RXINFO is the number of bytes the port partner put on the wire, so a malicious partner that transmits a 31-byte frame can drive the loop one byte past the destination if the WCOVE BMC receiver does not enforce the PD object-count limit in hardware. The existing FIXME flagged this as unverified. Independently, regmap_read() takes an unsigned int * and stores a full unsigned int at the destination. Passing the byte pointer msg + i means each iteration writes four bytes; the high three are zero (val_bits is 8) and are normally overwritten by the next iteration, but the final iteration's high bytes are not. With RXBYTES == 30 the i == 29 iteration already writes three zero bytes past msg, which sits on the IRQ thread's stack in wcove_typec_irq(). Clamp the loop to sizeof(struct pd_message) and read each register into a local before storing only its low byte, so the copy can never exceed the destination regardless of what RXINFO reports. Assisted-by: gkh_clanker_t1000 Cc: stable Reviewed-by: Heikki Krogerus Link: https://patch.msgid.link/2026051347-clustered-deflected-9543@gregkh Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm/wcove.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/usb/typec/tcpm/wcove.c b/drivers/usb/typec/tcpm/wcove.c index 759c982bb16a..0e5a3e277c3e 100644 --- a/drivers/usb/typec/tcpm/wcove.c +++ b/drivers/usb/typec/tcpm/wcove.c @@ -444,9 +444,11 @@ static int wcove_start_toggling(struct tcpc_dev *tcpc, return regmap_write(wcove->regmap, USBC_CONTROL1, usbc_ctrl); } -static int wcove_read_rx_buffer(struct wcove_typec *wcove, void *msg) +static int wcove_read_rx_buffer(struct wcove_typec *wcove, + struct pd_message *msg) { - unsigned int info; + unsigned int info, val, len; + u8 *buf = (u8 *)msg; int ret; int i; @@ -454,12 +456,13 @@ static int wcove_read_rx_buffer(struct wcove_typec *wcove, void *msg) if (ret) return ret; - /* FIXME: Check that USBC_RXINFO_RXBYTES(info) matches the header */ + len = min(USBC_RXINFO_RXBYTES(info), sizeof(*msg)); - for (i = 0; i < USBC_RXINFO_RXBYTES(info); i++) { - ret = regmap_read(wcove->regmap, USBC_RX_DATA + i, msg + i); + for (i = 0; i < len; i++) { + ret = regmap_read(wcove->regmap, USBC_RX_DATA + i, &val); if (ret) return ret; + buf[i] = val; } return regmap_write(wcove->regmap, USBC_RXSTATUS, From 8a18f896e667df491331371b55d4ad644dc51d60 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 13 May 2026 17:52:49 +0200 Subject: [PATCH 082/337] usb: typec: altmodes/displayport: validate count before reading Status Update VDO A broken/malicious device can send the incorrect count for a status update VDO, which will cause the kernel to read uninitialized stack data and send it off elsewhere. Fix this up by correctly verifying the count for the update object. Assisted-by: gkh_clanker_t1000 Cc: stable Reviewed-by: Heikki Krogerus Link: https://patch.msgid.link/2026051350-reacquire-sculpture-4244@gregkh Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/altmodes/displayport.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/usb/typec/altmodes/displayport.c b/drivers/usb/typec/altmodes/displayport.c index 35d9c3086990..263a89c5f324 100644 --- a/drivers/usb/typec/altmodes/displayport.c +++ b/drivers/usb/typec/altmodes/displayport.c @@ -405,6 +405,8 @@ static int dp_altmode_vdm(struct typec_altmode *alt, dp->state = DP_STATE_EXIT_PRIME; break; case DP_CMD_STATUS_UPDATE: + if (count < 2) + break; dp->data.status = *vdo; ret = dp_altmode_status_update(dp); break; From aa2f716327be1818e1cb156da8a2844804aaec2f Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 13 May 2026 17:52:50 +0200 Subject: [PATCH 083/337] usb: typec: tcpm/tcpci_maxim: validate header NDO against RX_BYTE_CNT MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A broken/malicious port can transmit a CRC-valid frame whose header advertises up to seven data objects but whose body carries fewer than that. Check for this, and rightfully reject the message, instead of reading from uninitialized stack memory. Assisted-by: gkh_clanker_t1000 Cc: Heikki Krogerus Cc: "André Draszik" Cc: Badhri Jagan Sridharan Cc: Amit Sunil Dhamne Cc: stable Link: https://patch.msgid.link/2026051350-sitter-canopener-9045@gregkh Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm/tcpci_maxim_core.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/usb/typec/tcpm/tcpci_maxim_core.c b/drivers/usb/typec/tcpm/tcpci_maxim_core.c index c0ee7e6959ed..7324139d51c8 100644 --- a/drivers/usb/typec/tcpm/tcpci_maxim_core.c +++ b/drivers/usb/typec/tcpm/tcpci_maxim_core.c @@ -181,6 +181,15 @@ static void process_rx(struct max_tcpci_chip *chip, u16 status) rx_buf_ptr = rx_buf + TCPC_RECEIVE_BUFFER_RX_BYTE_BUF_OFFSET; msg.header = cpu_to_le16(*(u16 *)rx_buf_ptr); rx_buf_ptr = rx_buf_ptr + sizeof(msg.header); + + if (count < TCPC_RECEIVE_BUFFER_RX_BYTE_BUF_OFFSET + sizeof(msg.header) + + pd_header_cnt_le(msg.header) * sizeof(msg.payload[0])) { + max_tcpci_write16(chip, TCPC_ALERT, TCPC_ALERT_RX_STATUS); + dev_err(chip->dev, "Invalid TCPC_RX_BYTE_CNT %d for header cnt %d\n", + count, pd_header_cnt_le(msg.header)); + return; + } + for (payload_index = 0; payload_index < pd_header_cnt_le(msg.header); payload_index++, rx_buf_ptr += sizeof(msg.payload[0])) msg.payload[payload_index] = cpu_to_le32(*(u32 *)rx_buf_ptr); From 8fbc349e8383125dd2d8de1c1e926279d398ab17 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 13 May 2026 17:52:51 +0200 Subject: [PATCH 084/337] usb: typec: tcpm: validate VDO count in Discover Identity ACK handlers Properly validate the count passed from a device when calling svdm_consume_identity() or svdm_consume_identity_sop_prime() as the device-controlled value could index off of the static arrays, which could leak data. Assisted-by: gkh_clanker_t1000 Cc: Heikki Krogerus Cc: stable Reviewed-by: Badhri Jagan Sridharan Link: https://patch.msgid.link/2026051350-plated-salute-0efe@gregkh Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm/tcpm.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index 55fee96d3342..44dab6c32c33 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -1855,6 +1855,9 @@ static void svdm_consume_identity(struct tcpm_port *port, const u32 *p, int cnt) u32 vdo = p[VDO_INDEX_IDH]; u32 product = p[VDO_INDEX_PRODUCT]; + if (cnt <= VDO_INDEX_PRODUCT) + return; + memset(&port->mode_data, 0, sizeof(port->mode_data)); port->partner_ident.id_header = vdo; @@ -1875,6 +1878,9 @@ static void svdm_consume_identity_sop_prime(struct tcpm_port *port, const u32 *p u32 product = p[VDO_INDEX_PRODUCT]; int svdm_version; + if (cnt <= VDO_INDEX_CABLE_1) + return; + /* * Attempt to consume identity only if cable currently is not set */ @@ -1898,7 +1904,7 @@ static void svdm_consume_identity_sop_prime(struct tcpm_port *port, const u32 *p switch (port->negotiated_rev_prime) { case PD_REV30: port->cable_desc.pd_revision = 0x0300; - if (port->cable_desc.active) + if (port->cable_desc.active && cnt > VDO_INDEX_CABLE_2) port->cable_ident.vdo[1] = p[VDO_INDEX_CABLE_2]; break; case PD_REV20: From 3389c149c68c3fea61910ad5d34f7bf3bff44e32 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 13 May 2026 17:52:53 +0200 Subject: [PATCH 085/337] usb: typec: tcpm: bound altmode_desc[] per iteration in svdm_consume_modes() svdm_consume_modes() checks pmdata->altmodes against the array size once before the loop over the count, but forgot to check the bound at every point in the loop. In the well-behaved SVDM discovery flow this is harmless because each of at most SVID_DISCOVERY_MAX SVIDs contributes at most MODE_DISCOVERY_MAX modes, exactly filling altmode_desc[ALTMODE_DISCOVERY_MAX]. But the CMDT_RSP_ACK handler in tcpm_pd_svdm() does not correlate an incoming ACK with any request the port actually sent. Once port->partner is set, an unsolicited Discover Modes ACK is consumed unconditionally. A broken or malicious port partner can therefore drive altmodes to ALTMODE_DISCOVERY_MAX - 1 via the normal flow, and then send one extra Discover Modes ACK with seven VDOs. Because the pre-loop check passes, the loop could then writes up to five entries past altmode_desc[]. For mode_data_prime the next field in struct tcpm_port is the partner_altmode[] pointer array, which then receives partner-chosen SVID/VDO bytes. Move the bound check inside the loop so the array can never be indexed past ALTMODE_DISCOVERY_MAX regardless of how many VDOs the partner supplies or how the function was reached. Assisted-by: gkh_clanker_t1000 Cc: Badhri Jagan Sridharan Cc: Heikki Krogerus Cc: stable Link: https://patch.msgid.link/2026051351-reshuffle-skillful-90af@gregkh Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm/tcpm.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index 44dab6c32c33..ed5f745a8231 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -1992,23 +1992,19 @@ static void svdm_consume_modes(struct tcpm_port *port, const u32 *p, int cnt, switch (rx_sop_type) { case TCPC_TX_SOP_PRIME: pmdata = &port->mode_data_prime; - if (pmdata->altmodes >= ARRAY_SIZE(port->plug_prime_altmode)) { - /* Already logged in svdm_consume_svids() */ - return; - } break; case TCPC_TX_SOP: pmdata = &port->mode_data; - if (pmdata->altmodes >= ARRAY_SIZE(port->partner_altmode)) { - /* Already logged in svdm_consume_svids() */ - return; - } break; default: return; } for (i = 1; i < cnt; i++) { + if (pmdata->altmodes >= ALTMODE_DISCOVERY_MAX) { + /* Already logged in svdm_consume_svids() */ + return; + } paltmode = &pmdata->altmode_desc[pmdata->altmodes]; memset(paltmode, 0, sizeof(*paltmode)); From 167dd8d12226587ee554f520aed0256b7769cd5d Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 13 May 2026 17:52:54 +0200 Subject: [PATCH 086/337] usb: typec: ucsi: displayport: NAK DP_CMD_CONFIGURE without a payload VDO ucsi_displayport_vdm() handles a DP_CMD_CONFIGURE by copying the first payload VDO from data[], but unlike the equivalent handler in altmodes/displayport.c it does not check that count covers a VDO beyond the header. A header-only Configure VDM (count == 1) would read one u32 past the caller's array. In the normal UCSI path the caller controls count, so this is hardening for non-standard delivery paths. NAK and bail when no configuration VDO is present, matching the generic DP altmode driver's existing guard. Assisted-by: gkh_clanker_t1000 Cc: Pooja Katiyar Cc: Johan Hovold Cc: stable Reviewed-by: Heikki Krogerus Link: https://patch.msgid.link/2026051351-vividly-flattered-eb3d@gregkh Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/displayport.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/usb/typec/ucsi/displayport.c b/drivers/usb/typec/ucsi/displayport.c index 8aae80b457d7..67a0991a7b76 100644 --- a/drivers/usb/typec/ucsi/displayport.c +++ b/drivers/usb/typec/ucsi/displayport.c @@ -240,6 +240,10 @@ static int ucsi_displayport_vdm(struct typec_altmode *alt, dp->header |= VDO_CMDT(CMDT_RSP_ACK); break; case DP_CMD_CONFIGURE: + if (count < 2) { + dp->header |= VDO_CMDT(CMDT_RSP_NAK); + break; + } dp->data.conf = *data; if (ucsi_displayport_configure(dp)) { dp->header |= VDO_CMDT(CMDT_RSP_NAK); From 288a81a8507052bcfbf884d39a463c44c42c5fd9 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 13 May 2026 17:52:55 +0200 Subject: [PATCH 087/337] usb: typec: ucsi: validate connector number in ucsi_connector_change() The connector number in a UCSI CCI notification is a 7-bit field supplied by the PPM. ucsi_connector_change() uses it to index the ucsi->connector[] array without checking it against the number of connectors the PPM reported at init time, so a buggy or malicious PPM (EC firmware, or an I2C-attached UCSI controller on the ccg / stm32g0 / glink transports) can drive schedule_work() on memory past the end of the array. Reject connector numbers that are zero or exceed cap.num_connectors before dereferencing the array. Assisted-by: gkh_clanker_t1000 Cc: Heikki Krogerus Cc: Benson Leung Cc: Jameson Thies Cc: Nathan Rebello Cc: Johan Hovold Cc: Pooja Katiyar Cc: Hsin-Te Yuan Cc: Abel Vesa Cc: stable Reviewed-by: Abel Vesa Reviewed-by: Heikki Krogerus Reviewed-by: Benson Leung Link: https://patch.msgid.link/2026051351-truck-steadfast-df48@gregkh Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/ucsi.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index 5b7ad9e99cb9..539dc706798d 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -1380,13 +1380,22 @@ static void ucsi_handle_connector_change(struct work_struct *work) */ void ucsi_connector_change(struct ucsi *ucsi, u8 num) { - struct ucsi_connector *con = &ucsi->connector[num - 1]; + struct ucsi_connector *con; if (!(ucsi->ntfy & UCSI_ENABLE_NTFY_CONNECTOR_CHANGE)) { dev_dbg(ucsi->dev, "Early connector change event\n"); return; } + if (!num || num > ucsi->cap.num_connectors) { + dev_warn_ratelimited(ucsi->dev, + "Bogus connector change on %u (max %u)\n", + num, ucsi->cap.num_connectors); + return; + } + + con = &ucsi->connector[num - 1]; + if (!test_and_set_bit(EVENT_PENDING, &ucsi->flags)) schedule_work(&con->work); } From c15d7a2a11ea055bcecc0b538ae8ba79475637f9 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 4 Dec 2025 11:17:23 +0100 Subject: [PATCH 088/337] tee: fix tee_ioctl_object_invoke_arg padding The tee_ioctl_object_invoke_arg structure has padding on some architectures but not on x86-32 and a few others: include/linux/tee.h:474:32: error: padding struct to align 'params' [-Werror=padded] I expect that all current users of this are on architectures that do have implicit padding here (arm64, arm, x86, riscv), so make the padding explicit in order to avoid surprises if this later gets used elsewhere. Fixes: d5b8b0fa1775 ("tee: add TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF") Signed-off-by: Arnd Bergmann Reviewed-by: Jens Wiklander Tested-by: Harshal Dev Reviewed-by: Sumit Garg Signed-off-by: Jens Wiklander --- include/uapi/linux/tee.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/uapi/linux/tee.h b/include/uapi/linux/tee.h index cab5cadca8ef..5203977ed35d 100644 --- a/include/uapi/linux/tee.h +++ b/include/uapi/linux/tee.h @@ -470,6 +470,7 @@ struct tee_ioctl_object_invoke_arg { __u32 op; __u32 ret; __u32 num_params; + __u32 :32; /* num_params tells the actual number of element in params */ struct tee_ioctl_param params[]; }; From 26682f5efc276e3ad96d102019472bfbf03833b2 Mon Sep 17 00:00:00 2001 From: Georgiy Osokin Date: Wed, 8 Apr 2026 18:52:03 +0300 Subject: [PATCH 089/337] tee: shm: fix shm leak in register_shm_helper() register_shm_helper() allocates shm before calling iov_iter_npages(). If iov_iter_npages() returns 0, the function jumps to err_ctx_put and leaks shm. This can be triggered by TEE_IOC_SHM_REGISTER with struct tee_ioctl_shm_register_data where length is 0. Jump to err_free_shm instead. Fixes: 7bdee4157591 ("tee: Use iov_iter to better support shared buffer registration") Cc: stable@vger.kernel.org Cc: lvc-project@linuxtesting.org Signed-off-by: Georgiy Osokin Reviewed-by: Sumit Garg Signed-off-by: Jens Wiklander --- drivers/tee/tee_shm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tee/tee_shm.c b/drivers/tee/tee_shm.c index e9ea9f80cfd9..6742b3579c86 100644 --- a/drivers/tee/tee_shm.c +++ b/drivers/tee/tee_shm.c @@ -435,7 +435,7 @@ register_shm_helper(struct tee_context *ctx, struct iov_iter *iter, u32 flags, num_pages = iov_iter_npages(iter, INT_MAX); if (!num_pages) { ret = ERR_PTR(-ENOMEM); - goto err_ctx_put; + goto err_free_shm; } shm->pages = kzalloc_objs(*shm->pages, num_pages); From 6fa9b543f6b4ed15ff72af266b29f316643de289 Mon Sep 17 00:00:00 2001 From: Qihang Date: Thu, 7 May 2026 23:39:17 +0800 Subject: [PATCH 090/337] tee: fix params_from_user() error path in tee_ioctl_supp_recv params_from_user() may acquire tee_shm references for MEMREF parameters before failing after partially processing the supplied parameter array. In tee_ioctl_supp_recv(), those references are currently not released on that error path. Fix this by freeing MEMREF references before returning when params_from_user() fails. Keep the final cleanup path in tee_ioctl_supp_recv() unchanged since supp_recv() may consume and replace the supplied parameters, unlike the other TEE ioctl callback paths. Signed-off-by: Qihang Signed-off-by: Jens Wiklander --- drivers/tee/tee_core.c | 56 +++++++++++++++++++----------------------- 1 file changed, 25 insertions(+), 31 deletions(-) diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c index ef9642d72672..1aac50c7c1de 100644 --- a/drivers/tee/tee_core.c +++ b/drivers/tee/tee_core.c @@ -530,11 +530,24 @@ static int params_to_user(struct tee_ioctl_param __user *uparams, return 0; } +static void free_params(struct tee_param *params, size_t num_params) +{ + size_t n; + + if (!params) + return; + + for (n = 0; n < num_params; n++) + if (tee_param_is_memref(params + n) && params[n].u.memref.shm) + tee_shm_put(params[n].u.memref.shm); + + kfree(params); +} + static int tee_ioctl_open_session(struct tee_context *ctx, struct tee_ioctl_buf_data __user *ubuf) { int rc; - size_t n; struct tee_ioctl_buf_data buf; struct tee_ioctl_open_session_arg __user *uarg; struct tee_ioctl_open_session_arg arg; @@ -595,16 +608,7 @@ static int tee_ioctl_open_session(struct tee_context *ctx, */ if (rc && have_session && ctx->teedev->desc->ops->close_session) ctx->teedev->desc->ops->close_session(ctx, arg.session); - - if (params) { - /* Decrease ref count for all valid shared memory pointers */ - for (n = 0; n < arg.num_params; n++) - if (tee_param_is_memref(params + n) && - params[n].u.memref.shm) - tee_shm_put(params[n].u.memref.shm); - kfree(params); - } - + free_params(params, arg.num_params); return rc; } @@ -612,7 +616,6 @@ static int tee_ioctl_invoke(struct tee_context *ctx, struct tee_ioctl_buf_data __user *ubuf) { int rc; - size_t n; struct tee_ioctl_buf_data buf; struct tee_ioctl_invoke_arg __user *uarg; struct tee_ioctl_invoke_arg arg; @@ -657,14 +660,7 @@ static int tee_ioctl_invoke(struct tee_context *ctx, } rc = params_to_user(uparams, arg.num_params, params); out: - if (params) { - /* Decrease ref count for all valid shared memory pointers */ - for (n = 0; n < arg.num_params; n++) - if (tee_param_is_memref(params + n) && - params[n].u.memref.shm) - tee_shm_put(params[n].u.memref.shm); - kfree(params); - } + free_params(params, arg.num_params); return rc; } @@ -672,7 +668,6 @@ static int tee_ioctl_object_invoke(struct tee_context *ctx, struct tee_ioctl_buf_data __user *ubuf) { int rc; - size_t n; struct tee_ioctl_buf_data buf; struct tee_ioctl_object_invoke_arg __user *uarg; struct tee_ioctl_object_invoke_arg arg; @@ -716,14 +711,7 @@ static int tee_ioctl_object_invoke(struct tee_context *ctx, } rc = params_to_user(uparams, arg.num_params, params); out: - if (params) { - /* Decrease ref count for all valid shared memory pointers */ - for (n = 0; n < arg.num_params; n++) - if (tee_param_is_memref(params + n) && - params[n].u.memref.shm) - tee_shm_put(params[n].u.memref.shm); - kfree(params); - } + free_params(params, arg.num_params); return rc; } @@ -846,9 +834,15 @@ static int tee_ioctl_supp_recv(struct tee_context *ctx, return -ENOMEM; rc = params_from_user(ctx, params, num_params, uarg->params); - if (rc) - goto out; + if (rc) { + free_params(params, num_params); + return rc; + } + /* + * supp_recv() may consume and replace the supplied parameters, so the + * final cleanup cannot use free_params() like the other ioctl paths. + */ rc = ctx->teedev->desc->ops->supp_recv(ctx, &func, &num_params, params); if (rc) goto out; From 471c18323dfdfe7844e193b896a9267ae23a1026 Mon Sep 17 00:00:00 2001 From: Robertus Diawan Chris Date: Tue, 19 May 2026 09:05:28 +0700 Subject: [PATCH 091/337] tee: qcomtee: add missing va_end in early return qcomtee_object_user_init() qcomtee_object_user_init() is a variadic function and when the function return because there's no dispatch callback in QCOMTEE_OBJECT_TYPE_CB case, there's no va_end to cleanup "ap" object initialized by va_start and that can cause undefined behavior. So make sure to use va_end before returning the error code when there's no dispatch callback. This is reported by Coverity Scan as "Missing varargs init or cleanup". Fixes: d6e290837e50 ("tee: add Qualcomm TEE driver") Signed-off-by: Robertus Diawan Chris Reviewed-by: Amirreza Zarrabi Signed-off-by: Jens Wiklander --- drivers/tee/qcomtee/core.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/tee/qcomtee/core.c b/drivers/tee/qcomtee/core.c index b1cb50e434f0..60fe3b5776e3 100644 --- a/drivers/tee/qcomtee/core.c +++ b/drivers/tee/qcomtee/core.c @@ -306,8 +306,10 @@ int qcomtee_object_user_init(struct qcomtee_object *object, break; case QCOMTEE_OBJECT_TYPE_CB: object->ops = ops; - if (!object->ops->dispatch) - return -EINVAL; + if (!object->ops->dispatch) { + ret = -EINVAL; + break; + } /* If failed, "no-name". */ object->name = kvasprintf_const(GFP_KERNEL, fmt, ap); From 4ce058df2ee02cc2a0f0fd5cd64ce6f1482a0b65 Mon Sep 17 00:00:00 2001 From: Zhang Cen Date: Tue, 19 May 2026 19:11:50 +0800 Subject: [PATCH 092/337] USB: serial: belkin_sa: validate interrupt status length The Belkin interrupt callback treats interrupt data as a four-byte status report and reads LSR/MSR fields at offsets 2 and 3. The interrupt-in buffer length is derived from endpoint wMaxPacketSize, and short interrupt transfers may complete successfully with a smaller actual_length. Check the completed interrupt packet length before parsing status fields so short interrupt endpoints and short successful packets are ignored instead of causing out-of-bounds or stale status-byte reads. KASAN report as below: BUG: KASAN: slab-out-of-bounds in belkin_sa_read_int_callback() Read of size 1 Call trace: belkin_sa_read_int_callback() (drivers/usb/serial/belkin_sa.c:202) __usb_hcd_giveback_urb() (drivers/usb/core/hcd.c:1630) dummy_timer() (?:?) Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Assisted-by: Codex:gpt-5.5 Signed-off-by: Zhang Cen Cc: stable@vger.kernel.org Signed-off-by: Johan Hovold --- drivers/usb/serial/belkin_sa.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/usb/serial/belkin_sa.c b/drivers/usb/serial/belkin_sa.c index 38ac910b1082..7bbd9523d4e9 100644 --- a/drivers/usb/serial/belkin_sa.c +++ b/drivers/usb/serial/belkin_sa.c @@ -194,6 +194,9 @@ static void belkin_sa_read_int_callback(struct urb *urb) usb_serial_debug_data(&port->dev, __func__, urb->actual_length, data); + if (urb->actual_length < BELKIN_SA_MSR_INDEX + 1) + goto exit; + /* Handle known interrupt data */ /* ignore data[0] and data[1] */ From cb3560e8eab1dfa1cac1ed52631adf8ec6ff2cd5 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 20 May 2026 16:26:22 +0200 Subject: [PATCH 093/337] USB: serial: digi_acceleport: fix memory corruption with small endpoints Add the missing bulk-out buffer size sanity checks to avoid out-of-bounds memory accesses or slab corruption should a malicious device report smaller buffers than expected. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/digi_acceleport.c | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index d515df045c4c..c481208255eb 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -1229,15 +1229,34 @@ static int digi_port_init(struct usb_serial_port *port, unsigned port_num) static int digi_startup(struct usb_serial *serial) { struct digi_serial *serial_priv; + int oob_port_num; int ret; + int i; + + /* + * The port bulk-out buffers must be large enough for header and + * buffered data. + */ + for (i = 0; i < serial->type->num_ports; i++) { + if (serial->port[i]->bulk_out_size < DIGI_OUT_BUF_SIZE + 2) + return -EINVAL; + } + + /* + * The OOB port bulk-out buffer must be large enough for the two + * commands in digi_set_modem_signals(). + */ + oob_port_num = serial->type->num_ports; + if (serial->port[oob_port_num]->bulk_out_size < 8) + return -EINVAL; serial_priv = kzalloc_obj(*serial_priv); if (!serial_priv) return -ENOMEM; spin_lock_init(&serial_priv->ds_serial_lock); - serial_priv->ds_oob_port_num = serial->type->num_ports; - serial_priv->ds_oob_port = serial->port[serial_priv->ds_oob_port_num]; + serial_priv->ds_oob_port_num = oob_port_num; + serial_priv->ds_oob_port = serial->port[oob_port_num]; ret = digi_port_init(serial_priv->ds_oob_port, serial_priv->ds_oob_port_num); From ab8336a7e414f018430aa1af3a46944032f7ff96 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 20 May 2026 16:26:48 +0200 Subject: [PATCH 094/337] USB: serial: keyspan: fix missing indat transfer sanity check Add the missing sanity check on the size of usa49wg indat transfers to avoid parsing stale or uninitialised slab data. Fixes: 0ca1268e109a ("USB Serial Keyspan: add support for USA-49WG & USA-28XG") Cc: stable@vger.kernel.org # 2.6.23 Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/keyspan.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c index 46448843541a..28b80607cebd 100644 --- a/drivers/usb/serial/keyspan.c +++ b/drivers/usb/serial/keyspan.c @@ -1187,6 +1187,10 @@ static void usa49wg_indat_callback(struct urb *urb) len = 0; while (i < urb->actual_length) { + if (urb->actual_length - i < 3) { + dev_warn_ratelimited(&urb->dev->dev, "malformed indat packet\n"); + break; + } /* Check port number from message */ if (data[i] >= serial->num_ports) { From 915b36d701950503c4ea0f6e314b10868e59fce3 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 20 May 2026 16:27:00 +0200 Subject: [PATCH 095/337] USB: serial: mct_u232: fix memory corruption with small endpoint The driver overrides the maximum transfer size for a specific device which only accepts 16 byte packets for its 32 byte bulk-out endpoint. Make sure to never increase the maximum transfer size to prevent slab corruption should a malicious device report a smaller endpoint max packet size than expected. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/mct_u232.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c index 18844b92bd08..ca1530da6e77 100644 --- a/drivers/usb/serial/mct_u232.c +++ b/drivers/usb/serial/mct_u232.c @@ -378,6 +378,7 @@ static int mct_u232_port_probe(struct usb_serial_port *port) { struct usb_serial *serial = port->serial; struct mct_u232_private *priv; + u16 pid; /* check first to simplify error handling */ if (!serial->port[1] || !serial->port[1]->interrupt_in_urb) { @@ -385,6 +386,16 @@ static int mct_u232_port_probe(struct usb_serial_port *port) return -ENODEV; } + /* + * Compensate for a hardware bug: although the Sitecom U232-P25 + * device reports a maximum output packet size of 32 bytes, + * it seems to be able to accept only 16 bytes (and that's what + * SniffUSB says too...) + */ + pid = le16_to_cpu(serial->dev->descriptor.idProduct); + if (pid == MCT_U232_SITECOM_PID) + port->bulk_out_size = min(16, port->bulk_out_size); + priv = kzalloc_obj(*priv); if (!priv) return -ENOMEM; @@ -410,7 +421,6 @@ static void mct_u232_port_remove(struct usb_serial_port *port) static int mct_u232_open(struct tty_struct *tty, struct usb_serial_port *port) { - struct usb_serial *serial = port->serial; struct mct_u232_private *priv = usb_get_serial_port_data(port); int retval = 0; unsigned int control_state; @@ -418,15 +428,6 @@ static int mct_u232_open(struct tty_struct *tty, struct usb_serial_port *port) unsigned char last_lcr; unsigned char last_msr; - /* Compensate for a hardware bug: although the Sitecom U232-P25 - * device reports a maximum output packet size of 32 bytes, - * it seems to be able to accept only 16 bytes (and that's what - * SniffUSB says too...) - */ - if (le16_to_cpu(serial->dev->descriptor.idProduct) - == MCT_U232_SITECOM_PID) - port->bulk_out_size = 16; - /* Do a defined restart: the normal serial device seems to * always turn on DTR and RTS here, so do the same. I'm not * sure if this is really necessary. But it should not harm From 245aba83e3c288e176ed037a1f6b618b09e92ed8 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 20 May 2026 16:27:10 +0200 Subject: [PATCH 096/337] USB: serial: mct_u232: fix missing interrupt-in transfer sanity check Add the missing sanity check on the size of interrupt-in transfers to avoid parsing stale or uninitialised slab data (and leaking it to user space). Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/mct_u232.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c index ca1530da6e77..163161881d2d 100644 --- a/drivers/usb/serial/mct_u232.c +++ b/drivers/usb/serial/mct_u232.c @@ -544,6 +544,11 @@ static void mct_u232_read_int_callback(struct urb *urb) goto exit; } + if (urb->actual_length < 2) { + dev_warn_ratelimited(&port->dev, "short interrupt-in packet\n"); + goto exit; + } + /* * The interrupt-in pipe signals exceptional conditions (modem line * signal changes and errors). data[0] holds MSR, data[1] holds LSR. From 7d2b37d3e42d19071b62f4ddbee6e16e905efbf1 Mon Sep 17 00:00:00 2001 From: Jan Volckaert Date: Sun, 17 May 2026 17:32:37 +0200 Subject: [PATCH 097/337] USB: serial: option: add MeiG SRM813Q Add support for the Qualcomm Technology Snapdragon X35-based MeiG SRM813Q module. The module can be put in different modes via AT commands to enable/disable GPS functionality: MODEM - PPP mode(2dee:4d63): AT+SER=1,1 If#= 0: RMNET If#= 1: DIAG/ADB If#= 2: MODEM If#= 3: AT P: Vendor=2dee ProdID=4d63 Rev=05.15 S: Manufacturer=MEIG S: Product=LTE-A Module S: SerialNumber=1bd51f0e C: #Ifs= 4 Cfg#= 1 Atr=80 MxPwr=500mA I: If#= 0 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=50 Driver=qmi_wwan E: Ad=01(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=82(I) Atr=03(Int.) MxPS= 8 Ivl=32ms I: If#= 1 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=30 Driver=option E: Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=83(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms I: If#= 2 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option E: Ad=03(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=84(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=85(I) Atr=03(Int.) MxPS= 10 Ivl=32ms I: If#= 3 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option E: Ad=04(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=86(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=87(I) Atr=03(Int.) MxPS= 10 Ivl=32ms NMEA mode(2dee:4d64): AT+SER=51,1 If#= 0: RMNET If#= 1: DIAG/ADB If#= 2: NMEA If#= 3: AT P: Vendor=2dee ProdID=4d64 Rev=05.15 S: Manufacturer=MEIG S: Product=LTE-A Module S: SerialNumber=1bd51f0e C: #Ifs= 4 Cfg#= 1 Atr=80 MxPwr=500mA I: If#= 0 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=50 Driver=qmi_wwan E: Ad=01(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=82(I) Atr=03(Int.) MxPS= 8 Ivl=32ms I: If#= 1 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=30 Driver=option E: Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=83(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms I: If#= 2 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=60 Driver=option E: Ad=03(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=84(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=85(I) Atr=03(Int.) MxPS= 10 Ivl=32ms I: If#= 3 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option E: Ad=04(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=86(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=87(I) Atr=03(Int.) MxPS= 10 Ivl=32ms Signed-off-by: Jan Volckaert Cc: stable@vger.kernel.org Signed-off-by: Johan Hovold --- drivers/usb/serial/option.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 42e4cecd28ac..92c2feda3e3d 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -2450,6 +2450,12 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d38, 0xff, 0xff, 0x30) }, /* MeiG Smart SRM825WN (Diag) */ { USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d38, 0xff, 0xff, 0x40) }, /* MeiG Smart SRM825WN (AT) */ { USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d38, 0xff, 0xff, 0x60) }, /* MeiG Smart SRM825WN (NMEA) */ + { USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d63, 0xff, 0xff, 0x30) }, /* MeiG SRM813Q (Diag) */ + { USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d63, 0xff, 0xff, 0x40) }, /* MeiG SRM813Q (AT) */ + { USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d64, 0xff, 0xff, 0x30) }, /* MeiG SRM813Q (Diag) */ + { USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d64, 0xff, 0xff, 0x40) }, /* MeiG SRM813Q (AT) */ + { USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d64, 0xff, 0xff, 0x60) }, /* MeiG SRM813Q (NMEA) */ + { USB_DEVICE_INTERFACE_CLASS(0x2df3, 0x9d03, 0xff) }, /* LongSung M5710 */ { USB_DEVICE_INTERFACE_CLASS(0x305a, 0x1404, 0xff) }, /* GosunCn GM500 RNDIS */ { USB_DEVICE_INTERFACE_CLASS(0x305a, 0x1405, 0xff) }, /* GosunCn GM500 MBIM */ From 689f2facc689c8add11d7ff69fbbad17d65ee596 Mon Sep 17 00:00:00 2001 From: Wanquan Zhong Date: Wed, 20 May 2026 19:32:45 +0800 Subject: [PATCH 098/337] USB: serial: option: add missing RSVD(5) flag for Rolling RW135R-GL The RW135R-GL entry added in commit 01e8d0f74222 ("USB: serial: option: add support for Rolling Wireless RW135R-GL") was missing the .driver_info = RSVD(5) flag used by other Rolling Wireless MBIM laptop modules (e.g. RW135-GL and RW350-GL). Without this flag, the option driver incorrectly binds to the reserved ADB interface (If#5) in multi-interface USB modes, causing AT/MBIM communication failures after mode switching. This matches the handling of other Rolling Wireless MBIM devices. - VID:PID 33f8:1003, RW135R-GL for laptop debug M.2 cards (with MBIM interface for Linux/Chrome OS) 0x1003: mbim, diag, AT, pipe Here are the outputs of usb-devices: T: Bus=03 Lev=01 Prnt=01 Port=04 Cnt=02 Dev#= 8 Spd=480 MxCh= 0 D: Ver= 2.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs= 1 P: Vendor=33f8 ProdID=1003 Rev= 5.15 S: Manufacturer=Rolling Wireless S.a.r.l. S: Product=Rolling RW135R-GL Module S: SerialNumber=12345678 C:* #Ifs= 5 Cfg#= 1 Atr=a0 MxPwr=500mA A: FirstIf#= 0 IfCount= 2 Cls=02(comm.) Sub=0e Prot=00 I:* If#= 0 Alt= 0 #EPs= 1 Cls=02(comm.) Sub=0e Prot=00 Driver=cdc_mbim E: Ad=82(I) Atr=03(Int.) MxPS= 64 Ivl=32ms I: If#= 1 Alt= 0 #EPs= 0 Cls=0a(data ) Sub=00 Prot=02 Driver=cdc_mbim I:* If#= 1 Alt= 1 #EPs= 2 Cls=0a(data ) Sub=00 Prot=02 Driver=cdc_mbim E: Ad=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=01(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms I:* If#= 2 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=00 Prot=00 Driver=option E: Ad=83(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms I:* If#= 3 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=30 Driver=option E: Ad=03(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=84(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms I:* If#= 4 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option E: Ad=86(I) Atr=03(Int.) MxPS= 10 Ivl=32ms E: Ad=85(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=04(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms - VID:PID 33f8:1003, RW135R-GL for laptop debug M.2 cards (with MBIM interface for Linux/Chrome OS) 0x1003: mbim, diag, AT, ADB, pipe Here are the outputs of usb-devices: T: Bus=03 Lev=01 Prnt=01 Port=04 Cnt=02 Dev#= 7 Spd=480 MxCh= 0 D: Ver= 2.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs= 1 P: Vendor=33f8 ProdID=1003 Rev= 5.15 S: Manufacturer=Rolling Wireless S.a.r.l. S: Product=Rolling RW135R-GL Module S: SerialNumber=12345678 C:* #Ifs= 6 Cfg#= 1 Atr=a0 MxPwr=500mA A: FirstIf#= 0 IfCount= 2 Cls=02(comm.) Sub=0e Prot=00 I:* If#= 0 Alt= 0 #EPs= 1 Cls=02(comm.) Sub=0e Prot=00 Driver=cdc_mbim E: Ad=82(I) Atr=03(Int.) MxPS= 64 Ivl=32ms I: If#= 1 Alt= 0 #EPs= 0 Cls=0a(data ) Sub=00 Prot=02 Driver=cdc_mbim I:* If#= 1 Alt= 1 #EPs= 2 Cls=0a(data ) Sub=00 Prot=02 Driver=cdc_mbim E: Ad=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=01(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms I:* If#= 2 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=00 Prot=00 Driver=option E: Ad=83(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms I:* If#= 3 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=30 Driver=option E: Ad=03(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=84(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms I:* If#= 4 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option E: Ad=86(I) Atr=03(Int.) MxPS= 10 Ivl=32ms E: Ad=85(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=04(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms I:* If#= 5 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=42 Prot=01 Driver=(none) E: Ad=05(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=87(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms - VID:PID 33f8:1003, RW135R-GL for laptop debug M.2 cards (with MBIM interface for Linux/Chrome OS) 0x1003: mbim, pipe Here are the outputs of usb-devices: T: Bus=03 Lev=01 Prnt=01 Port=04 Cnt=02 Dev#= 9 Spd=480 MxCh= 0 D: Ver= 2.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs= 1 P: Vendor=33f8 ProdID=1003 Rev= 5.15 S: Manufacturer=Rolling Wireless S.a.r.l. S: Product=Rolling RW135R-GL Module S: SerialNumber=12345678 C:* #Ifs= 3 Cfg#= 1 Atr=a0 MxPwr=500mA A: FirstIf#= 0 IfCount= 2 Cls=02(comm.) Sub=0e Prot=00 I:* If#= 0 Alt= 0 #EPs= 1 Cls=02(comm.) Sub=0e Prot=00 Driver=cdc_mbim E: Ad=82(I) Atr=03(Int.) MxPS= 64 Ivl=32ms I: If#= 1 Alt= 0 #EPs= 0 Cls=0a(data ) Sub=00 Prot=02 Driver=cdc_mbim I:* If#= 1 Alt= 1 #EPs= 2 Cls=0a(data ) Sub=00 Prot=02 Driver=cdc_mbim E: Ad=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=01(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms I:* If#= 2 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=00 Prot=00 Driver=option E: Ad=83(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms Fixes: 01e8d0f74222 ("USB: serial: option: add support for Rolling Wireless RW135R-GL") Signed-off-by: Wanquan Zhong Cc: stable@vger.kernel.org Signed-off-by: Johan Hovold --- drivers/usb/serial/option.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 92c2feda3e3d..48ae0188f2e9 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -2476,7 +2476,8 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_INTERFACE_CLASS(0x33f8, 0x0302, 0xff) }, /* Rolling RW101R-GL (laptop MBIM) */ { USB_DEVICE_INTERFACE_CLASS(0x33f8, 0x0802, 0xff), /* Rolling RW350-GL (laptop MBIM) */ .driver_info = RSVD(5) }, - { USB_DEVICE_INTERFACE_CLASS(0x33f8, 0x1003, 0xff) }, /* Rolling RW135R-GL (laptop MBIM) */ + { USB_DEVICE_INTERFACE_CLASS(0x33f8, 0x1003, 0xff), /* Rolling RW135R-GL (laptop MBIM) */ + .driver_info = RSVD(5) }, { USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x0100, 0xff, 0xff, 0x30) }, /* NetPrisma LCUK54-WWD for Global */ { USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x0100, 0xff, 0x00, 0x40) }, { USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x0100, 0xff, 0xff, 0x40) }, From 8a3bee801d420be8a7a0bae4a26547b353b8fe22 Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Wed, 22 Apr 2026 15:46:37 +0100 Subject: [PATCH 099/337] comedi: comedi_test: Fix limiting of convert_arg in waveform_ai_cmdtest() The function checks and possibly modifies the description of an asynchronous command to be run on the analog input subdevice of a comedi device attached to the "comedi_test" driver, returning 0 if no modifications were required, or a positive value that indicates which step of the checking process it failed on. Step 4 fixes up various argument values for various trigger sources. There are two bugs in the fixing up of the `convert_arg` value to keep the `scan_begin_arg` value within the range of `unsigned int` when `scan_begin_src` and `convert_src` both have the value `TRIG_TIMER`, which indicates that the corresponding `_arg` values hold a time period in nanoseconds. The code also uses `scan_end_arg` which hold the number of "conversions" within each "scan". The goal is to end up with the scan period being less than or equal to the convert period multiplied by the number of conversions per scan. It intends to do that by clamping the `convert_arg` value to a maximum value of `UINT_MAX / scan_end_arg` rounded down to a multiple of 1000 (`NSEC_PER_USEC`). (The rounding from nanoseconds to microseconds is because the driver is modelling a device that uses a 1 MHz clock for timing. This is partly because that is a more typical timing base for real hardware devices driven by comedi, and partly because the driver used to use `struct timeval` internally.) The first bug is that the code checks if `scan_begin_arg == TRIG_TIMER` when it should be checking if `scan_begin_src == TRIG_TIMER`. The bugged check will always fail because if `scan_begin_src == TRIG_TIMER`, then `scan_begin_arg` will be at least 1000 (`NSEC_PER_USEC`), otherwise `scan_begin_src == TRIG_FOLLOW` and `scan_begin_arg` will be 0. (N.B `TRIG_TIMER` is defined as `0x10`.) The second bug is that is rounding the maximum value down to a multiple of 1000000000 (`NSEC_PER_SEC`) instead of 1000 (`NSEC_PER_USEC`), however this bug is not reached due to the first bug. This patch fixes both bugs. Fixes: 783ddaebd397 ("staging: comedi: comedi_test: support scan_begin_src == TRIG_FOLLOW") Fixes: 5afdcad2f818 ("staging: comedi: comedi_test: limit maximum convert_arg") Cc: stable Signed-off-by: Ian Abbott Link: https://patch.msgid.link/20260422144637.27692-1-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/comedi_test.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/comedi/drivers/comedi_test.c b/drivers/comedi/drivers/comedi_test.c index 01aafce20ef8..4050f66193e5 100644 --- a/drivers/comedi/drivers/comedi_test.c +++ b/drivers/comedi/drivers/comedi_test.c @@ -324,10 +324,10 @@ static int waveform_ai_cmdtest(struct comedi_device *dev, arg = min(arg, rounddown(UINT_MAX, (unsigned int)NSEC_PER_USEC)); arg = NSEC_PER_USEC * DIV_ROUND_CLOSEST(arg, NSEC_PER_USEC); - if (cmd->scan_begin_arg == TRIG_TIMER) { + if (cmd->scan_begin_src == TRIG_TIMER) { /* limit convert_arg to keep scan_begin_arg in range */ limit = UINT_MAX / cmd->scan_end_arg; - limit = rounddown(limit, (unsigned int)NSEC_PER_SEC); + limit = rounddown(limit, (unsigned int)NSEC_PER_USEC); arg = min(arg, limit); } err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg); From 542f5248cb481073203e0dadab5bcbd28aeae308 Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Wed, 22 Apr 2026 17:21:19 +0100 Subject: [PATCH 100/337] comedi: comedi_test: fix check for valid scan_begin_src in waveform_ai_cmdtest() Commit 783ddaebd397 ("staging: comedi: comedi_test: support scan_begin_src == TRIG_FOLLOW") neglected to add a test that `scan_begin_src` has only one bit set. The allowed values are `TRIG_FOLLOW` and `TRIG_TIMER`, but the code incorrectly also allows `TRIG_FOLLOW | TRIG_TIMER`. Add a call to `comedi_check_trigger_is_unique()` to check that only one trigger source bit is set. Fixes: 783ddaebd397 ("staging: comedi: comedi_test: support scan_begin_src == TRIG_FOLLOW") Cc: stable Signed-off-by: Ian Abbott Link: https://patch.msgid.link/20260422162138.36003-1-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/comedi_test.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/comedi/drivers/comedi_test.c b/drivers/comedi/drivers/comedi_test.c index 4050f66193e5..1f430ffc7bd9 100644 --- a/drivers/comedi/drivers/comedi_test.c +++ b/drivers/comedi/drivers/comedi_test.c @@ -274,6 +274,7 @@ static int waveform_ai_cmdtest(struct comedi_device *dev, /* Step 2a : make sure trigger sources are unique */ err |= comedi_check_trigger_is_unique(cmd->convert_src); + err |= comedi_check_trigger_is_unique(cmd->scan_begin_src); err |= comedi_check_trigger_is_unique(cmd->stop_src); /* Step 2b : and mutually compatible */ From c1a0ecbf32c4b397353204e2ec94c5bb9f3300ed Mon Sep 17 00:00:00 2001 From: Radhey Shyam Pandey Date: Tue, 19 May 2026 17:25:29 +0530 Subject: [PATCH 101/337] usb: dwc3: xilinx: fix error handling in zynqmp init error paths Fix error handling and resource cleanup i.e remove invalid phy_exit() after failed phy_init(), route failures through proper cleanup paths and return 0 explicitly on success. Fixes: 84770f028fab ("usb: dwc3: Add driver for Xilinx platforms") Cc: stable@vger.kernel.org Acked-by: Thinh Nguyen Signed-off-by: Radhey Shyam Pandey Link: https://patch.msgid.link/20260519115529.2980421-1-radhey.shyam.pandey@amd.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/dwc3-xilinx.c | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/drivers/usb/dwc3/dwc3-xilinx.c b/drivers/usb/dwc3/dwc3-xilinx.c index f41b0da5e89d..9b9525592a85 100644 --- a/drivers/usb/dwc3/dwc3-xilinx.c +++ b/drivers/usb/dwc3/dwc3-xilinx.c @@ -184,15 +184,13 @@ static int dwc3_xlnx_init_zynqmp(struct dwc3_xlnx *priv_data) } ret = phy_init(priv_data->usb3_phy); - if (ret < 0) { - phy_exit(priv_data->usb3_phy); + if (ret < 0) goto err; - } ret = reset_control_deassert(apbrst); if (ret < 0) { dev_err(dev, "Failed to release APB reset\n"); - goto err; + goto err_phy_exit; } if (priv_data->usb3_phy) { @@ -208,26 +206,24 @@ static int dwc3_xlnx_init_zynqmp(struct dwc3_xlnx *priv_data) ret = reset_control_deassert(crst); if (ret < 0) { dev_err(dev, "Failed to release core reset\n"); - goto err; + goto err_phy_exit; } ret = reset_control_deassert(hibrst); if (ret < 0) { dev_err(dev, "Failed to release hibernation reset\n"); - goto err; + goto err_phy_exit; } ret = phy_power_on(priv_data->usb3_phy); - if (ret < 0) { - phy_exit(priv_data->usb3_phy); - goto err; - } + if (ret < 0) + goto err_phy_exit; /* ulpi reset via gpio-modepin or gpio-framework driver */ reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); if (IS_ERR(reset_gpio)) { - return dev_err_probe(dev, PTR_ERR(reset_gpio), - "Failed to request reset GPIO\n"); + ret = PTR_ERR(reset_gpio); + goto err_phy_power_off; } if (reset_gpio) { @@ -237,6 +233,13 @@ static int dwc3_xlnx_init_zynqmp(struct dwc3_xlnx *priv_data) } dwc3_xlnx_set_coherency(priv_data, XLNX_USB_TRAFFIC_ROUTE_CONFIG); + + return 0; + +err_phy_power_off: + phy_power_off(priv_data->usb3_phy); +err_phy_exit: + phy_exit(priv_data->usb3_phy); err: return ret; } From ca927fc45e4906bdab42426da044fba4d3584f34 Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Tue, 28 Apr 2026 21:18:21 +0800 Subject: [PATCH 102/337] usb: typec: fusb302: Fix resource leak when devm_drm_dp_hpd_bridge_add() fails If devm_drm_dp_hpd_bridge_add() fails during fusb302_probe(), the original code returned directly without cleaning up the resources. Move bridge registration before the IRQ is requested and route bridge registration failures through the existing TCPM unregister and fwnode cleanup path. Fixes: 5d79c525405d ("usb: typec: fusb302: add DRM DP HPD bridge support") Signed-off-by: Felix Gu Reviewed-by: Heikki Krogerus Reviewed-by: Sebastian Reichel Link: https://patch.msgid.link/20260428-fusb-v2-1-aa3b5942cabb@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm/fusb302.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/usb/typec/tcpm/fusb302.c b/drivers/usb/typec/tcpm/fusb302.c index 889c4c29c1b8..9ab1277b7ed1 100644 --- a/drivers/usb/typec/tcpm/fusb302.c +++ b/drivers/usb/typec/tcpm/fusb302.c @@ -1751,19 +1751,22 @@ static int fusb302_probe(struct i2c_client *client) bridge_dev = devm_drm_dp_hpd_bridge_alloc(chip->dev, to_of_node(chip->tcpc_dev.fwnode)); if (IS_ERR(bridge_dev)) { - ret = PTR_ERR(bridge_dev); - dev_err_probe(chip->dev, ret, "failed to alloc bridge\n"); - goto destroy_workqueue; + ret = dev_err_probe(chip->dev, PTR_ERR(bridge_dev), + "failed to alloc bridge\n"); + goto fwnode_put; } chip->tcpm_port = tcpm_register_port(&client->dev, &chip->tcpc_dev); if (IS_ERR(chip->tcpm_port)) { - fwnode_handle_put(chip->tcpc_dev.fwnode); ret = dev_err_probe(dev, PTR_ERR(chip->tcpm_port), "cannot register tcpm port\n"); - goto destroy_workqueue; + goto fwnode_put; } + ret = devm_drm_dp_hpd_bridge_add(chip->dev, bridge_dev); + if (ret) + goto tcpm_unregister_port; + ret = request_threaded_irq(chip->gpio_int_n_irq, NULL, fusb302_irq_intn, IRQF_ONESHOT | IRQF_TRIGGER_LOW, "fsc_interrupt_int_n", chip); @@ -1774,14 +1777,11 @@ static int fusb302_probe(struct i2c_client *client) enable_irq_wake(chip->gpio_int_n_irq); i2c_set_clientdata(client, chip); - ret = devm_drm_dp_hpd_bridge_add(chip->dev, bridge_dev); - if (ret) - return ret; - - return ret; + return 0; tcpm_unregister_port: tcpm_unregister_port(chip->tcpm_port); +fwnode_put: fwnode_handle_put(chip->tcpc_dev.fwnode); destroy_workqueue: fusb302_debugfs_exit(chip); From e6970cda63fd4b4546aeed9d0e2f53a7c95cd09c Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Wed, 13 May 2026 16:53:09 +0800 Subject: [PATCH 103/337] usb: cdns3: plat: fix leaked usb2_phy initialization on usb3_phy acquisition failure Move usb2_phy initialization after usb3_phy acquisition. Fixes: f738957277ba ("usb: cdns3: Split core.c into cdns3-plat and core.c file") Cc: stable Reported-by: sashiko-bot Closes: https://lore.kernel.org/linux-devicetree/agKaEePSFknhDBg2@nchen-desktop/T/#m21e1d9c1574eb127ce03c0c2a1a49002ce435b52 Signed-off-by: Peter Chen Link: https://patch.msgid.link/20260513085310.2217547-2-peter.chen@cixtech.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/cdns3/cdns3-plat.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/usb/cdns3/cdns3-plat.c b/drivers/usb/cdns3/cdns3-plat.c index 735df88774e4..d2e8d1e9007b 100644 --- a/drivers/usb/cdns3/cdns3-plat.c +++ b/drivers/usb/cdns3/cdns3-plat.c @@ -126,15 +126,15 @@ static int cdns3_plat_probe(struct platform_device *pdev) return dev_err_probe(dev, PTR_ERR(cdns->usb2_phy), "Failed to get cdn3,usb2-phy\n"); - ret = phy_init(cdns->usb2_phy); - if (ret) - return ret; - cdns->usb3_phy = devm_phy_optional_get(dev, "cdns3,usb3-phy"); if (IS_ERR(cdns->usb3_phy)) return dev_err_probe(dev, PTR_ERR(cdns->usb3_phy), "Failed to get cdn3,usb3-phy\n"); + ret = phy_init(cdns->usb2_phy); + if (ret) + return ret; + ret = phy_init(cdns->usb3_phy); if (ret) goto err_phy3_init; From ae6f3b82324e4f39ad8443c9020787e6fc889637 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Wed, 13 May 2026 16:53:10 +0800 Subject: [PATCH 104/337] usb: cdns3: plat: fix unbalanced pm_runtime_forbid() call permanently leaks the runtime PM usage counter across bind/unbind cycles Call pm_runtime_allow(dev) conditionally at cdns3_plat_remove. Fixes: f738957277ba ("usb: cdns3: Split core.c into cdns3-plat and core.c file") Cc: stable Reported-by: sashiko-bot Closes: https://lore.kernel.org/linux-devicetree/agKaEePSFknhDBg2@nchen-desktop/T/#m21e1d9c1574eb127ce03c0c2a1a49002ce435b52 Signed-off-by: Peter Chen Link: https://patch.msgid.link/20260513085310.2217547-3-peter.chen@cixtech.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/cdns3/cdns3-plat.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/usb/cdns3/cdns3-plat.c b/drivers/usb/cdns3/cdns3-plat.c index d2e8d1e9007b..94e9706a1806 100644 --- a/drivers/usb/cdns3/cdns3-plat.c +++ b/drivers/usb/cdns3/cdns3-plat.c @@ -186,6 +186,9 @@ static void cdns3_plat_remove(struct platform_device *pdev) struct device *dev = cdns->dev; pm_runtime_get_sync(dev); + if (!(cdns->pdata && (cdns->pdata->quirks & CDNS3_DEFAULT_PM_RUNTIME_ALLOW))) + pm_runtime_allow(dev); + pm_runtime_disable(dev); pm_runtime_put_noidle(dev); cdns_remove(cdns); From c8778ff817a7047d6848fefba99dcb27b1bf01fe Mon Sep 17 00:00:00 2001 From: Yongchao Wu Date: Thu, 14 May 2026 00:00:12 +0800 Subject: [PATCH 105/337] usb: cdns3: gadget: fix request skipping after clearing halt According to the cdns3 datasheet, the EPRST (Endpoint Reset) command causes the DMA engine to reposition its internal pointer to the next Transfer Descriptor (TD) if it was already processing one. This issue is consistently observed during the ADB identification process on macOS hosts, where the host issues a Clear_Halt. Although commit 4bf2dd65135a ("usb: cdns3: gadget: toggle cycle bit before reset endpoint") attempted to avoid DMA advance by toggling the cycle bit, trace logs show that on certain hosts like macOS, the DMA pointer (EP_TRADDR) still shifts after EPRST: cdns3_ctrl_req: Clear Endpoint Feature(Halt ep1out) cdns3_doorbell_epx: ep1out, ep_trbaddr f9c04030 <-- Should be f9c04000 cdns3_gadget_giveback: ep1out: req: ... length: 16384/16384 As shown above, the DMA pointer jumped to the next TD, causing the controller to skip the initial TRBs of the request. This leads to data misalignment and ADB protocol hangs on macOS. Fix this by manually restoring the EP_TRADDR register to the starting physical address of the current request after the EPRST operation is complete. Fixes: 7733f6c32e36 ("usb: cdns3: Add Cadence USB3 DRD Driver") Cc: stable Cc: Peter Chen Signed-off-by: Yongchao Wu Acked-by: Peter Chen Link: https://patch.msgid.link/20260513160012.2547894-1-yongchao.wu@autochips.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/cdns3/cdns3-gadget.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/usb/cdns3/cdns3-gadget.c b/drivers/usb/cdns3/cdns3-gadget.c index 8382231af357..1db8db1b7cc3 100644 --- a/drivers/usb/cdns3/cdns3-gadget.c +++ b/drivers/usb/cdns3/cdns3-gadget.c @@ -2817,9 +2817,19 @@ int __cdns3_gadget_ep_clear_halt(struct cdns3_endpoint *priv_ep) priv_ep->flags &= ~(EP_STALLED | EP_STALL_PENDING); if (request) { - if (trb) + if (trb) { *trb = trb_tmp; + /* + * Per datasheet, EPRST causes DMA to reposition to the next TD. + * Manually reset EP_TRADDR to the current TRB to prevent + * the hardware from skipping the interrupted request. + */ + writel(EP_TRADDR_TRADDR(priv_ep->trb_pool_dma + + priv_req->start_trb * TRB_SIZE), + &priv_dev->regs->ep_traddr); + } + cdns3_rearm_transfer(priv_ep, 1); } From c06e6cd488194e37ed4dc29d1488d1ffb760de60 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Wed, 29 Apr 2026 18:33:32 +0200 Subject: [PATCH 106/337] usb: typec: tcpm: improve handling of DISCOVER_MODES failures UGREEN USB-C Multifunction Adapter Model CM512 (AKA "Revodok 107") exposes two SVIDs: 0xff01 (DP Alt Mode) and 0x1d5c. The DISCOVER_MODES step succeeds for 0xff01 and gets a NAK for 0x1d5c. Currently this results in DP Alt Mode not being registered either, since the modes are only registered once all of them have been discovered. The NAK results in the processing being stopped and thus no Alt modes being registered. Improve the situation by handling the NAK gracefully and continue processing the other modes. Before this change, the TCPM log ends like this: (more log entries before this) [ 5.028287] AMS DISCOVER_SVIDS finished [ 5.028291] cc:=4 [ 5.040040] SVID 1: 0xff01 [ 5.040054] SVID 2: 0x1d5c [ 5.040082] AMS DISCOVER_MODES start [ 5.040096] PD TX, header: 0x1b6f [ 5.050946] PD TX complete, status: 0 [ 5.059609] PD RX, header: 0x264f [1] [ 5.059626] Rx VDM cmd 0xff018043 type 1 cmd 3 len 2 [ 5.059640] AMS DISCOVER_MODES finished [ 5.059644] cc:=4 [ 5.069994] Alternate mode 0: SVID 0xff01, VDO 1: 0x000c0045 [ 5.070029] AMS DISCOVER_MODES start [ 5.070043] PD TX, header: 0x1d6f [ 5.081139] PD TX complete, status: 0 [ 5.087498] PD RX, header: 0x184f [1] [ 5.087515] Rx VDM cmd 0x1d5c8083 type 2 cmd 3 len 1 [ 5.087529] AMS DISCOVER_MODES finished [ 5.087534] cc:=4 (no further log entries after this point) After this patch the TCPM log looks exactly the same, but then continues like this: [ 5.100222] Skip SVID 0x1d5c (failed to discover mode) [ 5.101699] AMS DFP_TO_UFP_ENTER_MODE start (log goes on as the system initializes DP AltMode) Cc: stable Fixes: 41d9d75344d9 ("usb: typec: tcpm: add discover svids and discover modes support for sop'") Reviewed-by: Heikki Krogerus Signed-off-by: Sebastian Reichel Reviewed-by: RD Babiera Reviewed-by: Badhri Jagan Sridharan Link: https://patch.msgid.link/20260429-tcpm-discover-modes-nak-fix-v4-1-75945d0ed30f@collabora.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm/tcpm.c | 97 ++++++++++++++++++++++------------- 1 file changed, 61 insertions(+), 36 deletions(-) diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index ed5f745a8231..7ef746a90a17 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -2149,6 +2149,55 @@ static bool tcpm_cable_vdm_supported(struct tcpm_port *port) tcpm_can_communicate_sop_prime(port); } +static int tcpm_handle_discover_mode(struct tcpm_port *port, u32 *response, + enum tcpm_transmit_type rx_sop_type, + enum tcpm_transmit_type *response_tx_sop_type) +{ + struct typec_port *typec = port->typec_port; + struct pd_mode_data *modep; + + if (rx_sop_type == TCPC_TX_SOP) { + modep = &port->mode_data; + modep->svid_index++; + + if (modep->svid_index < modep->nsvids) { + u16 svid = modep->svids[modep->svid_index]; + *response_tx_sop_type = TCPC_TX_SOP; + response[0] = VDO(svid, 1, + typec_get_negotiated_svdm_version(typec), + CMD_DISCOVER_MODES); + return 1; + } + + if (tcpm_cable_vdm_supported(port)) { + *response_tx_sop_type = TCPC_TX_SOP_PRIME; + response[0] = VDO(USB_SID_PD, 1, + typec_get_cable_svdm_version(typec), + CMD_DISCOVER_SVID); + return 1; + } + + tcpm_register_partner_altmodes(port); + } else if (rx_sop_type == TCPC_TX_SOP_PRIME) { + modep = &port->mode_data_prime; + modep->svid_index++; + + if (modep->svid_index < modep->nsvids) { + u16 svid = modep->svids[modep->svid_index]; + *response_tx_sop_type = TCPC_TX_SOP_PRIME; + response[0] = VDO(svid, 1, + typec_get_cable_svdm_version(typec), + CMD_DISCOVER_MODES); + return 1; + } + + tcpm_register_plug_altmodes(port); + tcpm_register_partner_altmodes(port); + } + + return 0; +} + static int tcpm_pd_svdm(struct tcpm_port *port, struct typec_altmode *adev, const u32 *p, int cnt, u32 *response, enum adev_actions *adev_action, @@ -2406,41 +2455,11 @@ static int tcpm_pd_svdm(struct tcpm_port *port, struct typec_altmode *adev, } break; case CMD_DISCOVER_MODES: - if (rx_sop_type == TCPC_TX_SOP) { - /* 6.4.4.3.3 */ - svdm_consume_modes(port, p, cnt, rx_sop_type); - modep->svid_index++; - if (modep->svid_index < modep->nsvids) { - u16 svid = modep->svids[modep->svid_index]; - *response_tx_sop_type = TCPC_TX_SOP; - response[0] = VDO(svid, 1, svdm_version, - CMD_DISCOVER_MODES); - rlen = 1; - } else if (tcpm_cable_vdm_supported(port)) { - *response_tx_sop_type = TCPC_TX_SOP_PRIME; - response[0] = VDO(USB_SID_PD, 1, - typec_get_cable_svdm_version(typec), - CMD_DISCOVER_SVID); - rlen = 1; - } else { - tcpm_register_partner_altmodes(port); - } - } else if (rx_sop_type == TCPC_TX_SOP_PRIME) { - /* 6.4.4.3.3 */ - svdm_consume_modes(port, p, cnt, rx_sop_type); - modep_prime->svid_index++; - if (modep_prime->svid_index < modep_prime->nsvids) { - u16 svid = modep_prime->svids[modep_prime->svid_index]; - *response_tx_sop_type = TCPC_TX_SOP_PRIME; - response[0] = VDO(svid, 1, - typec_get_cable_svdm_version(typec), - CMD_DISCOVER_MODES); - rlen = 1; - } else { - tcpm_register_plug_altmodes(port); - tcpm_register_partner_altmodes(port); - } - } + /* 6.4.4.3.3 */ + svdm_consume_modes(port, p, cnt, rx_sop_type); + rlen = tcpm_handle_discover_mode(port, response, + rx_sop_type, + response_tx_sop_type); break; case CMD_ENTER_MODE: *response_tx_sop_type = rx_sop_type; @@ -2483,9 +2502,15 @@ static int tcpm_pd_svdm(struct tcpm_port *port, struct typec_altmode *adev, switch (cmd) { case CMD_DISCOVER_IDENT: case CMD_DISCOVER_SVID: - case CMD_DISCOVER_MODES: case VDO_CMD_VENDOR(0) ... VDO_CMD_VENDOR(15): break; + case CMD_DISCOVER_MODES: + tcpm_log(port, "Skip SVID 0x%04x (failed to discover mode)", + PD_VDO_SVID_SVID0(p[0])); + rlen = tcpm_handle_discover_mode(port, response, + rx_sop_type, + response_tx_sop_type); + break; case CMD_ENTER_MODE: /* Back to USB Operation */ *adev_action = ADEV_NOTIFY_USB_AND_QUEUE_VDM; From b80e7d34c7ea6a564525119d6138fbb577a23dba Mon Sep 17 00:00:00 2001 From: Myrrh Periwinkle Date: Tue, 19 May 2026 18:41:39 +0700 Subject: [PATCH 107/337] usb: typec: ucsi: Check if power role change actually happened before handling The CrOS EC may send a connector status change event with the power direction changed flag set even if the power direction hasn't actually changed after initiating a SET_PDR command internally [1]. In practice this happens on every system suspend due to other changes performed by the EC [2][3][4], causing suspend to fail. Fix this by checking if the power role change actually happened before handling it. [1]: https://source.chromium.org/chromiumos/chromiumos/codesearch/+/main:src/platform/ec/zephyr/subsys/pd_controller/pdc_power_mgmt.c;l=1689;drc=2d5a1cffce4e5ac8a39442cb3b764d2d5e1cf794 [2]: https://source.chromium.org/chromiumos/chromiumos/codesearch/+/main:src/platform/ec/zephyr/subsys/pd_controller/pdc_power_mgmt.c;l=3923;drc=2d5a1cffce4e5ac8a39442cb3b764d2d5e1cf794 [3]: https://source.chromium.org/chromiumos/chromiumos/codesearch/+/main:src/platform/ec/zephyr/subsys/pd_controller/pdc_power_mgmt.c;l=5094;drc=2d5a1cffce4e5ac8a39442cb3b764d2d5e1cf794 [4]: https://source.chromium.org/chromiumos/chromiumos/codesearch/+/main:src/platform/ec/zephyr/subsys/pd_controller/pdc_power_mgmt.c;l=2229;drc=2d5a1cffce4e5ac8a39442cb3b764d2d5e1cf794 Cc: stable Fixes: 7616f006db07 ("usb: typec: ucsi: Update power_supply on power role change") Signed-off-by: Myrrh Periwinkle Reported-and-tested-by: Sergey Senozhatsky Reviewed-by: Heikki Krogerus Link: https://patch.msgid.link/20260519-ucsi-fix-2-v1-1-6f1239535187@qtmlabs.xyz Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/ucsi.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index 539dc706798d..53c101bd48e2 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -1277,7 +1277,7 @@ static void ucsi_handle_connector_change(struct work_struct *work) work); struct ucsi *ucsi = con->ucsi; u8 curr_scale, volt_scale; - enum typec_role role; + enum typec_role role, prev_role; u16 change; int ret; u32 val; @@ -1288,6 +1288,8 @@ static void ucsi_handle_connector_change(struct work_struct *work) dev_err_once(ucsi->dev, "%s entered without EVENT_PENDING\n", __func__); + prev_role = UCSI_CONSTAT(con, PWR_DIR); + ret = ucsi_get_connector_status(con, true); if (ret) { dev_err(ucsi->dev, "%s: GET_CONNECTOR_STATUS failed (%d)\n", @@ -1304,7 +1306,7 @@ static void ucsi_handle_connector_change(struct work_struct *work) change = UCSI_CONSTAT(con, CHANGE); role = UCSI_CONSTAT(con, PWR_DIR); - if (change & UCSI_CONSTAT_POWER_DIR_CHANGE) { + if ((change & UCSI_CONSTAT_POWER_DIR_CHANGE) && role != prev_role) { typec_set_pwr_role(con->port, role); ucsi_port_psy_changed(con); From d98d413ca65d0790a8f3695d0a5845538958ab84 Mon Sep 17 00:00:00 2001 From: Myrrh Periwinkle Date: Tue, 19 May 2026 18:41:40 +0700 Subject: [PATCH 108/337] usb: typec: ucsi: Don't update power_supply on power role change if not connected We only need to update the power_supply on power role change if the port is connected, because otherwise the online status should be the same for both cases. Cc: stable Fixes: 7616f006db07 ("usb: typec: ucsi: Update power_supply on power role change") Signed-off-by: Myrrh Periwinkle Reported-and-tested-by: Sergey Senozhatsky Link: https://patch.msgid.link/20260519-ucsi-fix-2-v1-2-6f1239535187@qtmlabs.xyz Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/ucsi.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index 53c101bd48e2..61cb24ed820f 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -1308,7 +1308,12 @@ static void ucsi_handle_connector_change(struct work_struct *work) if ((change & UCSI_CONSTAT_POWER_DIR_CHANGE) && role != prev_role) { typec_set_pwr_role(con->port, role); - ucsi_port_psy_changed(con); + + /* Some power_supply properties vary depending on the power direction when + * connected + */ + if (UCSI_CONSTAT(con, CONNECTED)) + ucsi_port_psy_changed(con); /* Complete pending power role swap */ if (!completion_done(&con->complete)) From f706e6a4ce75585af979aec3dcbdce68bc76306b Mon Sep 17 00:00:00 2001 From: Dhabaleshwar Das Date: Thu, 21 May 2026 00:00:00 +0530 Subject: [PATCH 109/337] accel/rocket: fix UAF via dangling GEM handle in create_bo rocket_ioctl_create_bo() inserts a GEM handle into the file's IDR via drm_gem_handle_create() early on, then performs several operations that can fail (sgt allocation, drm_mm insert, iommu_map). If any fail after the handle is live, the error path calls drm_gem_shmem_object_free() which kfree's the object without removing the handle from the IDR. This leaves a dangling handle pointing to freed slab memory. Any subsequent ioctl using that handle (PREP_BO, FINI_BO, SUBMIT) calls drm_gem_object_lookup() and dereferences freed memory (UAF). Fix by moving drm_gem_handle_create() to after all fallible operations succeed, matching the pattern used by panfrost, lima, and etnaviv. Also fix drm_mm_insert_node_generic() whose return value was silently overwritten by iommu_map_sgtable() on the next line. Add the missing error check. [tomeu: Move handle creation to the very end] Fixes: 658ebeac3351 ("accel/rocket: Add IOCTL for BO creation") Reported-by: Dhabaleshwar Das Signed-off-by: Dhabaleshwar Das Reviewed-by: Tomeu Vizoso Link: https://patch.msgid.link/20260521165720.2113571-1-tomeu@tomeuvizoso.net Signed-off-by: Tomeu Vizoso --- drivers/accel/rocket/rocket_gem.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/drivers/accel/rocket/rocket_gem.c b/drivers/accel/rocket/rocket_gem.c index c8084719208a..a5fffa51ff35 100644 --- a/drivers/accel/rocket/rocket_gem.c +++ b/drivers/accel/rocket/rocket_gem.c @@ -79,11 +79,6 @@ int rocket_ioctl_create_bo(struct drm_device *dev, void *data, struct drm_file * rkt_obj->size = args->size; rkt_obj->offset = 0; - ret = drm_gem_handle_create(file, gem_obj, &args->handle); - drm_gem_object_put(gem_obj); - if (ret) - goto err; - sgt = drm_gem_shmem_get_pages_sgt(shmem_obj); if (IS_ERR(sgt)) { ret = PTR_ERR(sgt); @@ -95,6 +90,8 @@ int rocket_ioctl_create_bo(struct drm_device *dev, void *data, struct drm_file * rkt_obj->size, PAGE_SIZE, 0, 0); mutex_unlock(&rocket_priv->mm_lock); + if (ret) + goto err; ret = iommu_map_sgtable(rocket_priv->domain->domain, rkt_obj->mm.start, @@ -112,8 +109,18 @@ int rocket_ioctl_create_bo(struct drm_device *dev, void *data, struct drm_file * args->offset = drm_vma_node_offset_addr(&gem_obj->vma_node); args->dma_address = rkt_obj->mm.start; + ret = drm_gem_handle_create(file, gem_obj, &args->handle); + if (ret) + goto err_unmap; + + drm_gem_object_put(gem_obj); + return 0; +err_unmap: + iommu_unmap(rocket_priv->domain->domain, + rkt_obj->mm.start, rkt_obj->size); + err_remove_node: mutex_lock(&rocket_priv->mm_lock); drm_mm_remove_node(&rkt_obj->mm); From a8878e19d2f5205ad1f170fc230c2cc25a3b9390 Mon Sep 17 00:00:00 2001 From: Lizhi Hou Date: Wed, 20 May 2026 15:35:31 -0700 Subject: [PATCH 110/337] accel/amdxdna: Block running when IOMMU is off The AIE2 device firmware requires IOMMU on. Closes: https://gitlab.freedesktop.org/drm/amd/-/work_items/5319 Reviewed-by: Mario Limonciello Signed-off-by: Lizhi Hou Link: https://patch.msgid.link/20260520223531.1403302-1-lizhi.hou@amd.com --- drivers/accel/amdxdna/aie2_pci.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/accel/amdxdna/aie2_pci.c b/drivers/accel/amdxdna/aie2_pci.c index f1ac4e00bd9f..4500b9ccb02e 100644 --- a/drivers/accel/amdxdna/aie2_pci.c +++ b/drivers/accel/amdxdna/aie2_pci.c @@ -511,6 +511,11 @@ static int aie2_init(struct amdxdna_dev *xdna) return -EINVAL; } + if (!xdna->group) { + XDNA_ERR(xdna, "Running without IOMMU not supported"); + return -EINVAL; + } + ndev = drmm_kzalloc(&xdna->ddev, sizeof(*ndev), GFP_KERNEL); if (!ndev) return -ENOMEM; From b7c9047f851e80b580aba485b61785c7554b992c Mon Sep 17 00:00:00 2001 From: Harshal Dev Date: Thu, 16 Apr 2026 17:29:29 +0530 Subject: [PATCH 111/337] arm64: dts: qcom: milos: Add power-domain and iface clk for ice node Qualcomm in-line crypto engine (ICE) platform driver specifies and votes for its own resources. Before accessing ICE hardware during probe, to avoid potential unclocked register access issues (when clk_ignore_unused is not passed on the kernel command line), in addition to the 'core' clock the 'iface' clock should also be turned on by the driver. This can only be done if the UFS_PHY_GDSC power domain is enabled. Specify both the UFS_PHY_GDSC power domain and the 'iface' clock in the ICE node for milos. Fixes: 04bb37433330e ("arm64: dts: qcom: milos: Add UFS nodes") Signed-off-by: Harshal Dev Reviewed-by: Konrad Dybcio Reviewed-by: Kuldeep Singh Link: https://lore.kernel.org/r/20260416-qcom_ice_power_and_clk_vote-v5-12-5ccf5d7e2846@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- arch/arm64/boot/dts/qcom/milos.dtsi | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/qcom/milos.dtsi b/arch/arm64/boot/dts/qcom/milos.dtsi index 4a64a98a434b..a6e463f3885d 100644 --- a/arch/arm64/boot/dts/qcom/milos.dtsi +++ b/arch/arm64/boot/dts/qcom/milos.dtsi @@ -1275,7 +1275,11 @@ ice: crypto@1d88000 { "qcom,inline-crypto-engine"; reg = <0x0 0x01d88000 0x0 0x18000>; - clocks = <&gcc GCC_UFS_PHY_ICE_CORE_CLK>; + clocks = <&gcc GCC_UFS_PHY_ICE_CORE_CLK>, + <&gcc GCC_UFS_PHY_AHB_CLK>; + clock-names = "core", + "iface"; + power-domains = <&gcc UFS_PHY_GDSC>; }; tcsr_mutex: hwlock@1f40000 { From 90825ab392ac15a51f62e3f561ad77e0226a1cfc Mon Sep 17 00:00:00 2001 From: Harshal Dev Date: Thu, 16 Apr 2026 17:29:30 +0530 Subject: [PATCH 112/337] arm64: dts: qcom: eliza: Add power-domain and iface clk for ice node Qualcomm in-line crypto engine (ICE) platform driver specifies and votes for its own resources. Before accessing ICE hardware during probe, to avoid potential unclocked register access issues (when clk_ignore_unused is not passed on the kernel command line), in addition to the 'core' clock the 'iface' clock should also be turned on by the driver. This can only be done if the GCC_UFS_PHY_GDSC power domain is enabled. Specify both the GCC_UFS_PHY_GDSC power domain and the 'iface' clock in the ICE node for eliza. Fixes: af20af39fc09b ("arm64: dts: qcom: Introduce Eliza Soc base dtsi") Signed-off-by: Harshal Dev Reviewed-by: Konrad Dybcio Fixes: 54a4f0239f2e ("KVM: MMU: make kvm_mmu_zap_page() return the Reviewed-by: Kuldeep Singh Link: https://lore.kernel.org/r/20260416-qcom_ice_power_and_clk_vote-v5-13-5ccf5d7e2846@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- arch/arm64/boot/dts/qcom/eliza.dtsi | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/qcom/eliza.dtsi b/arch/arm64/boot/dts/qcom/eliza.dtsi index 4a7a0ac40ce6..7e97361a5dc5 100644 --- a/arch/arm64/boot/dts/qcom/eliza.dtsi +++ b/arch/arm64/boot/dts/qcom/eliza.dtsi @@ -843,7 +843,11 @@ ice: crypto@1d88000 { "qcom,inline-crypto-engine"; reg = <0x0 0x01d88000 0x0 0x18000>; - clocks = <&gcc GCC_UFS_PHY_ICE_CORE_CLK>; + clocks = <&gcc GCC_UFS_PHY_ICE_CORE_CLK>, + <&gcc GCC_UFS_PHY_AHB_CLK>; + clock-names = "core", + "iface"; + power-domains = <&gcc GCC_UFS_PHY_GDSC>; }; tcsr_mutex: hwlock@1f40000 { From 462a85f9f887a4fef36550bb76c7f7d7a0fa296c Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Wed, 20 May 2026 21:27:04 +0530 Subject: [PATCH 113/337] soc: qcom: ice: Fix the error code when 'qcom,ice' property is not found When both 'ice' reg entry and 'qcom,ice' property are not found in DT, then it implies that ICE is not supported. So return -EOPNOTSUPP instead of -ENODEV to client drivers to specify ICE functionality is not supported. Fixes: b9ab7217dd7d ("soc: qcom: ice: Return proper error codes from devm_of_qcom_ice_get() instead of NULL") Reported-by: Marek Szyprowski Closes: https://lore.kernel.org/linux-arm-msm/8bac0358-9da0-4cbb-98ee-333b85ba4908@samsung.com Signed-off-by: Manivannan Sadhasivam Link: https://lore.kernel.org/r/20260520155704.130803-1-manivannan.sadhasivam@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/ice.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/soc/qcom/ice.c b/drivers/soc/qcom/ice.c index e26d5a64763c..5f20108aa03e 100644 --- a/drivers/soc/qcom/ice.c +++ b/drivers/soc/qcom/ice.c @@ -658,7 +658,7 @@ static struct qcom_ice *of_qcom_ice_get(struct device *dev) struct device_node *node __free(device_node) = of_parse_phandle(dev->of_node, "qcom,ice", 0); if (!node) - return ERR_PTR(-ENODEV); + return ERR_PTR(-EOPNOTSUPP); pdev = of_find_device_by_node(node); if (!pdev) { From e194ce048f5a6c549b3a23a8c568c6470f40f772 Mon Sep 17 00:00:00 2001 From: Wentao Liang Date: Thu, 9 Apr 2026 10:11:04 +0000 Subject: [PATCH 114/337] usb: musb: omap2430: Fix use-after-free in omap2430_probe() In omap2430_probe(), of_node_put(np) is called prematurely before the last access to np, leading to a use-after-free if the node's reference count drops to zero. Move the of_node_put() calls after the last use of np in both the success and error paths. Fixes: ffbe2feac59b ("usb: musb: omap2430: Fix probe regression for missing resources") Cc: stable Signed-off-by: Wentao Liang Link: https://patch.msgid.link/20260409101104.480623-1-vulab@iscas.ac.cn Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/omap2430.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index 48bb9bfb2204..333ab79f0ca9 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -337,7 +337,6 @@ static int omap2430_probe(struct platform_device *pdev) } else { device_set_of_node_from_dev(&musb->dev, &pdev->dev); } - of_node_put(np); glue->dev = &pdev->dev; glue->musb = musb; @@ -455,6 +454,7 @@ static int omap2430_probe(struct platform_device *pdev) dev_err(&pdev->dev, "failed to register musb device\n"); goto err_disable_rpm; } + of_node_put(np); return 0; @@ -464,6 +464,7 @@ static int omap2430_probe(struct platform_device *pdev) if (!IS_ERR(glue->control_otghs)) put_device(glue->control_otghs); err_put_musb: + of_node_put(np); platform_device_put(musb); return ret; From 4f88d65def6f3c90121601b4f62a4c967f3063a6 Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Mon, 13 Apr 2026 22:21:19 +0800 Subject: [PATCH 115/337] usb: gadget: f_hid: fix device reference leak in hidg_alloc() hidg_alloc() initializes hidg->dev with device_initialize() before calling dev_set_name(). If dev_set_name() fails, the function currently jumps to err_unlock and returns without calling put_device(). This leaves the device reference unbalanced and prevents hidg_release() from being called. Calling put_device() here is also safe, since hidg_release() only frees resources owned by hidg. The issue was identified by a static analysis tool I developed and confirmed by manual review. Route the dev_set_name() failure path through err_put_device so the device reference is dropped properly. Fixes: 89ff3dfac604 ("usb: gadget: f_hid: fix f_hidg lifetime vs cdev") Cc: stable Reviewed-by: Johan Hovold Signed-off-by: Guangshuo Li Reviewed-by: Johan Hovold johan@kernel.org Link: https://patch.msgid.link/20260413142119.2977716-1-lgs201920130244@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_hid.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/usb/gadget/function/f_hid.c b/drivers/usb/gadget/function/f_hid.c index c5a12a6760ea..3c6b43d06a6d 100644 --- a/drivers/usb/gadget/function/f_hid.c +++ b/drivers/usb/gadget/function/f_hid.c @@ -1622,7 +1622,7 @@ static struct usb_function *hidg_alloc(struct usb_function_instance *fi) hidg->dev.devt = MKDEV(major, opts->minor); ret = dev_set_name(&hidg->dev, "hidg%d", opts->minor); if (ret) - goto err_unlock; + goto err_put_device; hidg->bInterfaceSubClass = opts->subclass; hidg->bInterfaceProtocol = opts->protocol; @@ -1659,7 +1659,6 @@ static struct usb_function *hidg_alloc(struct usb_function_instance *fi) err_put_device: put_device(&hidg->dev); -err_unlock: mutex_unlock(&opts->lock); return ERR_PTR(ret); } From c8547c74988e0b5f4cbb1b895e2a57aae084f070 Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Mon, 27 Apr 2026 23:36:51 +0800 Subject: [PATCH 116/337] usb: gadget: net2280: Fix double free in probe error path usb_initialize_gadget() installs gadget_release() as the release callback for the embedded gadget device. The struct net2280 instance is therefore released through gadget_release() when the gadget device's last reference is dropped. The probe error path calls net2280_remove(), which tears down the partially initialized device and drops the gadget reference with usb_put_gadget(). Calling kfree(dev) afterwards can free the same object again. Drop the explicit kfree() and let the gadget device release callback handle the final free. This issue was found by a static analysis tool I am developing. Fixes: f770fbec4165 ("USB: UDC: net2280: Fix memory leaks") Cc: stable Signed-off-by: Guangshuo Li Reviewed-by: Alan Stern Link: https://patch.msgid.link/20260427153651.337846-1-lgs201920130244@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/net2280.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c index d02765bd49ce..7c5f30cfd24d 100644 --- a/drivers/usb/gadget/udc/net2280.c +++ b/drivers/usb/gadget/udc/net2280.c @@ -3790,10 +3790,8 @@ static int net2280_probe(struct pci_dev *pdev, const struct pci_device_id *id) return 0; done: - if (dev) { + if (dev) net2280_remove(pdev); - kfree(dev); - } return retval; } From 68aa70648b625fa684bc0b71bbfd905f4943ca20 Mon Sep 17 00:00:00 2001 From: Kai Aizen Date: Thu, 30 Apr 2026 20:56:43 +0300 Subject: [PATCH 117/337] usb: gadget: uvc: hold opts->lock across XU walks in uvc_function_bind uvc_function_bind() walks &opts->extension_units twice without holding opts->lock: - directly, for the iExtension string-descriptor fixup loop; - indirectly, four times via uvc_copy_descriptors() (once per speed), where the helper iterates uvc->desc.extension_units (which aliases &opts->extension_units) to size and emit XU descriptors. The configfs side (uvcg_extension_make / uvcg_extension_drop, in drivers/usb/gadget/function/uvc_configfs.c) takes opts->lock around its list_add_tail / list_del operations. A privileged userspace process that holds the configfs subtree open and writes the gadget UDC name to bind the function while concurrently rmdir()'ing an extensions subdir can race uvcg_extension_drop() against the bind-time list walks and dereference a freed struct uvcg_extension. Hold opts->lock from the start of the XU string-descriptor fixup through the last uvc_copy_descriptors() call, releasing on the descriptor-error path via a new error_unlock label that drops the lock before falling through to the existing error label. This matches the locking discipline of the configfs callbacks and removes the only remaining unsynchronised reader of the XU list during bind. Reachability: only privileged processes that can mount configfs and write to gadget UDC files can trigger the race, so this is a correctness fix rather than a security boundary. Fixes: 0525210c9840 ("usb: gadget: uvc: Allow definition of XUs in configfs") Cc: stable Signed-off-by: Kai Aizen Link: https://patch.msgid.link/20260430175643.67120-1-kai.aizen.dev@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_uvc.c | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index 8d404d88391c..73dc7e42875f 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -768,6 +768,16 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) uvc_hs_streaming_ep.bEndpointAddress = uvc->video.ep->address; uvc_ss_streaming_ep.bEndpointAddress = uvc->video.ep->address; + /* + * Hold opts->lock across both the XU string-descriptor fixup below and + * the descriptor-copy block further down. Without this, configfs + * uvcg_extension_drop() (which takes opts->lock) can race with the + * list_for_each_entry() walks here and inside uvc_copy_descriptors(), + * leading to a UAF on a freed struct uvcg_extension. See + * drivers/usb/gadget/function/uvc_configfs.c::uvcg_extension_drop(). + */ + mutex_lock(&opts->lock); + /* * XUs can have an arbitrary string descriptor describing them. If they * have one pick up the ID. @@ -785,7 +795,7 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) ARRAY_SIZE(uvc_en_us_strings)); if (IS_ERR(us)) { ret = PTR_ERR(us); - goto error; + goto error_unlock; } uvc_iad.iFunction = opts->iad_index ? cdev->usb_strings[opts->iad_index].id : @@ -799,14 +809,14 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) /* Allocate interface IDs. */ if ((ret = usb_interface_id(c, f)) < 0) - goto error; + goto error_unlock; uvc_iad.bFirstInterface = ret; uvc_control_intf.bInterfaceNumber = ret; uvc->control_intf = ret; opts->control_interface = ret; if ((ret = usb_interface_id(c, f)) < 0) - goto error; + goto error_unlock; uvc_streaming_intf_alt0.bInterfaceNumber = ret; uvc_streaming_intf_alt1.bInterfaceNumber = ret; uvc->streaming_intf = ret; @@ -817,30 +827,32 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) if (IS_ERR(f->fs_descriptors)) { ret = PTR_ERR(f->fs_descriptors); f->fs_descriptors = NULL; - goto error; + goto error_unlock; } f->hs_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_HIGH); if (IS_ERR(f->hs_descriptors)) { ret = PTR_ERR(f->hs_descriptors); f->hs_descriptors = NULL; - goto error; + goto error_unlock; } f->ss_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_SUPER); if (IS_ERR(f->ss_descriptors)) { ret = PTR_ERR(f->ss_descriptors); f->ss_descriptors = NULL; - goto error; + goto error_unlock; } f->ssp_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_SUPER_PLUS); if (IS_ERR(f->ssp_descriptors)) { ret = PTR_ERR(f->ssp_descriptors); f->ssp_descriptors = NULL; - goto error; + goto error_unlock; } + mutex_unlock(&opts->lock); + /* Preallocate control endpoint request. */ uvc->control_req = usb_ep_alloc_request(cdev->gadget->ep0, GFP_KERNEL); uvc->control_buf = kmalloc(UVC_MAX_REQUEST_SIZE, GFP_KERNEL); @@ -872,6 +884,8 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) return 0; +error_unlock: + mutex_unlock(&opts->lock); v4l2_error: v4l2_device_unregister(&uvc->v4l2_dev); error: From 5a4c828b8b29b47534814ade26d9aee09d5101fc Mon Sep 17 00:00:00 2001 From: Wei-Cheng Chen Date: Tue, 5 May 2026 19:26:30 +0800 Subject: [PATCH 118/337] xhci: tegra: Fix ghost USB device on dual-role port unplug When a USB device is unplugged from the dual-role port, the device-mode path in tegra_xhci_id_work() explicitly clears both SS and HS port power via direct hub_control ClearPortFeature(POWER) calls. This preempts the xHCI controller's normal disconnect processing -- PORT_CSC is never generated, the USB core never sees the disconnect, and the device remains in its internal tree as a ghost visible in lsusb. Add an otg_set_port_power flag to control whether the dual-role switch path performs explicit port power management. SoCs that need it (Tegra124 / Tegra210 / Tegra186) set the flag; later SoCs (Tegra194 and beyond) rely on the PHY mode change to handle disconnect naturally and skip all port power calls. Within the port power path, otg_reset_sspi additionally gates the SSPI reset sequence on host-mode entry for SoCs that require it. Flags set per SoC: Tegra124, Tegra186 -> otg_set_port_power Tegra210 -> otg_set_port_power, otg_reset_sspi Tegra194 and later -> (none) Fixes: f836e7843036 ("usb: xhci-tegra: Add OTG support") Cc: stable Signed-off-by: Wei-Cheng Chen Link: https://patch.msgid.link/20260505112630.217704-1-weichengc@nvidia.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-tegra.c | 71 ++++++++++++++++++++--------------- 1 file changed, 40 insertions(+), 31 deletions(-) diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c index d2214d309e96..d5637b376367 100644 --- a/drivers/usb/host/xhci-tegra.c +++ b/drivers/usb/host/xhci-tegra.c @@ -247,6 +247,7 @@ struct tegra_xusb_soc { bool has_ipfs; bool lpm_support; bool otg_reset_sspi; + bool otg_set_port_power; bool has_bar2; }; @@ -1352,12 +1353,13 @@ static void tegra_xhci_id_work(struct work_struct *work) struct tegra_xusb_mbox_msg msg; struct phy *phy = tegra_xusb_get_phy(tegra, "usb2", tegra->otg_usb2_port); + bool host_mode = tegra->host_mode; u32 status; int ret; - dev_dbg(tegra->dev, "host mode %s\n", str_on_off(tegra->host_mode)); + dev_dbg(tegra->dev, "host mode %s\n", str_on_off(host_mode)); - if (tegra->host_mode) + if (host_mode) phy_set_mode_ext(phy, PHY_MODE_USB_OTG, USB_ROLE_HOST); else phy_set_mode_ext(phy, PHY_MODE_USB_OTG, USB_ROLE_NONE); @@ -1366,41 +1368,43 @@ static void tegra_xhci_id_work(struct work_struct *work) tegra->otg_usb2_port); pm_runtime_get_sync(tegra->dev); - if (tegra->host_mode) { - /* switch to host mode */ - if (tegra->otg_usb3_port >= 0) { - if (tegra->soc->otg_reset_sspi) { - /* set PP=0 */ - tegra_xhci_hc_driver.hub_control( - xhci->shared_hcd, GetPortStatus, - 0, tegra->otg_usb3_port+1, - (char *) &status, sizeof(status)); - if (status & USB_SS_PORT_STAT_POWER) - tegra_xhci_set_port_power(tegra, false, - false); + if (tegra->soc->otg_set_port_power) { + if (host_mode) { + /* switch to host mode */ + if (tegra->otg_usb3_port >= 0) { + if (tegra->soc->otg_reset_sspi) { + /* set PP=0 */ + tegra_xhci_hc_driver.hub_control( + xhci->shared_hcd, GetPortStatus, + 0, tegra->otg_usb3_port+1, + (char *) &status, sizeof(status)); + if (status & USB_SS_PORT_STAT_POWER) + tegra_xhci_set_port_power(tegra, false, + false); - /* reset OTG port SSPI */ - msg.cmd = MBOX_CMD_RESET_SSPI; - msg.data = tegra->otg_usb3_port+1; + /* reset OTG port SSPI */ + msg.cmd = MBOX_CMD_RESET_SSPI; + msg.data = tegra->otg_usb3_port+1; - ret = tegra_xusb_mbox_send(tegra, &msg); - if (ret < 0) { - dev_info(tegra->dev, - "failed to RESET_SSPI %d\n", - ret); + ret = tegra_xusb_mbox_send(tegra, &msg); + if (ret < 0) { + dev_info(tegra->dev, + "failed to RESET_SSPI %d\n", + ret); + } } + + tegra_xhci_set_port_power(tegra, false, true); } - tegra_xhci_set_port_power(tegra, false, true); + tegra_xhci_set_port_power(tegra, true, true); + + } else { + if (tegra->otg_usb3_port >= 0) + tegra_xhci_set_port_power(tegra, false, false); + + tegra_xhci_set_port_power(tegra, true, false); } - - tegra_xhci_set_port_power(tegra, true, true); - - } else { - if (tegra->otg_usb3_port >= 0) - tegra_xhci_set_port_power(tegra, false, false); - - tegra_xhci_set_port_power(tegra, true, false); } pm_runtime_put_autosuspend(tegra->dev); } @@ -2553,6 +2557,7 @@ static const struct tegra_xusb_soc tegra124_soc = { .scale_ss_clock = true, .has_ipfs = true, .otg_reset_sspi = false, + .otg_set_port_power = true, .ops = &tegra124_ops, .mbox = { .cmd = 0xe4, @@ -2593,6 +2598,7 @@ static const struct tegra_xusb_soc tegra210_soc = { .scale_ss_clock = false, .has_ipfs = true, .otg_reset_sspi = true, + .otg_set_port_power = true, .ops = &tegra124_ops, .mbox = { .cmd = 0xe4, @@ -2640,6 +2646,7 @@ static const struct tegra_xusb_soc tegra186_soc = { .scale_ss_clock = false, .has_ipfs = false, .otg_reset_sspi = false, + .otg_set_port_power = true, .ops = &tegra124_ops, .mbox = { .cmd = 0xe4, @@ -2673,6 +2680,7 @@ static const struct tegra_xusb_soc tegra194_soc = { .scale_ss_clock = false, .has_ipfs = false, .otg_reset_sspi = false, + .otg_set_port_power = false, .ops = &tegra124_ops, .mbox = { .cmd = 0x68, @@ -2708,6 +2716,7 @@ static const struct tegra_xusb_soc tegra234_soc = { .scale_ss_clock = false, .has_ipfs = false, .otg_reset_sspi = false, + .otg_set_port_power = false, .ops = &tegra234_ops, .mbox = { .cmd = XUSB_BAR2_ARU_MBOX_CMD, From 52f2ad3f7e5eb3b5908e1d685d4342519dc9cfcd Mon Sep 17 00:00:00 2001 From: Heitor Alves de Siqueira Date: Tue, 5 May 2026 15:56:03 -0300 Subject: [PATCH 119/337] usb: usbtmc: check URB actual_length for interrupt-IN notifications USBTMC devices can use an optional interrupt endpoint for notification messages. These typically contain two-byte headers indicating the payload format, but the driver does not check if these headers are present before accessing the data buffers. In cases where the URB actual_length is not enough to fit these headers, the driver will either cause an out-of-bounds read, or consume stale leftover data from a previous notification. Fix by checking if actual_data contains enough bytes for the headers, otherwise resubmit URB to the interrupt endpoint. Fixes: dbf3e7f654c0 ("Implement an ioctl to support the USMTMC-USB488 READ_STATUS_BYTE operation.") Reported-by: syzbot+abbfd103085885cf16a2@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=abbfd103085885cf16a2 Cc: stable Suggested-by: Michal Pecio Signed-off-by: Heitor Alves de Siqueira Link: https://patch.msgid.link/20260505-usbtmc-iin-size-v3-1-a36113f62db7@igalia.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/usbtmc.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c index bd9347804dec..e15efd0c5ca7 100644 --- a/drivers/usb/class/usbtmc.c +++ b/drivers/usb/class/usbtmc.c @@ -2306,6 +2306,14 @@ static void usbtmc_interrupt(struct urb *urb) switch (status) { case 0: /* SUCCESS */ + /* ensure at least two bytes of headers were transferred */ + if (urb->actual_length < 2) { + dev_warn(dev, + "actual length %d not sufficient for interrupt headers\n", + urb->actual_length); + goto exit; + } + /* check for valid STB notification */ if (data->iin_buffer[0] > 0x81) { data->bNotify1 = data->iin_buffer[0]; From 121d2f682ba912b1427cddca7cf84840f41cc620 Mon Sep 17 00:00:00 2001 From: Heitor Alves de Siqueira Date: Tue, 5 May 2026 15:56:04 -0300 Subject: [PATCH 120/337] usb: usbtmc: reject interrupt endpoints with small wMaxPacketSize The USB488 subclass specification requires interrupt wMaxPacketSize to be 0x02, unless the device sends vendor-specific notifications. Endpoints that advertise less than 2 bytes for wMaxPacketSize are unlikely to work with the current driver, as URBs will not have enough space for interrupt headers. Considering that any notification URBs will be ignored by the driver, reject these endpoints early during probe. Fixes: 041370cce889 ("USB: usbtmc: refactor endpoint retrieval") Cc: stable Suggested-by: Michal Pecio Signed-off-by: Heitor Alves de Siqueira Link: https://patch.msgid.link/20260505-usbtmc-iin-size-v3-2-a36113f62db7@igalia.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/usbtmc.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c index e15efd0c5ca7..af9ae55dae14 100644 --- a/drivers/usb/class/usbtmc.c +++ b/drivers/usb/class/usbtmc.c @@ -2440,6 +2440,12 @@ static int usbtmc_probe(struct usb_interface *intf, data->iin_ep = int_in->bEndpointAddress; data->iin_wMaxPacketSize = usb_endpoint_maxp(int_in); data->iin_interval = int_in->bInterval; + /* wMaxPacketSize should be 0x02 or more as per USB488 Table 22 */ + if (iface_desc->desc.bInterfaceProtocol == 1 && + data->iin_wMaxPacketSize < 2) { + retcode = -EINVAL; + goto err_put; + } dev_dbg(&intf->dev, "Found Int in endpoint at %u\n", data->iin_ep); } From 9ddb9c0deca48d2c2a22ebf4d2f35c925a520328 Mon Sep 17 00:00:00 2001 From: "Stephen J. Fuhry" Date: Wed, 13 May 2026 13:14:19 -0400 Subject: [PATCH 121/337] USB: quirks: add NO_LPM for Lenovo ThinkPad USB-C Dock Gen2 hub controllers The Lenovo ThinkPad USB-C Dock Gen2 (17ef:a391, 17ef:a392) hub controllers exhibit link instability when USB Link Power Management is enabled, similar to the dock's Ethernet adapter (17ef:a387) which already carries USB_QUIRK_NO_LPM. When the dock reconnects after a transient disconnect, the hub controllers enter LPM states between re-enumeration retries, causing repeated disconnect/reconnect cycles lasting up to two minutes. Disabling LPM for these devices restores stable enumeration. Signed-off-by: Stephen J. Fuhry Cc: stable Link: https://patch.msgid.link/20260513171419.44849-1-fuhrysteve@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/quirks.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index 0ffdaefba508..87810eff974e 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -513,6 +513,10 @@ static const struct usb_device_id usb_quirk_list[] = { /* Lenovo ThinkPad USB-C Dock Gen2 Ethernet (RTL8153 GigE) */ { USB_DEVICE(0x17ef, 0xa387), .driver_info = USB_QUIRK_NO_LPM }, + /* Lenovo ThinkPad USB-C Dock Gen2 USB 3.1 and USB 2.0 hub controllers */ + { USB_DEVICE(0x17ef, 0xa391), .driver_info = USB_QUIRK_NO_LPM }, + { USB_DEVICE(0x17ef, 0xa392), .driver_info = USB_QUIRK_NO_LPM }, + /* BUILDWIN Photo Frame */ { USB_DEVICE(0x1908, 0x1315), .driver_info = USB_QUIRK_HONOR_BNUMINTERFACES }, From b53ebb811e00be50a779ce4e7aee604178b4a825 Mon Sep 17 00:00:00 2001 From: Sam Burkels Date: Fri, 1 May 2026 15:23:46 +0200 Subject: [PATCH 122/337] usb: storage: Add quirks for PNY Elite Portable SSD The PNY Elite Portable SSD (USB ID 154b:f009) is a sibling of the already-quirked PNY Pro Elite SSDs (154b:f00b and 154b:f00d). Like its siblings, it uses a Phison-based USB-SATA bridge that exhibits firmware bugs when bound to the uas driver. Without quirks, the device fails to complete READ CAPACITY commands when accessed over UAS on a SuperSpeed (USB 3) port. The device enumerates and reports as a SCSI direct-access device, but reports zero logical blocks and never finishes spin-up: usb 2-3: new SuperSpeed USB device number 8 using xhci_hcd usb 2-3: New USB device found, idVendor=154b, idProduct=f009 usb 2-3: Product: PNY ELITE PSSD usb 2-3: Manufacturer: PNY scsi host0: uas scsi 0:0:0:0: Direct-Access PNY PNY ELITE PSSD 0 sd 0:0:0:0: [sda] Spinning up disk... [...10+ seconds of polling, no progress...] sd 0:0:0:0: [sda] Read Capacity(16) failed: hostbyte=DID_ERROR sd 0:0:0:0: [sda] Read Capacity(10) failed: hostbyte=DID_ERROR sd 0:0:0:0: [sda] 0 512-byte logical blocks: (0 B/0 B) Tested each individual quirk to find the minimum that fixes this: - US_FL_NO_ATA_1X alone: device hangs on spin-up - US_FL_NO_REPORT_OPCODES alone: works on USB 2.0, hangs on USB 3.0 - US_FL_NO_ATA_1X | US_FL_NO_REPORT_OPCODES: works on both With both quirks the device enumerates correctly while still using the uas driver, and delivers full UAS throughput (~281 MB/s sequential read on a USB 3.0 Gen 1 port). The existing PNY Pro Elite entries (f00b, f00d) only set NO_ATA_1X, but this device additionally chokes on REPORT OPCODES under SuperSpeed. Signed-off-by: Sam Burkels Acked-by: Oliver Neukum Cc: stable Link: https://patch.msgid.link/20260501132346.86572-1-sam@1a38.nl Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/unusual_uas.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/usb/storage/unusual_uas.h b/drivers/usb/storage/unusual_uas.h index 939a98c2d3f7..d6f86d5db3bf 100644 --- a/drivers/usb/storage/unusual_uas.h +++ b/drivers/usb/storage/unusual_uas.h @@ -132,6 +132,13 @@ UNUSUAL_DEV(0x152d, 0x0583, 0x0000, 0x9999, USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_NO_REPORT_OPCODES), +/* Reported-by: Sam Burkels */ +UNUSUAL_DEV(0x154b, 0xf009, 0x0000, 0x9999, + "PNY", + "PNY ELITE PSSD", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_NO_ATA_1X | US_FL_NO_REPORT_OPCODES), + /* Reported-by: Thinh Nguyen */ UNUSUAL_DEV(0x154b, 0xf00b, 0x0000, 0x9999, "PNY", From 0b5bde9e1c4df3bbf93a01a0e00c05085f3449fd Mon Sep 17 00:00:00 2001 From: "Rob Herring (Arm)" Date: Fri, 8 May 2026 13:25:55 -0500 Subject: [PATCH 123/337] dt-bindings: usb: ti,omap4-musb: Drop duplicate 'usb-phy' property constraints The deprecated 'usb-phy' property is documented already in usb.yaml and doesn't need a type definition here. It just needs constraints on how many entries there are. As this is a host controller, reference usb-hcd.yaml which then references usb.yaml. Fixes: 70fcdc82cf4a ("dt-bindings: usb: ti,omap4-musb: convert to DT schema") Signed-off-by: Rob Herring (Arm) Link: https://patch.msgid.link/20260508182556.1759173-1-robh@kernel.org Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/usb/ti,omap4-musb.yaml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/usb/ti,omap4-musb.yaml b/Documentation/devicetree/bindings/usb/ti,omap4-musb.yaml index a3d15f217658..e1887e490e02 100644 --- a/Documentation/devicetree/bindings/usb/ti,omap4-musb.yaml +++ b/Documentation/devicetree/bindings/usb/ti,omap4-musb.yaml @@ -81,9 +81,7 @@ properties: const: usb2-phy usb-phy: - $ref: /schemas/types.yaml#/definitions/phandle-array - description: Phandle for the PHY device. - deprecated: true + maxItems: 1 ctrl-module: $ref: /schemas/types.yaml#/definitions/phandle @@ -96,6 +94,9 @@ required: - interrupts - interrupt-names +allOf: + - $ref: usb-hcd.yaml# + unevaluatedProperties: false examples: From d96209626a29ea64666be98c30b30ac82e5f1be6 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Fri, 17 Apr 2026 12:35:52 -0400 Subject: [PATCH 124/337] usbip: vudc: Fix use after free bug in vudc_remove due to race condition This patch follows up Zheng Wang's 2023 report of a use-after-free in vudc_remove(). The original thread stalled on Shuah Khan's request for runtime testing of the unplug/unbind path. This patch supplies that testing and keeps Zheng's original fix shape. In vudc_probe(), v_init_timer() binds udc->tr_timer.timer to v_timer(). usbip_sockfd_store() starts the timer via v_start_timer()/v_kick_timer(). vudc_remove() can then free the containing struct vudc while the timer is still pending or executing. KASAN confirms the race on an unpatched x86_64 QEMU guest with CONFIG_KASAN=y, CONFIG_USBIP_VUDC=y, CONFIG_USB_ZERO=y, and a tight loop that repeatedly writes a socket fd to usbip_sockfd, closes the socket pair, and unbinds/rebinds usbip-vudc.0: BUG: KASAN: slab-use-after-free in __run_timer_base.part.0+0x8ba/0x8e0 Write of size 8 at addr ffff888001b80740 by task trigger_and_unb/239 Allocated by task 239: vudc_probe+0x4d/0xaa0 Freed by task 239: kfree+0x18f/0x520 device_release_driver_internal+0x388/0x540 unbind_store+0xd9/0x100 This lands in the timer core rather than v_timer() itself because the embedded timer_list is being walked after its containing struct vudc has already been freed. The underlying lifetime bug is the same one Zheng reported. With v_stop_timer() called from vudc_remove() and the timer deleted synchronously, the same harness completed 5000 bind/unbind iterations with no KASAN report. Fixes: b6a0ca111867 ("usbip: vudc: Add UDC specific ops") Cc: stable Reported-by: Zheng Wang Closes: https://lore.kernel.org/linux-usb/20230317100954.2626573-1-zyytlz.wz@163.com/ Signed-off-by: Michael Bommarito Acked-by: Shuah Khan Link: https://patch.msgid.link/20260417163552.807548-1-michael.bommarito@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/usbip/vudc_dev.c | 1 + drivers/usb/usbip/vudc_transfer.c | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/usb/usbip/vudc_dev.c b/drivers/usb/usbip/vudc_dev.c index 90383107b660..c5f079c5a1ea 100644 --- a/drivers/usb/usbip/vudc_dev.c +++ b/drivers/usb/usbip/vudc_dev.c @@ -632,6 +632,7 @@ void vudc_remove(struct platform_device *pdev) { struct vudc *udc = platform_get_drvdata(pdev); + v_stop_timer(udc); usb_del_gadget_udc(&udc->gadget); cleanup_vudc_hw(udc); kfree(udc); diff --git a/drivers/usb/usbip/vudc_transfer.c b/drivers/usb/usbip/vudc_transfer.c index a4f02ea3e3ef..d4ce85c4c6a2 100644 --- a/drivers/usb/usbip/vudc_transfer.c +++ b/drivers/usb/usbip/vudc_transfer.c @@ -490,7 +490,8 @@ void v_stop_timer(struct vudc *udc) { struct transfer_timer *t = &udc->tr_timer; - /* timer itself will take care of stopping */ + /* Delete the timer synchronously before teardown frees udc. */ dev_dbg(&udc->pdev->dev, "timer stop"); + timer_delete_sync(&t->timer); t->state = VUDC_TR_STOPPED; } From f1ecb0e563595d4ba9a3b8e39ed52a3dc2d8e328 Mon Sep 17 00:00:00 2001 From: Hang Cao Date: Wed, 15 Apr 2026 14:42:38 +0800 Subject: [PATCH 125/337] dt-bindings: usb: Fix EIC7700 USB reset's issue The EIC7700 USB requires a USB PHY reset operation; otherwise, the USB will not work. The reason why the USB driver that was applied can work properly is that the USB PHY has already been reset in ESWIN's U-Boot. However, the proper functioning of the USB driver should not be dependent on the bootloader. Therefore, it is necessary to incorporate the USB PHY reset signal into the DT bindings. This patch does not introduce any backward incompatibility since the dts is not upstream yet. As array of reset operations are used in the driver, no modifications to the USB controller driver are needed. Fixes: c640a4239db5 ("dt-bindings: usb: Add ESWIN EIC7700 USB controller") Cc: stable Signed-off-by: Hang Cao Acked-by: Conor Dooley Link: https://patch.msgid.link/20260415064238.1784-1-caohang@eswincomputing.com Signed-off-by: Greg Kroah-Hartman --- .../devicetree/bindings/usb/eswin,eic7700-usb.yaml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/usb/eswin,eic7700-usb.yaml b/Documentation/devicetree/bindings/usb/eswin,eic7700-usb.yaml index 41c3b1b98991..658260619423 100644 --- a/Documentation/devicetree/bindings/usb/eswin,eic7700-usb.yaml +++ b/Documentation/devicetree/bindings/usb/eswin,eic7700-usb.yaml @@ -41,12 +41,13 @@ properties: - const: usb_en resets: - maxItems: 2 + maxItems: 3 reset-names: items: - const: vaux - const: usb_rst + - const: usb_phy eswin,hsp-sp-csr: description: @@ -85,8 +86,8 @@ examples: interrupt-parent = <&plic>; interrupts = <85>; interrupt-names = "peripheral"; - resets = <&reset 84>, <&hspcrg 2>; - reset-names = "vaux", "usb_rst"; + resets = <&reset 84>, <&hspcrg 2>, <&hspcrg 4>; + reset-names = "vaux", "usb_rst", "usb_phy"; dr_mode = "peripheral"; maximum-speed = "high-speed"; phy_type = "utmi"; From 7d9633528dd40e33964d2dc74a5abbf5c4d116ce Mon Sep 17 00:00:00 2001 From: Seungjin Bae Date: Mon, 18 May 2026 19:43:14 -0400 Subject: [PATCH 126/337] usb: gadget: dummy_hcd: Reject hub port requests for non-existent ports The `dummy_hub_control()` function handles USB hub class requests to the virtual root hub. The `GetPortStatus` case returns -EPIPE for requests with `wIndex != 1`, since the virtual root hub has only a single port. However, the `ClearPortFeature` and `SetPortFeature` cases lack the same check. Fix this by extending the `wIndex != 1` rejection to both cases, matching the existing behavior of `GetPortStatus`. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable Suggested-by: Alan Stern Signed-off-by: Seungjin Bae Reviewed-by: Alan Stern Link: https://patch.msgid.link/20260518234314.1889396-1-eeodqql09@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/dummy_hcd.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c index f094491b1041..f47903461ed5 100644 --- a/drivers/usb/gadget/udc/dummy_hcd.c +++ b/drivers/usb/gadget/udc/dummy_hcd.c @@ -2134,6 +2134,8 @@ static int dummy_hub_control( case ClearHubFeature: break; case ClearPortFeature: + if (wIndex != 1) + goto error; switch (wValue) { case USB_PORT_FEAT_SUSPEND: if (hcd->speed == HCD_USB3) { @@ -2248,6 +2250,8 @@ static int dummy_hub_control( retval = -EPIPE; break; case SetPortFeature: + if (wIndex != 1) + goto error; switch (wValue) { case USB_PORT_FEAT_LINK_STATE: if (hcd->speed != HCD_USB3) { From 4e036c10e7f4df5d951c69cc3697bc8e209c6d02 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Sun, 19 Apr 2026 12:03:59 -0400 Subject: [PATCH 127/337] usb: gadget: f_fs: copy only received bytes on short ep0 read ffs_ep0_read() allocates its control-OUT data buffer with kmalloc() (not kzalloc) at the Length value from the Setup packet, then copies that full len to userspace regardless of how many bytes were actually received: data = kmalloc(len, GFP_KERNEL); ... ret = __ffs_ep0_queue_wait(ffs, data, len); if ((ret > 0) && (copy_to_user(buf, data, len))) ret = -EFAULT; __ffs_ep0_queue_wait() returns req->actual, which on a short control OUT transfer is strictly less than len. The copy_to_user() call still copies len bytes, so on a short OUT the last (len - ret) bytes of the kmalloc() buffer -- uninitialised slab residue -- are delivered to the FunctionFS daemon. Short ep0 OUT completions are specified USB control-transfer behavior and are produced by in-tree UDCs: * dwc2 continues on req->actual < req->length for ep0 DATA OUT (short-not-ok is the only ep0-OUT stall path). * aspeed_udc ends ep0 OUT on rx_len < ep->ep.maxpacket. * renesas_usbf logs "ep0 short packet" and completes the request. * dwc3 stalls on short IN but not on short OUT. A short ep0 OUT is therefore not evidence of a broken UDC; it is a normal condition f_fs has to cope with. The sibling gadgetfs implementation in drivers/usb/gadget/legacy/inode.c already does this correctly via min(len, dev->req->actual) before copy_to_user(). This patch brings f_fs.c to the same safe pattern rather than trimming at a defensive layer. The bug is reached from the FunctionFS device node, which in real deployments is owned by the privileged gadget daemon (adbd, UMS, composite gadget services, etc.); it is not reachable from unprivileged userspace. Linux host stacks normally reject short-wLength control OUTs before they reach the gadget, so reproducing this required a build that bypasses that host-side check. With the bypass in place, a 1-byte payload on a 64-byte Setup produces 63 bytes of non-canary slab residue in the daemon's read buffer. Fix by copying only ret (actually received) bytes to userspace. Fixes: ddf8abd25994 ("USB: f_fs: the FunctionFS driver") Cc: stable Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Michael Bommarito Link: https://patch.msgid.link/20260419160359.1577270-1-michael.bommarito@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_fs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 002c3441bea3..815639506520 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -619,7 +619,7 @@ static ssize_t ffs_ep0_read(struct file *file, char __user *buf, /* unlocks spinlock */ ret = __ffs_ep0_queue_wait(ffs, data, len); - if ((ret > 0) && (copy_to_user(buf, data, len))) + if ((ret > 0) && (copy_to_user(buf, data, ret))) ret = -EFAULT; goto done_mutex; From 2796646f6d892c1eb6818c7ca41fdfa12568e8d1 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Sun, 19 Apr 2026 12:12:27 -0400 Subject: [PATCH 128/337] usb: gadget: f_fs: serialize DMABUF cancel against request completion ffs_epfile_dmabuf_io_complete() calls usb_ep_free_request() on the completed request but leaves priv->req, the back-pointer that ffs_dmabuf_transfer() set on submission, pointing at the freed memory. A later FUNCTIONFS_DMABUF_DETACH ioctl or ffs_epfile_release() on the close path still sees priv->req non-NULL under ffs->eps_lock: if (priv->ep && priv->req) usb_ep_dequeue(priv->ep, priv->req); so usb_ep_dequeue() is called on a freed usb_request. On dummy_hcd the dequeue path only walks a live queue and pointer-compares, so the freed pointer reads without faulting and KASAN requires an explicit check at the FunctionFS call site to surface the use-after-free. On SG-capable in-tree UDCs the dequeue path dereferences the supplied request immediately: * chipidea's ep_dequeue() does container_of(req, struct ci_hw_req, req) and reads hwreq->req.status before acquiring its own lock. * cdnsp's cdnsp_gadget_ep_dequeue() reads request->status first. The narrower option of clearing priv->req via cmpxchg() in the completion does not close the race: the completion runs without eps_lock, so a cancel path holding eps_lock can still observe priv->req non-NULL, race a concurrent completion that clears and frees, and pass the freed pointer to usb_ep_dequeue(). A slightly longer fix that moves the free into the cleanup work is needed. Same class of lifetime race as the recent usbip-vudc timer fix [1]. Take eps_lock in the sole place that mutates priv->req from the callback direction by moving usb_ep_free_request() out of the completion into ffs_dmabuf_cleanup(), the existing work handler scheduled by ffs_dmabuf_signal_done() on ffs->io_completion_wq. Clear priv->req there under eps_lock before freeing, and only clear if priv->req still names our request (a subsequent ffs_dmabuf_transfer() on the same attachment may have queued a new one). This keeps the existing dummy_hcd sync-dequeue invariant: the completion callback is still invoked by the UDC without eps_lock held (dummy_hcd drops its own lock before calling the callback), and the callback now takes no f_fs lock at all. Serialization against the cancel path happens in cleanup, which runs from the workqueue with no f_fs lock held on entry. The priv ref count protects the containing ffs_dmabuf_priv: ffs_dmabuf_transfer() takes a ref via ffs_dmabuf_get(), cleanup drops it via ffs_dmabuf_put(), so priv stays live for the cleanup even after the cancel path's list_del + ffs_dmabuf_put. The ffs_dmabuf_transfer() error path no longer frees usb_req inline: fence->req and fence->ep are set before usb_ep_queue(), so ffs_dmabuf_cleanup() (scheduled by the error-path ffs_dmabuf_signal_done()) owns the free regardless of whether the queue succeeded. Reproduced under KASAN on both detach and close paths against dummy_hcd with an observability hook (kasan_check_byte(priv->req) immediately before usb_ep_dequeue) at the two FunctionFS cancel sites to surface the stale-pointer access; the hook is not part of this patch. The KASAN allocator / free stacks in the captured splats identify the same request: alloc in dummy_alloc_request, free in dummy_timer, fault reached from ffs_epfile_release (close) and from the FUNCTIONFS_DMABUF_DETACH ioctl (detach). With the patch applied, both paths are silent under the same hook. The bug is reached from the FunctionFS device node, which in real deployments is owned by the privileged gadget daemon (adbd, UMS, composite gadget services, etc.); it is not reachable from unprivileged userspace or from a USB host on the cable. FunctionFS mounts default to GLOBAL_ROOT_UID, but the filesystem supports uid=, gid=, and fmode= delegation to a non-root gadget daemon, so on real deployments the attacker may be a less-privileged service rather than root. Fixes: 7b07a2a7ca02 ("usb: gadget: functionfs: Add DMABUF import interface") Link: https://lore.kernel.org/all/20260417163552.807548-1-michael.bommarito@gmail.com/ [1] Cc: stable Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Michael Bommarito Link: https://patch.msgid.link/20260419161227.1587668-1-michael.bommarito@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_fs.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 815639506520..75912ce6ab55 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -150,6 +150,8 @@ struct ffs_dma_fence { struct dma_fence base; struct ffs_dmabuf_priv *priv; struct work_struct work; + struct usb_ep *ep; + struct usb_request *req; }; struct ffs_epfile { @@ -1385,6 +1387,21 @@ static void ffs_dmabuf_cleanup(struct work_struct *work) struct ffs_dmabuf_priv *priv = dma_fence->priv; struct dma_buf_attachment *attach = priv->attach; struct dma_fence *fence = &dma_fence->base; + struct usb_request *req = dma_fence->req; + struct usb_ep *ep = dma_fence->ep; + + /* + * eps_lock pairs with the cancel paths so they cannot pass a freed + * req to usb_ep_dequeue(). Only clear if priv->req still names ours; + * a re-queue on the same attachment may have taken that slot. + */ + spin_lock_irq(&priv->ffs->eps_lock); + if (priv->req == req) + priv->req = NULL; + spin_unlock_irq(&priv->ffs->eps_lock); + + if (ep && req) + usb_ep_free_request(ep, req); ffs_dmabuf_put(attach); dma_fence_put(fence); @@ -1414,8 +1431,8 @@ static void ffs_epfile_dmabuf_io_complete(struct usb_ep *ep, struct usb_request *req) { pr_vdebug("FFS: DMABUF transfer complete, status=%d\n", req->status); + /* req is freed by ffs_dmabuf_cleanup() under eps_lock. */ ffs_dmabuf_signal_done(req->context, req->status); - usb_ep_free_request(ep, req); } static const char *ffs_dmabuf_get_driver_name(struct dma_fence *fence) @@ -1699,6 +1716,10 @@ static int ffs_dmabuf_transfer(struct file *file, usb_req->context = fence; usb_req->complete = ffs_epfile_dmabuf_io_complete; + /* ffs_dmabuf_cleanup() frees usb_req via these two fields. */ + fence->req = usb_req; + fence->ep = ep->ep; + cookie = dma_fence_begin_signalling(); ret = usb_ep_queue(ep->ep, usb_req, GFP_ATOMIC); dma_fence_end_signalling(cookie); @@ -1708,7 +1729,6 @@ static int ffs_dmabuf_transfer(struct file *file, } else { pr_warn("FFS: Failed to queue DMABUF: %d\n", ret); ffs_dmabuf_signal_done(fence, ret); - usb_ep_free_request(ep->ep, usb_req); } spin_unlock_irq(&epfile->ffs->eps_lock); From 8f6aa392653e52a45858cff5c063df550028836b Mon Sep 17 00:00:00 2001 From: Xu Yang Date: Mon, 27 Apr 2026 15:57:55 +0800 Subject: [PATCH 129/337] usb: chipidea: core: convert ci_role_switch to local variable When a system contains multiple USB controllers, the global ci_role_switch variable may be overwritten by subsequent driver initialization code. This can cause issues in the following cases: - The 2nd ci_hdrc_probe() sees ci_role_switch.fwnode as non-NULL even though the "usb-role-switch" property is not present for the controller. - When the ci_hdrc device is unbound and bound again, ci_role_switch fwnode will not be reassigned, and the old value will be used instead. Convert ci_role_switch to a local variable to fix these issues. Fixes: 05559f10ed79 ("usb: chipidea: add role switch class support") Cc: stable Acked-by: Peter Chen Reviewed-by: Frank Li Signed-off-by: Xu Yang Link: https://patch.msgid.link/20260427075755.3611217-1-xu.yang_2@nxp.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/core.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index 7cfabb04a4fb..2ab3db3c1015 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -655,12 +655,6 @@ static enum ci_role ci_get_role(struct ci_hdrc *ci) return role; } -static struct usb_role_switch_desc ci_role_switch = { - .set = ci_usb_role_switch_set, - .get = ci_usb_role_switch_get, - .allow_userspace_control = true, -}; - static int ci_get_platdata(struct device *dev, struct ci_hdrc_platform_data *platdata) { @@ -787,9 +781,6 @@ static int ci_get_platdata(struct device *dev, cable->connected = false; } - if (device_property_read_bool(dev, "usb-role-switch")) - ci_role_switch.fwnode = dev->fwnode; - platdata->pctl = devm_pinctrl_get(dev); if (!IS_ERR(platdata->pctl)) { struct pinctrl_state *p; @@ -1033,6 +1024,7 @@ ATTRIBUTE_GROUPS(ci); static int ci_hdrc_probe(struct platform_device *pdev) { + struct usb_role_switch_desc ci_role_switch = {}; struct device *dev = &pdev->dev; struct ci_hdrc *ci; struct resource *res; @@ -1179,7 +1171,11 @@ static int ci_hdrc_probe(struct platform_device *pdev) } } - if (ci_role_switch.fwnode) { + if (device_property_read_bool(dev, "usb-role-switch")) { + ci_role_switch.set = ci_usb_role_switch_set; + ci_role_switch.get = ci_usb_role_switch_get; + ci_role_switch.allow_userspace_control = true; + ci_role_switch.fwnode = dev_fwnode(dev); ci_role_switch.driver_data = ci; ci->role_switch = usb_role_switch_register(dev, &ci_role_switch); From 9ea06a3fbf9f16e0d98c52cb3b99642be15ec281 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 20 May 2026 08:59:28 +0300 Subject: [PATCH 130/337] usb: dwc2: Fix use after free in debug code We're not allowed to dereference "urb" after calling usb_hcd_giveback_urb() so save the urb->status ahead of time. Fixes: 7359d482eb4d ("staging: HCD files for the DWC2 driver") Cc: stable Signed-off-by: Dan Carpenter Link: https://patch.msgid.link/ag1NwBpqT4IEQcdJ@stanley.mountain Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc2/hcd.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index 1a763ad4f721..2414291aa908 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -4804,6 +4804,7 @@ static int _dwc2_hcd_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd); int rc; unsigned long flags; + int urb_status; dev_dbg(hsotg->dev, "DWC OTG HCD URB Dequeue\n"); dwc2_dump_urb_info(hcd, urb, "urb_dequeue"); @@ -4828,11 +4829,12 @@ static int _dwc2_hcd_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, /* Higher layer software sets URB status */ spin_unlock(&hsotg->lock); + urb_status = urb->status; usb_hcd_giveback_urb(hcd, urb, status); spin_lock(&hsotg->lock); dev_dbg(hsotg->dev, "Called usb_hcd_giveback_urb()\n"); - dev_dbg(hsotg->dev, " urb->status = %d\n", urb->status); + dev_dbg(hsotg->dev, " urb->status = %d\n", urb_status); out: spin_unlock_irqrestore(&hsotg->lock, flags); From 5eb070769ea5e18405535609d1d3f6886f3755bd Mon Sep 17 00:00:00 2001 From: Wentao Guan Date: Fri, 22 May 2026 17:13:58 +0800 Subject: [PATCH 131/337] USB: cdc-acm: Fix bit overlap and move quirk definitions to header The VENDOR_CLASS_DATA_IFACE and ALWAYS_POLL_CTRL quirk flags added in commit f58752ebcb35 ("USB: cdc-acm: Add quirks for Yoga Book 9 14IAH10 INGENIC touchscreen") were placed inside the acm_ctrl_msg() function rather than in the header with the other quirk flags. Then, their values (BIT(9) and BIT(10)) collided with NO_UNION_12 which is already BIT(9). Move the definitions to drivers/usb/class/cdc-acm.h where they belong and shift them to BIT(10) and BIT(11) to avoid the overlap. Fixes: f58752ebcb35 ("USB: cdc-acm: Add quirks for Yoga Book 9 14IAH10 INGENIC touchscreen") Cc: stable Signed-off-by: Wentao Guan Link: https://patch.msgid.link/20260522091357.1301196-1-guanwentao@uniontech.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-acm.c | 2 -- drivers/usb/class/cdc-acm.h | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 54059e4fc6ed..ddf0b5963859 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -114,8 +114,6 @@ static int acm_ctrl_msg(struct acm *acm, int request, int value, int retval; retval = usb_autopm_get_interface(acm->control); -#define VENDOR_CLASS_DATA_IFACE BIT(9) /* data interface uses vendor-specific class */ -#define ALWAYS_POLL_CTRL BIT(10) /* keep ctrl URB active even without an open TTY */ if (retval) return retval; diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h index 25fd5329a878..01f448a783c0 100644 --- a/drivers/usb/class/cdc-acm.h +++ b/drivers/usb/class/cdc-acm.h @@ -115,3 +115,5 @@ struct acm { #define DISABLE_ECHO BIT(7) #define MISSING_CAP_BRK BIT(8) #define NO_UNION_12 BIT(9) +#define VENDOR_CLASS_DATA_IFACE BIT(10) /* data interface uses vendor-specific class */ +#define ALWAYS_POLL_CTRL BIT(11) /* keep ctrl URB active even without an open TTY */ From ea66be25f0e934f49d24cd0c5845d13cdba3520b Mon Sep 17 00:00:00 2001 From: Myeonghun Pak Date: Tue, 12 May 2026 15:56:57 +0900 Subject: [PATCH 132/337] serial: altera_jtaguart: handle uart_add_one_port() failures altera_jtaguart_probe() maps the register window before registering the UART port, but it ignores failures from uart_add_one_port(). If port registration fails, probe still returns success and the mapping remains live until a later remove path that is not part of probe failure cleanup. Return the uart_add_one_port() error and unmap the register window on that failure path. This issue was identified during our ongoing static-analysis research while reviewing kernel code. Fixes: 5bcd601049c6 ("serial: Add driver for the Altera JTAG UART") Cc: stable Co-developed-by: Ijae Kim Signed-off-by: Ijae Kim Signed-off-by: Myeonghun Pak Link: https://patch.msgid.link/20260512065837.79528-1-mhun512@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/altera_jtaguart.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/altera_jtaguart.c b/drivers/tty/serial/altera_jtaguart.c index d47a62d1c9f7..20f079fe11d8 100644 --- a/drivers/tty/serial/altera_jtaguart.c +++ b/drivers/tty/serial/altera_jtaguart.c @@ -379,6 +379,7 @@ static int altera_jtaguart_probe(struct platform_device *pdev) struct resource *res_mem; int i = pdev->id; int irq; + int ret; /* -1 emphasizes that the platform must have one port, no .N suffix */ if (i == -1) @@ -418,7 +419,11 @@ static int altera_jtaguart_probe(struct platform_device *pdev) port->flags = UPF_BOOT_AUTOCONF; port->dev = &pdev->dev; - uart_add_one_port(&altera_jtaguart_driver, port); + ret = uart_add_one_port(&altera_jtaguart_driver, port); + if (ret) { + iounmap(port->membase); + return ret; + } return 0; } From a3bb136bff5e6a5e48cdd813246c9c4686feaaa9 Mon Sep 17 00:00:00 2001 From: Tudor Ambarus Date: Fri, 15 May 2026 12:41:21 +0000 Subject: [PATCH 133/337] tty: serial: samsung: Remove redundant port lock acquisition in rx helpers Sashiko identified a deadlock when the console flow is engaged [1]. When console flow control is enabled (UPF_CONS_FLOW), s3c24xx_serial_stop_tx() calls s3c24xx_serial_rx_enable() and s3c24xx_serial_start_tx() calls s3c24xx_serial_rx_disable(). The serial core framework invokes the .stop_tx() and .start_tx() callbacks with the port->lock spinlock already held. Furthermore, all internal driver paths that invoke stop_tx (such as the DMA TX completion handler s3c24xx_serial_tx_dma_complete() or the PIO TX IRQ handler s3c24xx_serial_tx_irq()) also acquire port->lock prior to calling it. (Note that s3c24xx_serial_start_tx() is only invoked by the serial core). However, s3c24xx_serial_rx_enable() and s3c24xx_serial_rx_disable() unconditionally attempt to acquire port->lock again using uart_port_lock_irqsave(). Since spinlocks are not recursive, this causes a deadlock on the same CPU when console flow control is engaged. Remove the redundant lock acquisition from both rx helper functions. Cc: stable Fixes: b497549a035c ("[ARM] S3C24XX: Split serial driver into core and per-cpu drivers") Reported-by: John Ogness Closes: https://sashiko.dev/#/patchset/20260506121606.5805-1-john.ogness%40linutronix.de [1] Signed-off-by: Tudor Ambarus Link: https://patch.msgid.link/20260515-samsung-tty-flow-control-deadlock-v1-1-93255edbc9bc@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/samsung_tty.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/drivers/tty/serial/samsung_tty.c b/drivers/tty/serial/samsung_tty.c index e27806bf2cf3..17cd5bb100b1 100644 --- a/drivers/tty/serial/samsung_tty.c +++ b/drivers/tty/serial/samsung_tty.c @@ -245,12 +245,9 @@ static bool s3c24xx_serial_txempty_nofifo(const struct uart_port *port) static void s3c24xx_serial_rx_enable(struct uart_port *port) { struct s3c24xx_uart_port *ourport = to_ourport(port); - unsigned long flags; int count = 10000; u32 ucon, ufcon; - uart_port_lock_irqsave(port, &flags); - while (--count && !s3c24xx_serial_txempty_nofifo(port)) udelay(100); @@ -263,23 +260,18 @@ static void s3c24xx_serial_rx_enable(struct uart_port *port) wr_regl(port, S3C2410_UCON, ucon); ourport->rx_enabled = 1; - uart_port_unlock_irqrestore(port, flags); } static void s3c24xx_serial_rx_disable(struct uart_port *port) { struct s3c24xx_uart_port *ourport = to_ourport(port); - unsigned long flags; u32 ucon; - uart_port_lock_irqsave(port, &flags); - ucon = rd_regl(port, S3C2410_UCON); ucon &= ~S3C2410_UCON_RXIRQMODE; wr_regl(port, S3C2410_UCON, ucon); ourport->rx_enabled = 0; - uart_port_unlock_irqrestore(port, flags); } static void s3c24xx_serial_stop_tx(struct uart_port *port) From c3cce2e67bb22a223f5b8ef05db0fcde70994068 Mon Sep 17 00:00:00 2001 From: Jacques Nilo Date: Wed, 13 May 2026 15:30:23 +0200 Subject: [PATCH 134/337] serial: core: introduce guard(uart_port_lock_check_sysrq_irqsave) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit uart_handle_break() and uart_prepare_sysrq_char() (in include/linux/serial_core.h) capture a SysRq character into port->sysrq_ch while the port lock is held and rely on the unlock helper -- uart_unlock_and_check_sysrq_irqrestore() -- to dispatch the captured character to handle_sysrq() on scope exit. The existing guard(uart_port_lock_irqsave) cannot be used by IRQ handlers that process RX, because its destructor calls plain uart_port_unlock_irqrestore() and silently drops port->sysrq_ch. Add a dedicated guard(uart_port_lock_check_sysrq_irqsave) variant whose destructor is the sysrq-aware unlock helper. The lock side is identical to uart_port_lock_irqsave -- only the unlock-time behaviour differs. Callers that may capture SysRq characters must use guard(uart_port_lock_check_sysrq_irqsave); the existing guard(uart_port_lock_irqsave) keeps its current plain-unlock semantics for the many callers that do not process RX. The new macro is placed after the CONFIG_MAGIC_SYSRQ_SERIAL block so both definitions of uart_unlock_and_check_sysrq_irqrestore() (sysrq enabled and disabled) are visible at expansion time. When CONFIG_MAGIC_SYSRQ_SERIAL=n the destructor degenerates to plain uart_port_unlock_irqrestore(), so there is no overhead. No functional change on its own; users are converted in the following patches. Cc: stable@vger.kernel.org Signed-off-by: Jacques Nilo Reviewed-by: Ilpo Järvinen Link: https://patch.msgid.link/3849af4bc55d5d2a424fa850844e94d641b2f8a6.1778675349.git.jnilo@free.fr Signed-off-by: Greg Kroah-Hartman --- include/linux/serial_core.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index 666430b47899..110ad4e2aef9 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -1274,6 +1274,18 @@ static inline void uart_unlock_and_check_sysrq_irqrestore(struct uart_port *port } #endif /* CONFIG_MAGIC_SYSRQ_SERIAL */ +/* + * Variant of guard(uart_port_lock_irqsave) for IRQ handlers that may capture + * a SysRq character via uart_prepare_sysrq_char(). The destructor uses the + * sysrq-aware unlock helper so that a captured port->sysrq_ch is dispatched + * to handle_sysrq() on scope exit. The plain guard variant silently drops + * sysrq_ch and must not be used by callers that process RX. + */ +DEFINE_LOCK_GUARD_1(uart_port_lock_check_sysrq_irqsave, struct uart_port, + uart_port_lock_irqsave(_T->lock, &_T->flags), + uart_unlock_and_check_sysrq_irqrestore(_T->lock, _T->flags), + unsigned long flags); + /* * We do the SysRQ and SAK checking like this... */ From 71f42b2149a1307a97165b409493665579462ea0 Mon Sep 17 00:00:00 2001 From: Jacques Nilo Date: Wed, 13 May 2026 15:30:24 +0200 Subject: [PATCH 135/337] serial: 8250: dispatch SysRq character in serial8250_handle_irq() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit serial8250_handle_irq() captures a SysRq character into port->sysrq_ch inside serial8250_handle_irq_locked() via uart_prepare_sysrq_char() (reached from serial8250_read_char()). Dispatch of that captured character to handle_sysrq() is expected to happen at port-unlock time, through uart_unlock_and_check_sysrq[_irqrestore](). After commit 8324a54f604d ("serial: 8250: Add serial8250_handle_irq_locked()") the function was reduced to a wrapper that takes the port lock via guard(uart_port_lock_irqsave) whose destructor is plain uart_port_unlock_irqrestore(). The sysrq-aware unlock helper is no longer called, so port->sysrq_ch is captured but never dispatched: BREAK + SysRq key is consumed silently. This was the very condition Johan Hovold's 853a9ae29e978 ("serial: 8250: fix handle_irq locking", 2021) introduced uart_unlock_and_check_sysrq_irqrestore() to address. Switch to the new guard(uart_port_lock_check_sysrq_irqsave), whose destructor is the sysrq-aware unlock helper, restoring the pre-split behaviour. Update the Context: comment on serial8250_handle_irq_locked() so future HW-specific 8250 wrappers know to use the same guard or the explicit sysrq-aware unlock. Verified on RTL8196E with CONFIG_MAGIC_SYSRQ_SERIAL=y: BREAK + 'h' on the console UART produces the SysRq help dump in dmesg and the brk counter in /proc/tty/driver/serial increments correctly. Fixes: 8324a54f604d ("serial: 8250: Add serial8250_handle_irq_locked()") Cc: stable@vger.kernel.org Reviewed-by: Ilpo Järvinen Signed-off-by: Jacques Nilo Link: https://patch.msgid.link/52692ae6c3501f7940347cef364ad7fcacaab7e5.1778675349.git.jnilo@free.fr Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_port.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index af78cc02f38e..c66ba714caa5 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -1784,7 +1784,10 @@ static bool handle_rx_dma(struct uart_8250_port *up, unsigned int iir) } /* - * Context: port's lock must be held by the caller. + * Context: port's lock must be held by the caller. The caller must + * release it via guard(uart_port_lock_check_sysrq_irqsave) or + * uart_unlock_and_check_sysrq_irqrestore(), which captures SysRq + * character on unlock. */ void serial8250_handle_irq_locked(struct uart_port *port, unsigned int iir) { @@ -1837,7 +1840,7 @@ int serial8250_handle_irq(struct uart_port *port, unsigned int iir) if (iir & UART_IIR_NO_INT) return 0; - guard(uart_port_lock_irqsave)(port); + guard(uart_port_lock_check_sysrq_irqsave)(port); serial8250_handle_irq_locked(port, iir); return 1; From 2e211723953f7740e54b53f3d3a0d5e351a5e223 Mon Sep 17 00:00:00 2001 From: Jacques Nilo Date: Wed, 13 May 2026 15:30:25 +0200 Subject: [PATCH 136/337] serial: 8250_dw: dispatch SysRq character in dw8250_handle_irq() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit dw8250_handle_irq() calls serial8250_handle_irq_locked() with the port lock held via guard(uart_port_lock_irqsave). The guard destructor is plain uart_port_unlock_irqrestore(), so a SysRq character captured into port->sysrq_ch by uart_prepare_sysrq_char() is dropped without ever being dispatched to handle_sysrq(). This is the same regression pattern as in serial8250_handle_irq(), introduced when 883c5a2bc934 ("serial: 8250_dw: Rework dw8250_handle_irq() locking and IIR handling") moved the function to the guard()-based locking scheme without using the sysrq-aware unlock helper. Switch to guard(uart_port_lock_check_sysrq_irqsave) so that captured sysrq_ch is dispatched on scope exit, matching the fix in serial8250_handle_irq(). Fixes: 883c5a2bc934 ("serial: 8250_dw: Rework dw8250_handle_irq() locking and IIR handling") Cc: stable@vger.kernel.org Reviewed-by: Ilpo Järvinen Signed-off-by: Jacques Nilo Link: https://patch.msgid.link/ed56fcaf4af24e4ed011a7bce206e0182acb761c.1778675349.git.jnilo@free.fr Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_dw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index 94beadb4024d..2af0c4d0ad82 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -427,7 +427,7 @@ static int dw8250_handle_irq(struct uart_port *p) unsigned int quirks = d->pdata->quirks; unsigned int status; - guard(uart_port_lock_irqsave)(p); + guard(uart_port_lock_check_sysrq_irqsave)(p); switch (FIELD_GET(DW_UART_IIR_IID, iir)) { case UART_IIR_NO_INT: From ca904f4b42355287bc5ce8b7550ebe909cda4c2c Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Wed, 6 May 2026 23:42:31 +0100 Subject: [PATCH 137/337] serial: dz: Fix bootconsole message clobbering at chip reset In the DZ interface as implemented by the DC7085 gate array the serial transmitters are double buffered, meaning that at the time a transmitter is ready to accept the next character there is one in the transmit shift register still being sent to the line. Issuing a master clear at this time causes this character to be lost, so wait an extra amount of time sufficient for the transmit shift register to drain at 9600bps, which is the baud rate setting used by the firmware console. Mind the specified 1.4us TRDY recovery time in the course and continue using iob() as the completion barrier, since the platforms involved use a write buffer that can delay and combine writes, and reorder them with respect to reads regardless of the MMIO locations accessed and we still lack a platform-independent handler for that. When called from dz_serial_console_init() this is too early for fsleep() to work and even before lpj has been calculated and therefore the delay is actually not sufficient for the transmitter to drain and is merely a placeholder now. This will be addressed in a follow-up change. Fixes: e6ee512f5a77 ("dz.c: Resource management") Signed-off-by: Maciej W. Rozycki Cc: stable@vger.kernel.org # v2.6.25+ Link: https://patch.msgid.link/alpine.DEB.2.21.2605062259080.46195@angie.orcam.me.uk Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/dz.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/drivers/tty/serial/dz.c b/drivers/tty/serial/dz.c index e53c54353c3e..d14a40e8cc0a 100644 --- a/drivers/tty/serial/dz.c +++ b/drivers/tty/serial/dz.c @@ -542,10 +542,31 @@ static int dz_encode_baud_rate(unsigned int baud) static void dz_reset(struct dz_port *dport) { struct dz_mux *mux = dport->mux; + unsigned short tcr; + int loops = 10000; if (mux->initialised) return; + tcr = dz_in(dport, DZ_TCR); + + /* Do not disturb any ongoing transmissions. */ + if (dz_in(dport, DZ_CSR) & DZ_MSE) { + unsigned short csr, mask; + + mask = tcr; + while ((mask & DZ_LNENB) && loops--) { + csr = dz_in(dport, DZ_CSR); + if (!(csr & DZ_TRDY)) + continue; + mask &= ~(1 << ((csr & DZ_TLINE) >> 8)); + dz_out(dport, DZ_TCR, mask); + iob(); + udelay(2); /* 1.4us TRDY recovery. */ + } + udelay(1200); /* Transmitter drain. */ + } + dz_out(dport, DZ_CSR, DZ_CLR); while (dz_in(dport, DZ_CSR) & DZ_CLR); iob(); From 7f127b2208e5e2b817243cad41fe4211a6d5a7a3 Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Wed, 6 May 2026 23:42:35 +0100 Subject: [PATCH 138/337] serial: dz: Fix bootconsole handover lockup Calling dz_reset() in the course of setting up the serial device causes line parameters to be reset and the transmitter disabled. We've been lucky in that no message is usually produced to the kernel log between this call and the later call to uart_set_options() in the course of console setup done by dz_serial_console_init(), or the system would hang as the console output handler in the firmware tried to access a port the transmitter of which has been disabled and line parameters messed up. This will change with the next change to the driver, so fix dz_reset() such that line parameters are set for 9600n8 console operation as with the system firmware and the transmitter re-enabled after reset. This also means dz_pm() serves no purpose anymore, so drop it. Fixes: e6ee512f5a77 ("dz.c: Resource management") Signed-off-by: Maciej W. Rozycki Cc: stable@vger.kernel.org # v2.6.25+ Link: https://patch.msgid.link/alpine.DEB.2.21.2605062302010.46195@angie.orcam.me.uk Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/dz.c | 36 ++++++++++++------------------------ 1 file changed, 12 insertions(+), 24 deletions(-) diff --git a/drivers/tty/serial/dz.c b/drivers/tty/serial/dz.c index d14a40e8cc0a..03f4fc9248b8 100644 --- a/drivers/tty/serial/dz.c +++ b/drivers/tty/serial/dz.c @@ -571,6 +571,18 @@ static void dz_reset(struct dz_port *dport) while (dz_in(dport, DZ_CSR) & DZ_CLR); iob(); + /* + * Set parameters across all lines such as not to interfere + * with the initial PROM-based console. Otherwise any output + * produced before the console handover would cause the system + * firmware to produce rubbish. + */ + for (int line = 0; line < DZ_NB_PORT; line++) + dz_out(dport, DZ_LPR, DZ_B9600 | DZ_CS8 | line); + + /* Re-enable transmission for the initial PROM-based console. */ + dz_out(dport, DZ_TCR, tcr); + /* Enable scanning. */ dz_out(dport, DZ_CSR, DZ_MSE); @@ -654,26 +666,6 @@ static void dz_set_termios(struct uart_port *uport, struct ktermios *termios, uart_port_unlock_irqrestore(&dport->port, flags); } -/* - * Hack alert! - * Required solely so that the initial PROM-based console - * works undisturbed in parallel with this one. - */ -static void dz_pm(struct uart_port *uport, unsigned int state, - unsigned int oldstate) -{ - struct dz_port *dport = to_dport(uport); - unsigned long flags; - - uart_port_lock_irqsave(&dport->port, &flags); - if (state < 3) - dz_start_tx(&dport->port); - else - dz_stop_tx(&dport->port); - uart_port_unlock_irqrestore(&dport->port, flags); -} - - static const char *dz_type(struct uart_port *uport) { return "DZ"; @@ -769,7 +761,6 @@ static const struct uart_ops dz_ops = { .startup = dz_startup, .shutdown = dz_shutdown, .set_termios = dz_set_termios, - .pm = dz_pm, .type = dz_type, .release_port = dz_release_port, .request_port = dz_request_port, @@ -894,10 +885,7 @@ static int __init dz_console_setup(struct console *co, char *options) if (ret) return ret; - spin_lock_init(&dport->port.lock); /* For dz_pm(). */ - dz_reset(dport); - dz_pm(uport, 0, -1); if (options) uart_parse_options(options, &baud, &parity, &bits, &flow); From 6c05cf72e13314ce9b770b5951695dc5a2152920 Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Wed, 6 May 2026 23:42:39 +0100 Subject: [PATCH 139/337] serial: zs: Fix bootconsole handover lockup Calling zs_reset() in the course of setting up the serial device causes line parameters to be reset and the transmitter disabled. We've been lucky in that no message is usually produced to the kernel log between this call and the later call to uart_set_options() in the course of console setup done by zs_serial_console_init(), or the system would hang as the console output handler in the firmware tried to access a port the transmitter of which has been disabled and line parameters messed up. This will change with the next change to the driver, so fix zs_reset() such that line parameters are set for 9600n8 console operation as with the system firmware and the transmitter re-enabled after reset. This also means zs_pm() serves no purpose anymore, so drop it. Fixes: 8b4a40809e53 ("zs: move to the serial subsystem") Signed-off-by: Maciej W. Rozycki Cc: stable@vger.kernel.org # v2.6.23+ Link: https://patch.msgid.link/alpine.DEB.2.21.2605062308040.46195@angie.orcam.me.uk Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/zs.c | 29 ++++++++--------------------- 1 file changed, 8 insertions(+), 21 deletions(-) diff --git a/drivers/tty/serial/zs.c b/drivers/tty/serial/zs.c index 31e12645f66b..bb10cd8aa7dc 100644 --- a/drivers/tty/serial/zs.c +++ b/drivers/tty/serial/zs.c @@ -105,18 +105,24 @@ struct zs_parms { static struct zs_scc zs_sccs[ZS_NUM_SCCS]; +/* + * Set parameters in WR5, WR12, WR13 such as not to interfere + * with the initial PROM-based console. Otherwise any output + * produced before the console handover would cause the system + * firmware to hang (TxENAB) or produce rubbish (Tx8, B9600). + */ static u8 zs_init_regs[ZS_NUM_REGS] __initdata = { 0, /* write 0 */ PAR_SPEC, /* write 1 */ 0, /* write 2 */ 0, /* write 3 */ X16CLK | SB1, /* write 4 */ - 0, /* write 5 */ + Tx8 | TxENAB, /* write 5 */ 0, 0, 0, /* write 6, 7, 8 */ MIE | DLC | NV, /* write 9 */ NRZ, /* write 10 */ TCBR | RCBR, /* write 11 */ - 0, 0, /* BRG time constant, write 12 + 13 */ + 0x16, 0x00, /* BRG time constant, write 12 + 13 */ BRSRC | BRENABL, /* write 14 */ 0, /* write 15 */ }; @@ -956,23 +962,6 @@ static void zs_set_termios(struct uart_port *uport, struct ktermios *termios, spin_unlock_irqrestore(&scc->zlock, flags); } -/* - * Hack alert! - * Required solely so that the initial PROM-based console - * works undisturbed in parallel with this one. - */ -static void zs_pm(struct uart_port *uport, unsigned int state, - unsigned int oldstate) -{ - struct zs_port *zport = to_zport(uport); - - if (state < 3) - zport->regs[5] |= TxENAB; - else - zport->regs[5] &= ~TxENAB; - write_zsreg(zport, R5, zport->regs[5]); -} - static const char *zs_type(struct uart_port *uport) { @@ -1055,7 +1044,6 @@ static const struct uart_ops zs_ops = { .startup = zs_startup, .shutdown = zs_shutdown, .set_termios = zs_set_termios, - .pm = zs_pm, .type = zs_type, .release_port = zs_release_port, .request_port = zs_request_port, @@ -1210,7 +1198,6 @@ static int __init zs_console_setup(struct console *co, char *options) return ret; zs_reset(zport); - zs_pm(uport, 0, -1); if (options) uart_parse_options(options, &baud, &parity, &bits, &flow); From 8572955630f30948837088aa98bcbe0532d1ceac Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Wed, 6 May 2026 23:42:43 +0100 Subject: [PATCH 140/337] serial: zs: Switch to using channel reset Switch the driver to using the channel reset rather than hardware reset, simplifying handling by removing an interference between channels that causes the other channel to become uninitialised afterwards. There is little difference between the two kinds of reset in terms of register settings that result, and we initialise the whole register set right away anyway. However this prevents a hang from happening should the console output handler in the firmware try to access the other port whose transmitter has been disabled and line parameters messed up. For example this will happen if the keyboard port (port A) is chosen for the system console, unusually but not insanely for a headless system, as the port is wired to a standard DA-15 connector and an adapter can be easily made. Or with the next change in place this would happen for the regular console port (port B), since the keyboard port (port A) will be initialised first. Just remove the unnecessary complication then, a channel reset is good enough. We still need the initialisation marker, now per channel rather than per SCC, as for the console port zs_reset() will be called twice: once early on via zs_serial_console_init() for the console setup only, and then again via zs_config_port() as the port is associated with a TTY device. Fixes: 8b4a40809e53 ("zs: move to the serial subsystem") Signed-off-by: Maciej W. Rozycki Cc: stable@vger.kernel.org # v2.6.23+ Link: https://patch.msgid.link/alpine.DEB.2.21.2605062323430.46195@angie.orcam.me.uk Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/zs.c | 7 ++++--- drivers/tty/serial/zs.h | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/tty/serial/zs.c b/drivers/tty/serial/zs.c index bb10cd8aa7dc..71cab10a33c3 100644 --- a/drivers/tty/serial/zs.c +++ b/drivers/tty/serial/zs.c @@ -832,21 +832,22 @@ static void zs_shutdown(struct uart_port *uport) static void zs_reset(struct zs_port *zport) { + struct zs_port *zport_a = &zport->scc->zport[ZS_CHAN_A]; struct zs_scc *scc = zport->scc; int irq; unsigned long flags; spin_lock_irqsave(&scc->zlock, flags); irq = !irqs_disabled_flags(flags); - if (!scc->initialised) { + if (!zport->initialised) { /* Reset the pointer first, just in case... */ read_zsreg(zport, R0); /* And let the current transmission finish. */ zs_line_drain(zport, irq); - write_zsreg(zport, R9, FHWRES); + write_zsreg(zport, R9, zport == zport_a ? CHRA : CHRB); udelay(10); write_zsreg(zport, R9, 0); - scc->initialised = 1; + zport->initialised = 1; } load_zsregs(zport, zport->regs, irq); spin_unlock_irqrestore(&scc->zlock, flags); diff --git a/drivers/tty/serial/zs.h b/drivers/tty/serial/zs.h index 26ef8eafa1c1..8e51f847bc03 100644 --- a/drivers/tty/serial/zs.h +++ b/drivers/tty/serial/zs.h @@ -22,6 +22,7 @@ struct zs_port { struct zs_scc *scc; /* Containing SCC. */ struct uart_port port; /* Underlying UART. */ + int initialised; /* For the console port. */ int clk_mode; /* May be 1, 16, 32, or 64. */ @@ -41,7 +42,6 @@ struct zs_scc { struct zs_port zport[2]; spinlock_t zlock; atomic_t irq_guard; - int initialised; }; #endif /* __KERNEL__ */ From 5d7a49d60b8fda66da60e240fd7315232fa1754f Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Wed, 6 May 2026 23:42:48 +0100 Subject: [PATCH 141/337] serial: dz: Convert to use a platform device Prevent a crash from happening as the first serial port is initialised: Console: switching to colour frame buffer device 160x64 tgafb: SFB+ detected, rev=0x02 fb0: Digital ZLX-E1 frame buffer device at 0x1e000000 DECstation DZ serial driver version 1.04 CPU 0 Unable to handle kernel paging request at virtual address 000000bc, epc == 8048b3a4, ra == 80470a78 Oops[#1]: CPU: 0 UID: 0 PID: 1 Comm: swapper/0 Not tainted 6.19.0-dirty #35 NONE $ 0 : 00000000 1000ac00 00000004 804707ac $ 4 : 00000000 80e20850 80e20858 81000030 $ 8 : 00000000 8072c81c 00000008 fefefeff $12 : 6c616972 00000006 80c5917f 69726420 $16 : 80e20800 00000000 808f8968 80e20800 $20 : 00000000 807f5a90 808b0094 808d3bc8 $24 : 00000018 80479030 $28 : 80c2e000 80c2fd70 00000069 80470a78 Hi : 00000004 Lo : 00000000 epc : 8048b3a4 __dev_fwnode+0x0/0xc ra : 80470a78 serial_base_ctrl_add+0xa0/0x168 Status: 1000ac04 IEp Cause : 30000008 (ExcCode 02) BadVA : 000000bc PrId : 00000220 (R3000) Modules linked in: Process swapper/0 (pid: 1, threadinfo=(ptrval), task=(ptrval), tls=00000000) Stack : 00400044 00400040 8046f4cc 00000000 808a6148 808a0000 808f8968 8086983c 808e0000 8046fc84 1000ac01 00000028 80e20700 802ba3f8 80e20700 80d34a94 80c1b900 80e20700 80e20700 80e20700 80e20700 80444650 00000000 00000000 00000000 807f5a90 808b0094 80447080 00400040 808e0000 80d34a94 808a6148 80d34a94 00000004 80e20700 00000000 8076974c 80469810 80c2fe3c 1000ac01 ... Call Trace: [<8048b3a4>] __dev_fwnode+0x0/0xc [<80470a78>] serial_base_ctrl_add+0xa0/0x168 [<8046fc84>] serial_core_register_port+0x1c8/0x974 [<808c6af0>] dz_init+0x74/0xc8 [<800470e0>] do_one_initcall+0x44/0x2d4 [<808b111c>] kernel_init_freeable+0x258/0x308 [<8072e434>] kernel_init+0x20/0x114 [<80049cd0>] ret_from_kernel_thread+0x14/0x1c Code: 27bd0018 03e00008 2402ffea <8c8200bc> 03e00008 00000000 27bdffc0 afbe0038 afb30024 ---[ end trace 0000000000000000 ]--- -- where a pointer is dereferenced that has been derived from a null pointer to the port's parent device. Since no device is available with legacy probing and it's not anymore a preferable way to discover devices anyway, switch the driver to using a platform device and use it as the port's parent device. Update resource handling accordingly and only request the actual span of addresses used within the slot, which will have had its resource already requested by generic platform device code. Use platform_driver_probe() not just because the DZ device is fixed with solder on board and not straightforward to remove, but foremost because the associated TTY's major device number is the same as used by the zs driver and the first driver to claim it will prevent the other one from using it. Either one DZ device or some SCC devices will be present in a given system but never both at a time, and therefore we want the major device number to be claimed by the first driver to actually successfully bind to its device and platform_driver_probe() is a way to fulfil that. An unfortunate consequence of the switch to a platform device is we now hand the console over from the bootconsole much later in the bootstrap. The firmware console handler appears good enough though to work so late and in particular with interrupts enabled. Conversely only starting the console port so late lets the reset code fully utilise our delay handlers, so switch from udelay() to fsleep() for transmitter draining so as to avoid busy-waiting for an excessive amount of time. Fixes: 84a9582fd203 ("serial: core: Start managing serial controllers to enable runtime PM") Signed-off-by: Maciej W. Rozycki Cc: stable@vger.kernel.org # needs to use .remove_new for <= 6.10 Link: https://patch.msgid.link/alpine.DEB.2.21.2605062326540.46195@angie.orcam.me.uk Signed-off-by: Greg Kroah-Hartman --- arch/mips/dec/platform.c | 55 ++++++++++++++++++- drivers/tty/serial/dz.c | 116 +++++++++++++++++++-------------------- 2 files changed, 110 insertions(+), 61 deletions(-) diff --git a/arch/mips/dec/platform.c b/arch/mips/dec/platform.c index c4fcb8c58e01..fdecc91ee22a 100644 --- a/arch/mips/dec/platform.c +++ b/arch/mips/dec/platform.c @@ -10,6 +10,13 @@ #include #include +#include + +#include +#include +#include +#include + static struct resource dec_rtc_resources[] = { { .name = "rtc", @@ -30,11 +37,57 @@ static struct platform_device dec_rtc_device = { .num_resources = ARRAY_SIZE(dec_rtc_resources), }; +static struct resource dec_dz_resources[] = { + { .name = "dz", .flags = IORESOURCE_MEM, }, + { .name = "dz", .flags = IORESOURCE_IRQ, }, +}; + +static struct platform_device dec_dz_device = { + .name = "dz", + .id = PLATFORM_DEVID_NONE, + .resource = dec_dz_resources, + .num_resources = ARRAY_SIZE(dec_dz_resources), +}; + +static struct platform_device *dec_dz_devices[] __initdata = { + &dec_dz_device, +}; + static int __init dec_add_devices(void) { + int ret1, ret2; + int num_dz; + int irq, i; + dec_rtc_resources[0].start = RTC_PORT(0); dec_rtc_resources[0].end = RTC_PORT(0) + dec_kn_slot_size - 1; - return platform_device_register(&dec_rtc_device); + + i = 0; + irq = dec_interrupt[DEC_IRQ_DZ11]; + if (IS_ENABLED(CONFIG_32BIT) && irq >= 0) { + resource_size_t base; + + switch (mips_machtype) { + case MACH_DS23100: + case MACH_DS5100: + base = dec_kn_slot_base + KN01_DZ11; + break; + default: + base = dec_kn_slot_base + KN02_DZ11; + break; + } + dec_dz_device.resource[0].start = base; + dec_dz_device.resource[0].end = base + dec_kn_slot_size - 1; + dec_dz_device.resource[1].start = irq; + dec_dz_device.resource[1].end = irq; + i++; + } + num_dz = i; + + ret1 = platform_device_register(&dec_rtc_device); + ret2 = IS_ENABLED(CONFIG_32BIT) ? + platform_add_devices(dec_dz_devices, num_dz) : 0; + return ret1 ? ret1 : ret2; } device_initcall(dec_add_devices); diff --git a/drivers/tty/serial/dz.c b/drivers/tty/serial/dz.c index 03f4fc9248b8..39d93e9c2d15 100644 --- a/drivers/tty/serial/dz.c +++ b/drivers/tty/serial/dz.c @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -48,14 +49,6 @@ #include #include -#include - -#include -#include -#include -#include -#include -#include #include "dz.h" @@ -65,7 +58,9 @@ MODULE_LICENSE("GPL"); static char dz_name[] __initdata = "DECstation DZ serial driver version "; -static char dz_version[] __initdata = "1.04"; +static char dz_version[] __initdata = "1.05"; + +#define DZ_IO_SIZE 0x20 /* IOMEM space size. */ struct dz_port { struct dz_mux *mux; @@ -81,6 +76,7 @@ struct dz_mux { }; static struct dz_mux dz_mux; +static struct uart_driver dz_reg; static inline struct dz_port *to_dport(struct uart_port *uport) { @@ -564,7 +560,7 @@ static void dz_reset(struct dz_port *dport) iob(); udelay(2); /* 1.4us TRDY recovery. */ } - udelay(1200); /* Transmitter drain. */ + fsleep(1200); /* Transmitter drain. */ } dz_out(dport, DZ_CSR, DZ_CLR); @@ -681,14 +677,13 @@ static void dz_release_port(struct uart_port *uport) map_guard = atomic_add_return(-1, &mux->map_guard); if (!map_guard) - release_mem_region(uport->mapbase, dec_kn_slot_size); + release_mem_region(uport->mapbase, DZ_IO_SIZE); } static int dz_map_port(struct uart_port *uport) { if (!uport->membase) - uport->membase = ioremap(uport->mapbase, - dec_kn_slot_size); + uport->membase = ioremap(uport->mapbase, DZ_IO_SIZE); if (!uport->membase) { printk(KERN_ERR "dz: Cannot map MMIO\n"); return -ENOMEM; @@ -704,8 +699,7 @@ static int dz_request_port(struct uart_port *uport) map_guard = atomic_add_return(1, &mux->map_guard); if (map_guard == 1) { - if (!request_mem_region(uport->mapbase, dec_kn_slot_size, - "dz")) { + if (!request_mem_region(uport->mapbase, DZ_IO_SIZE, "dz")) { atomic_add(-1, &mux->map_guard); printk(KERN_ERR "dz: Unable to reserve MMIO resource\n"); @@ -716,7 +710,7 @@ static int dz_request_port(struct uart_port *uport) if (ret) { map_guard = atomic_add_return(-1, &mux->map_guard); if (!map_guard) - release_mem_region(uport->mapbase, dec_kn_slot_size); + release_mem_region(uport->mapbase, DZ_IO_SIZE); return ret; } return 0; @@ -768,20 +762,15 @@ static const struct uart_ops dz_ops = { .verify_port = dz_verify_port, }; -static void __init dz_init_ports(void) +static int __init dz_probe(struct platform_device *pdev) { - static int first = 1; - unsigned long base; + struct resource *mem_resource, *irq_resource; int line; - if (!first) - return; - first = 0; - - if (mips_machtype == MACH_DS23100 || mips_machtype == MACH_DS5100) - base = dec_kn_slot_base + KN01_DZ11; - else - base = dec_kn_slot_base + KN02_DZ11; + mem_resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); + irq_resource = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!mem_resource || !irq_resource) + return -ENODEV; for (line = 0; line < DZ_NB_PORT; line++) { struct dz_port *dport = &dz_mux.dport[line]; @@ -789,14 +778,33 @@ static void __init dz_init_ports(void) dport->mux = &dz_mux; - uport->irq = dec_interrupt[DEC_IRQ_DZ11]; + uport->dev = &pdev->dev; + uport->irq = irq_resource->start; uport->fifosize = 1; uport->iotype = UPIO_MEM; uport->flags = UPF_BOOT_AUTOCONF; uport->ops = &dz_ops; uport->line = line; - uport->mapbase = base; + uport->mapbase = mem_resource->start; uport->has_sysrq = IS_ENABLED(CONFIG_SERIAL_DZ_CONSOLE); + + if (uart_add_one_port(&dz_reg, uport)) + uport->dev = NULL; + } + + return 0; +} + +static void __exit dz_remove(struct platform_device *pdev) +{ + int line; + + for (line = DZ_NB_PORT - 1; line >= 0; line--) { + struct dz_port *dport = &dz_mux.dport[line]; + struct uart_port *uport = &dport->port; + + if (uport->dev) + uart_remove_one_port(&dz_reg, uport); } } @@ -879,21 +887,14 @@ static int __init dz_console_setup(struct console *co, char *options) int bits = 8; int parity = 'n'; int flow = 'n'; - int ret; - - ret = dz_map_port(uport); - if (ret) - return ret; - - dz_reset(dport); + if (!dport->mux) + return -ENODEV; if (options) uart_parse_options(options, &baud, &parity, &bits, &flow); - - return uart_set_options(&dport->port, co, baud, parity, bits, flow); + return uart_set_options(uport, co, baud, parity, bits, flow); } -static struct uart_driver dz_reg; static struct console dz_console = { .name = "ttyS", .write = dz_console_print, @@ -904,18 +905,6 @@ static struct console dz_console = { .data = &dz_reg, }; -static int __init dz_serial_console_init(void) -{ - if (!IOASIC) { - dz_init_ports(); - register_console(&dz_console); - return 0; - } else - return -ENXIO; -} - -console_initcall(dz_serial_console_init); - #define SERIAL_DZ_CONSOLE &dz_console #else #define SERIAL_DZ_CONSOLE NULL @@ -931,25 +920,32 @@ static struct uart_driver dz_reg = { .cons = SERIAL_DZ_CONSOLE, }; +static struct platform_driver dz_driver = { + .remove = __exit_p(dz_remove), + .driver = { .name = "dz" }, +}; + static int __init dz_init(void) { - int ret, i; - - if (IOASIC) - return -ENXIO; + int ret; printk("%s%s\n", dz_name, dz_version); - dz_init_ports(); - ret = uart_register_driver(&dz_reg); if (ret) return ret; + ret = platform_driver_probe(&dz_driver, dz_probe); + if (ret) + uart_unregister_driver(&dz_reg); - for (i = 0; i < DZ_NB_PORT; i++) - uart_add_one_port(&dz_reg, &dz_mux.dport[i].port); + return ret; +} - return 0; +static void __exit dz_exit(void) +{ + platform_driver_unregister(&dz_driver); + uart_unregister_driver(&dz_reg); } module_init(dz_init); +module_exit(dz_exit); From 7cac59d08a73cb866ec51a483a6f3fe0f531947c Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Wed, 6 May 2026 23:42:52 +0100 Subject: [PATCH 142/337] serial: zs: Convert to use a platform device Prevent a crash from happening as the first serial port is initialised: Console: switching to mono frame buffer device 160x64 fb0: PMAG-AA frame buffer device at tc0 DECstation Z85C30 serial driver version 0.10 CPU 0 Unable to handle kernel paging request at virtual address 0000002c, epc == 803ab00c, ra == 803aafe0 Oops[#1]: CPU: 0 PID: 1 Comm: swapper Not tainted 6.4.0-rc3-00031-g84a9582fd203-dirty #57 $ 0 : 00000000 10012c00 803aaeb0 00000000 $ 4 : 80e12f60 80e12f50 80e12f58 81000030 $ 8 : 00000000 805ff37c 00000000 33433538 $12 : 65732030 00000006 80c2915d 6c616972 $16 : 80e12f00 807b7630 00000000 00000000 $20 : 00000004 00000348 000001a0 807623b8 $24 : 00000018 00000000 $28 : 80c24000 80c25d60 8078b148 803aafe0 Hi : 00000000 Lo : 00000000 epc : 803ab00c serial_base_ctrl_add+0x78/0xf4 ra : 803aafe0 serial_base_ctrl_add+0x4c/0xf4 Status: 10012c03 KERNEL EXL IE Cause : 00000008 (ExcCode 02) BadVA : 0000002c PrId : 00000440 (R4400SC) Modules linked in: Process swapper (pid: 1, threadinfo=(ptrval), task=(ptrval), tls=00000000) Stack : 80760000 00000cc0 00400044 00400040 803aa02c 80d61ab8 00000000 807b7630 80760000 807623b8 807b7628 803aa644 80386998 00000000 80e17780 80220f68 80e17780 80d61ab8 80c17d80 80e17780 80e17780 8063c798 80e17780 80383fa0 00000010 80e17780 00000000 80386998 807a0000 00000000 00400040 8038f848 807623b8 80d61ab8 00000004 80e17780 00000000 803a68e4 80c25e2c 803bb884 ... Call Trace: [<803ab00c>] serial_base_ctrl_add+0x78/0xf4 [<803aa644>] serial_core_register_port+0x174/0x69c [<8077e9ac>] zs_init+0xc8/0xfc [<800404d4>] do_one_initcall+0x40/0x2ac [<8076cecc>] kernel_init_freeable+0x1e4/0x270 [<80605bec>] kernel_init+0x20/0x108 [<800431e8>] ret_from_kernel_thread+0x14/0x1c Code: 2442aeb0 ae120024 ae0200d0 <8c67002c> 50e00001 8c670000 3c06806e 3c05806e afb30010 ---[ end trace 0000000000000000 ]--- (report at the offending commit) -- where a pointer is dereferenced that has been derived from a null pointer to the port's parent device. Since no device is available with legacy probing and it's not anymore a preferable way to discover devices anyway, switch the driver to using a platform device and use it as the port's parent device. Update resource handling accordingly and only request the actual span of addresses used within the slot, which will have had its resource already requested by generic platform device code. Use platform_driver_probe() not just because SCC devices are fixed with solder on board and not straightforward to remove, but foremost because the associated TTY's major device number is the same as used by the dz driver and the first driver to claim it will prevent the other one from using it. Either one DZ device or some SCC devices will be present in a given system but never both at a time, and therefore we want the major device number to be claimed by the first driver to actually successfully bind to its device and platform_driver_probe() is a way to fulfil that. An unfortunate consequence of the switch to a platform device is we now hand the console over from the bootconsole much later in the bootstrap. The firmware console handler appears good enough though to work so late and in particular with interrupts enabled. Since there is one way only remaining to reach zs_reset() now, remove the port initialisation marker as no longer needed and go through the channel reset unconditionally. Fixes: 84a9582fd203 ("serial: core: Start managing serial controllers to enable runtime PM") Signed-off-by: Maciej W. Rozycki Cc: stable@vger.kernel.org # needs to use .remove_new for <= 6.10 Link: https://patch.msgid.link/alpine.DEB.2.21.2605062328480.46195@angie.orcam.me.uk Signed-off-by: Greg Kroah-Hartman --- arch/mips/dec/platform.c | 60 ++++++++++++- drivers/tty/serial/zs.c | 184 +++++++++++++++------------------------ drivers/tty/serial/zs.h | 1 - 3 files changed, 125 insertions(+), 120 deletions(-) diff --git a/arch/mips/dec/platform.c b/arch/mips/dec/platform.c index fdecc91ee22a..723ce16cbfc0 100644 --- a/arch/mips/dec/platform.c +++ b/arch/mips/dec/platform.c @@ -13,6 +13,7 @@ #include #include +#include #include #include #include @@ -53,10 +54,37 @@ static struct platform_device *dec_dz_devices[] __initdata = { &dec_dz_device, }; +static struct resource dec_zs_resources[][2] = { + { + { .name = "scc0", .flags = IORESOURCE_MEM, }, + { .name = "scc0", .flags = IORESOURCE_IRQ, }, + }, + { + { .name = "scc1", .flags = IORESOURCE_MEM, }, + { .name = "scc1", .flags = IORESOURCE_IRQ, }, + }, +}; + +static struct platform_device dec_zs_device[] = { + { + .name = "zs", + .id = 0, + .resource = dec_zs_resources[0], + .num_resources = ARRAY_SIZE(dec_zs_resources[0]), + }, + { + .name = "zs", + .id = 1, + .resource = dec_zs_resources[1], + .num_resources = ARRAY_SIZE(dec_zs_resources[1]), + }, +}; + static int __init dec_add_devices(void) { - int ret1, ret2; - int num_dz; + struct platform_device *dec_zs_devices[ARRAY_SIZE(dec_zs_device)]; + int ret1, ret2, ret3; + int num_dz, num_zs; int irq, i; dec_rtc_resources[0].start = RTC_PORT(0); @@ -84,10 +112,36 @@ static int __init dec_add_devices(void) } num_dz = i; + i = 0; + irq = dec_interrupt[DEC_IRQ_SCC0]; + if (irq >= 0) { + resource_size_t base = dec_kn_slot_base + IOASIC_SCC0; + + dec_zs_device[i].resource[0].start = base; + dec_zs_device[i].resource[0].end = base + dec_kn_slot_size - 1; + dec_zs_device[i].resource[1].start = irq; + dec_zs_device[i].resource[1].end = irq; + dec_zs_devices[i] = &dec_zs_device[i]; + i++; + } + irq = dec_interrupt[DEC_IRQ_SCC1]; + if (irq >= 0) { + resource_size_t base = dec_kn_slot_base + IOASIC_SCC1; + + dec_zs_device[i].resource[0].start = base; + dec_zs_device[i].resource[0].end = base + dec_kn_slot_size - 1; + dec_zs_device[i].resource[1].start = irq; + dec_zs_device[i].resource[1].end = irq; + dec_zs_devices[i] = &dec_zs_device[i]; + i++; + } + num_zs = i; + ret1 = platform_device_register(&dec_rtc_device); ret2 = IS_ENABLED(CONFIG_32BIT) ? platform_add_devices(dec_dz_devices, num_dz) : 0; - return ret1 ? ret1 : ret2; + ret3 = platform_add_devices(dec_zs_devices, num_zs); + return ret1 ? ret1 : ret2 ? ret2 : ret3; } device_initcall(dec_add_devices); diff --git a/drivers/tty/serial/zs.c b/drivers/tty/serial/zs.c index 71cab10a33c3..8f92b4129a38 100644 --- a/drivers/tty/serial/zs.c +++ b/drivers/tty/serial/zs.c @@ -56,6 +56,7 @@ #include #include #include +#include #include #include #include @@ -66,10 +67,6 @@ #include -#include -#include -#include - #include "zs.h" @@ -79,7 +76,7 @@ MODULE_LICENSE("GPL"); static char zs_name[] __initdata = "DECstation Z85C30 serial driver version "; -static char zs_version[] __initdata = "0.10"; +static char zs_version[] __initdata = "0.11"; /* * It would be nice to dynamically allocate everything that @@ -98,12 +95,8 @@ static char zs_version[] __initdata = "0.10"; #define to_zport(uport) container_of(uport, struct zs_port, port) -struct zs_parms { - resource_size_t scc[ZS_NUM_SCCS]; - int irq[ZS_NUM_SCCS]; -}; - static struct zs_scc zs_sccs[ZS_NUM_SCCS]; +static struct uart_driver zs_reg; /* * Set parameters in WR5, WR12, WR13 such as not to interfere @@ -839,16 +832,15 @@ static void zs_reset(struct zs_port *zport) spin_lock_irqsave(&scc->zlock, flags); irq = !irqs_disabled_flags(flags); - if (!zport->initialised) { - /* Reset the pointer first, just in case... */ - read_zsreg(zport, R0); - /* And let the current transmission finish. */ - zs_line_drain(zport, irq); - write_zsreg(zport, R9, zport == zport_a ? CHRA : CHRB); - udelay(10); - write_zsreg(zport, R9, 0); - zport->initialised = 1; - } + + /* Reset the pointer first, just in case... */ + read_zsreg(zport, R0); + /* And let the current transmission finish. */ + zs_line_drain(zport, irq); + write_zsreg(zport, R9, zport == zport_a ? CHRA : CHRB); + udelay(10); + write_zsreg(zport, R9, 0); + load_zsregs(zport, zport->regs, irq); spin_unlock_irqrestore(&scc->zlock, flags); } @@ -1055,63 +1047,62 @@ static const struct uart_ops zs_ops = { /* * Initialize Z85C30 port structures. */ -static int __init zs_probe_sccs(void) +static int __init zs_probe(struct platform_device *pdev) { - static int probed; - struct zs_parms zs_parms; - int chip, side, irq; - int n_chips = 0; + struct resource *mem_resource, *irq_resource; + int chip, side; int i; - if (probed) - return 0; + mem_resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); + irq_resource = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!mem_resource || !irq_resource) + return -ENODEV; - irq = dec_interrupt[DEC_IRQ_SCC0]; - if (irq >= 0) { - zs_parms.scc[n_chips] = IOASIC_SCC0; - zs_parms.irq[n_chips] = dec_interrupt[DEC_IRQ_SCC0]; - n_chips++; - } - irq = dec_interrupt[DEC_IRQ_SCC1]; - if (irq >= 0) { - zs_parms.scc[n_chips] = IOASIC_SCC1; - zs_parms.irq[n_chips] = dec_interrupt[DEC_IRQ_SCC1]; - n_chips++; - } - if (!n_chips) - return -ENXIO; + chip = pdev->id; + spin_lock_init(&zs_sccs[chip].zlock); + for (side = 0; side < ZS_NUM_CHAN; side++) { + struct zs_port *zport = &zs_sccs[chip].zport[side]; + struct uart_port *uport = &zport->port; - probed = 1; + zport->scc = &zs_sccs[chip]; + zport->clk_mode = 16; - for (chip = 0; chip < n_chips; chip++) { - spin_lock_init(&zs_sccs[chip].zlock); - for (side = 0; side < ZS_NUM_CHAN; side++) { - struct zs_port *zport = &zs_sccs[chip].zport[side]; - struct uart_port *uport = &zport->port; + uport->dev = &pdev->dev; + uport->has_sysrq = IS_ENABLED(CONFIG_SERIAL_ZS_CONSOLE); + uport->irq = irq_resource->start; + uport->uartclk = ZS_CLOCK; + uport->fifosize = 1; + uport->iotype = UPIO_MEM; + uport->flags = UPF_BOOT_AUTOCONF; + uport->ops = &zs_ops; + uport->line = chip * ZS_NUM_CHAN + side; + uport->mapbase = mem_resource->start + + (side ^ ZS_CHAN_B) * ZS_CHAN_IO_SIZE; - zport->scc = &zs_sccs[chip]; - zport->clk_mode = 16; + for (i = 0; i < ZS_NUM_REGS; i++) + zport->regs[i] = zs_init_regs[i]; - uport->has_sysrq = IS_ENABLED(CONFIG_SERIAL_ZS_CONSOLE); - uport->irq = zs_parms.irq[chip]; - uport->uartclk = ZS_CLOCK; - uport->fifosize = 1; - uport->iotype = UPIO_MEM; - uport->flags = UPF_BOOT_AUTOCONF; - uport->ops = &zs_ops; - uport->line = chip * ZS_NUM_CHAN + side; - uport->mapbase = dec_kn_slot_base + - zs_parms.scc[chip] + - (side ^ ZS_CHAN_B) * ZS_CHAN_IO_SIZE; - - for (i = 0; i < ZS_NUM_REGS; i++) - zport->regs[i] = zs_init_regs[i]; - } + if (uart_add_one_port(&zs_reg, uport)) + uport->dev = NULL; } return 0; } +static void __exit zs_remove(struct platform_device *pdev) +{ + int chip, side; + + chip = pdev->id; + for (side = ZS_NUM_CHAN - 1; side >= 0; side--) { + struct zs_port *zport = &zs_sccs[chip].zport[side]; + struct uart_port *uport = &zport->port; + + if (uport->dev) + uart_remove_one_port(&zs_reg, uport); + } +} + #ifdef CONFIG_SERIAL_ZS_CONSOLE static void zs_console_putchar(struct uart_port *uport, unsigned char ch) @@ -1192,20 +1183,14 @@ static int __init zs_console_setup(struct console *co, char *options) int bits = 8; int parity = 'n'; int flow = 'n'; - int ret; - - ret = zs_map_port(uport); - if (ret) - return ret; - - zs_reset(zport); + if (!zport->scc) + return -ENODEV; if (options) uart_parse_options(options, &baud, &parity, &bits, &flow); return uart_set_options(uport, co, baud, parity, bits, flow); } -static struct uart_driver zs_reg; static struct console zs_console = { .name = "ttyS", .write = zs_console_write, @@ -1216,23 +1201,6 @@ static struct console zs_console = { .data = &zs_reg, }; -/* - * Register console. - */ -static int __init zs_serial_console_init(void) -{ - int ret; - - ret = zs_probe_sccs(); - if (ret) - return ret; - register_console(&zs_console); - - return 0; -} - -console_initcall(zs_serial_console_init); - #define SERIAL_ZS_CONSOLE &zs_console #else #define SERIAL_ZS_CONSOLE NULL @@ -1248,47 +1216,31 @@ static struct uart_driver zs_reg = { .cons = SERIAL_ZS_CONSOLE, }; +static struct platform_driver zs_driver = { + .remove = __exit_p(zs_remove), + .driver = { .name = "zs" }, +}; + /* zs_init inits the driver. */ static int __init zs_init(void) { - int i, ret; + int ret; pr_info("%s%s\n", zs_name, zs_version); - /* Find out how many Z85C30 SCCs we have. */ - ret = zs_probe_sccs(); - if (ret) - return ret; - ret = uart_register_driver(&zs_reg); if (ret) return ret; + ret = platform_driver_probe(&zs_driver, zs_probe); + if (ret) + uart_unregister_driver(&zs_reg); - for (i = 0; i < ZS_NUM_SCCS * ZS_NUM_CHAN; i++) { - struct zs_scc *scc = &zs_sccs[i / ZS_NUM_CHAN]; - struct zs_port *zport = &scc->zport[i % ZS_NUM_CHAN]; - struct uart_port *uport = &zport->port; - - if (zport->scc) - uart_add_one_port(&zs_reg, uport); - } - - return 0; + return ret; } static void __exit zs_exit(void) { - int i; - - for (i = ZS_NUM_SCCS * ZS_NUM_CHAN - 1; i >= 0; i--) { - struct zs_scc *scc = &zs_sccs[i / ZS_NUM_CHAN]; - struct zs_port *zport = &scc->zport[i % ZS_NUM_CHAN]; - struct uart_port *uport = &zport->port; - - if (zport->scc) - uart_remove_one_port(&zs_reg, uport); - } - + platform_driver_unregister(&zs_driver); uart_unregister_driver(&zs_reg); } diff --git a/drivers/tty/serial/zs.h b/drivers/tty/serial/zs.h index 8e51f847bc03..e0d3c189b33f 100644 --- a/drivers/tty/serial/zs.h +++ b/drivers/tty/serial/zs.h @@ -22,7 +22,6 @@ struct zs_port { struct zs_scc *scc; /* Containing SCC. */ struct uart_port port; /* Underlying UART. */ - int initialised; /* For the console port. */ int clk_mode; /* May be 1, 16, 32, or 64. */ From e4240d8845445d58b4b96f7066adfe175a61bd0c Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Wed, 6 May 2026 23:42:56 +0100 Subject: [PATCH 143/337] serial: dz: Enable modular build Enable modular build since the driver now has a proper module_exit() handler. There's nothing specific to DZ hardware to prevent driver unloading and reloading from working. Signed-off-by: Maciej W. Rozycki Link: https://patch.msgid.link/alpine.DEB.2.21.2605062331420.46195@angie.orcam.me.uk Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 9aa61c93d7bc..ec284aceb909 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -334,7 +334,7 @@ config SERIAL_MAX310X Say Y here if you want to support this ICs. config SERIAL_DZ - bool "DECstation DZ serial driver" + tristate "DECstation DZ serial driver" depends on MACH_DECSTATION && 32BIT select SERIAL_CORE default y From 4c19719eb8b8df08c5bec7c499f73ddaea6f09fc Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Tue, 14 Apr 2026 12:02:34 +0000 Subject: [PATCH 144/337] rust_binder: avoid calling pending_oneway_finished() on TF_UPDATE_TXN When an outdated transaction is removed from `oneway_todo` due to `TF_UPDATE_TXN`, its `Allocation` is dropped. The current implementation of `Allocation::drop` calls `pending_oneway_finished()`, assuming the transaction was executed. This leads to premature execution of the next queued one-way transaction. Fix this by taking the `oneway_node` from the `Allocation` of the outdated transaction before it is dropped. This prevents `Allocation::drop` from signaling completion. We do not call `take_oneway_node()` from `Transaction::cancel` because it's actually correct to call `pending_oneway_finished()` on cancel if the transaction did not come from `oneway_todo`. This ensures that if `BINDER_THREAD_EXIT` is invoked and cancels a oneway transaction, then the next transaction is taken from `oneway_todo`. This bug does not lead to any issues in the kernel, but may lead to Binder delivering transactions to userspace earlier than userspace expected to receive them. Cc: stable Fixes: eafedbc7c050 ("rust_binder: add Rust Binder driver") Assisted-by: Antigravity:gemini Signed-off-by: Alice Ryhl Acked-by: Carlos Llamas Link: https://patch.msgid.link/20260414-tf-update-txn-fix-v1-1-d2b83303acc9@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder/allocation.rs | 8 ++++++++ drivers/android/binder/transaction.rs | 11 ++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/drivers/android/binder/allocation.rs b/drivers/android/binder/allocation.rs index 0cab959e4b7e..b7b05e72970a 100644 --- a/drivers/android/binder/allocation.rs +++ b/drivers/android/binder/allocation.rs @@ -157,6 +157,14 @@ pub(crate) fn set_info_target_node(&mut self, target_node: NodeRef) { self.get_or_init_info().target_node = Some(target_node); } + pub(crate) fn take_oneway_node(&mut self) -> Option> { + if let Some(info) = self.allocation_info.as_mut() { + info.oneway_node.take() + } else { + None + } + } + /// Reserve enough space to push at least `num_fds` fds. pub(crate) fn info_add_fd_reserve(&mut self, num_fds: usize) -> Result { self.get_or_init_info() diff --git a/drivers/android/binder/transaction.rs b/drivers/android/binder/transaction.rs index 47d5e4d88b07..1d9b66920a21 100644 --- a/drivers/android/binder/transaction.rs +++ b/drivers/android/binder/transaction.rs @@ -270,7 +270,8 @@ fn drop_outstanding_txn(&self) { /// Not used for replies. pub(crate) fn submit(self: DLArc, info: &mut TransactionInfo) -> BinderResult { // Defined before `process_inner` so that the destructor runs after releasing the lock. - let mut _t_outdated; + let _t_outdated; + let _oneway_node; let oneway = self.flags & TF_ONE_WAY != 0; let process = self.to.clone(); @@ -287,6 +288,14 @@ pub(crate) fn submit(self: DLArc, info: &mut TransactionInfo) -> BinderRes if let Some(t_outdated) = target_node.take_outdated_transaction(&self, &mut process_inner) { + let mut alloc_guard = t_outdated.allocation.lock(); + if let Some(alloc) = (*alloc_guard).as_mut() { + // Take the oneway node to prevent `Allocation::drop` from calling + // `pending_oneway_finished()`, which would be incorrect as this + // transaction is not being submitted. + _oneway_node = alloc.take_oneway_node(); + } + drop(alloc_guard); // Save the transaction to be dropped after locks are released. _t_outdated = t_outdated; } From f6d8fea9e3953151a4adb4f603503dc3dc9c69da Mon Sep 17 00:00:00 2001 From: Matthew Maurer Date: Fri, 3 Apr 2026 18:18:58 +0000 Subject: [PATCH 145/337] rust_binder: Avoid holding lock when dropping delivered_death In 6c37bebd8c926, we switched to looping over the list and dropping each individual node, ostensibly without the lock held in the loop body. If the kernel were using Rust Edition 2024, the comment would be accurate, and the lock would not be held across the drop. However, the kernel is currently using 2021, so tail expression lifetime extension results in the lock being held across the drop. Explicitly binding the expression result to a variable makes the lockguard no longer part of a tail expression, causing the lock to be dropped before entering the loop body. This was detected via `CONFIG_PROVE_LOCKING` identifying an invalid wait context at the drop site. Reported-by: David Stevens Signed-off-by: Matthew Maurer Cc: stable Fixes: 6c37bebd8c92 ("rust_binder: avoid mem::take on delivered_deaths") Reviewed-by: Alice Ryhl Acked-by: Carlos Llamas Link: https://patch.msgid.link/20260403-lockhold-v1-1-c332b56cd8ae@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder/process.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/android/binder/process.rs b/drivers/android/binder/process.rs index 820cbd541435..96b8440ceac6 100644 --- a/drivers/android/binder/process.rs +++ b/drivers/android/binder/process.rs @@ -1402,7 +1402,12 @@ fn deferred_release(self: Arc) { // Clear delivered_deaths list. // // Scope ensures that MutexGuard is dropped while executing the body. - while let Some(delivered_death) = { self.inner.lock().delivered_deaths.pop_front() } { + while let Some(delivered_death) = { + // Explicitly bind to avoid tail expression lifetime extension of the lockguard + // Can be removed when the kernel moves to edition 2024 + let maybe_death = self.inner.lock().delivered_deaths.pop_front(); + maybe_death + } { drop(delivered_death); } From f74c8696f14149d5e43cc28b015326a759c48f00 Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Tue, 5 May 2026 23:02:56 +0800 Subject: [PATCH 146/337] uio: uio_pci_generic_sva: fix double free of devm_kzalloc() memory uio_pci_sva allocates struct uio_pci_sva_dev with devm_kzalloc() in probe(), but then calls kfree(udev) both on the probe() error path (label out_free) and again in remove(). Because devm_kzalloc() allocations are devres-managed and are freed automatically when the device is detached (including after a failing probe() and during driver unbind), the explicit kfree() can lead to a double free. If probe() fails after devm_kzalloc(), the error path frees udev and devres cleanup will free it again when the core unwinds the partially bound device. On normal driver removal, remove() frees udev and devres will free it again when the device is detached. This issue was identified by a static analysis tool I developed and confirmed by manual review. Fix by removing the manual kfree() calls and dropping the now-unused label. Fixes: 3397c3cd859a2 ("uio: Add SVA support for PCI devices via uio_pci_generic_sva.c") Cc: stable Signed-off-by: Guangshuo Li Link: https://patch.msgid.link/20260505150256.614071-1-lgs201920130244@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/uio/uio_pci_generic_sva.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/drivers/uio/uio_pci_generic_sva.c b/drivers/uio/uio_pci_generic_sva.c index 4a46acd994a8..d05ef77f7e32 100644 --- a/drivers/uio/uio_pci_generic_sva.c +++ b/drivers/uio/uio_pci_generic_sva.c @@ -129,15 +129,13 @@ static int probe(struct pci_dev *pdev, const struct pci_device_id *id) ret = devm_uio_register_device(&pdev->dev, &udev->info); if (ret) { dev_err(&pdev->dev, "Failed to register uio device\n"); - goto out_free; + goto out_disable; } pci_set_drvdata(pdev, udev); return 0; -out_free: - kfree(udev); out_disable: pci_disable_device(pdev); @@ -146,11 +144,8 @@ static int probe(struct pci_dev *pdev, const struct pci_device_id *id) static void remove(struct pci_dev *pdev) { - struct uio_pci_sva_dev *udev = pci_get_drvdata(pdev); - pci_release_regions(pdev); pci_disable_device(pdev); - kfree(udev); } static ssize_t pasid_show(struct device *dev, From ef15ccbb3e8640a723c42ad90eaf81d66ae02017 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Tue, 5 May 2026 20:45:12 +0200 Subject: [PATCH 147/337] parport: Fix race between port and client registration The parport subsystem registers port devices before they are fully initialised, resulting in a race condition where client drivers such as lp can attach to ports that are not completely initialised or even being torn down. When the port and client drivers are built as modules and loaded around the same time during boot, this occasionally results in a crash. I was able to make this happen reliably in a VM with a PC-style parallel port by patching parport_pc to fail probing: > --- a/drivers/parport/parport_pc.c > +++ b/drivers/parport/parport_pc.c > @@ -2069,7 +2069,7 @@ static struct parport *__parport_pc_probe_port(unsigned long int base, > if (!p) > goto out3; > > - base_res = request_region(base, 3, p->name); > + base_res = NULL; > if (!base_res) > goto out4; > and then running: while true; do modprobe lp & modprobe parport_pc wait rmmod lp parport_pc done for a few seconds. In the long term I think port registration should be changed to put the call to device_add() inside parport_announce_port(), but since the latter currently cannot fail this will require changing all port drivers. For now, add a flag to indicate whether a port has been "announced" and only try to attach client drivers to ports when the flag is set. Fixes: 6fa45a226897 ("parport: add device-model to parport subsystem") Closes: https://bugs.debian.org/1130365 Closes: https://lore.kernel.org/all/6ba903ad-9897-42bb-8c2d-337385cc3746@molgen.mpg.de/ Cc: stable Signed-off-by: Ben Hutchings Acked-by: Sudip Mukherjee Link: https://patch.msgid.link/afo6uBv68GDevbMD@decadent.org.uk Signed-off-by: Greg Kroah-Hartman --- drivers/parport/share.c | 11 +++++++++-- include/linux/parport.h | 1 + 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/parport/share.c b/drivers/parport/share.c index ba5292828703..eb0977ca1605 100644 --- a/drivers/parport/share.c +++ b/drivers/parport/share.c @@ -214,10 +214,14 @@ static void get_lowlevel_driver(void) static int port_check(struct device *dev, void *dev_drv) { struct parport_driver *drv = dev_drv; + struct parport *port; /* only send ports, do not send other devices connected to bus */ - if (is_parport(dev)) - drv->match_port(to_parport_dev(dev)); + if (is_parport(dev)) { + port = to_parport_dev(dev); + if (test_bit(PARPORT_ANNOUNCED, &port->devflags)) + drv->match_port(port); + } return 0; } @@ -532,6 +536,7 @@ void parport_announce_port(struct parport *port) if (slave) attach_driver_chain(slave); } + set_bit(PARPORT_ANNOUNCED, &port->devflags); mutex_unlock(®istration_lock); } EXPORT_SYMBOL(parport_announce_port); @@ -561,6 +566,8 @@ void parport_remove_port(struct parport *port) mutex_lock(®istration_lock); + clear_bit(PARPORT_ANNOUNCED, &port->devflags); + /* Spread the word. */ detach_driver_chain(port); diff --git a/include/linux/parport.h b/include/linux/parport.h index 464c2ad28039..f64cb0676e3b 100644 --- a/include/linux/parport.h +++ b/include/linux/parport.h @@ -240,6 +240,7 @@ struct parport { unsigned long devflags; #define PARPORT_DEVPROC_REGISTERED 0 +#define PARPORT_ANNOUNCED 1 struct pardevice *proc_device; /* Currently register proc device */ struct list_head full_list; From 2eae90a457baa0048a96ed38ad93090ee38c8b2f Mon Sep 17 00:00:00 2001 From: Hongling Zeng Date: Mon, 18 May 2026 10:29:39 +0800 Subject: [PATCH 148/337] gpib: cb7210: Fix region leak when request_irq fails When request_irq() fails, the region allocated by request_region() is not released. Fix this by adding an error handling path with proper goto labels to release the region. Fixes: e9dc69956d4d ("staging: gpib: Add Computer Boards GPIB driver") Closes: https://lore.kernel.org/oe-kbuild-all/202605160620.ReBOadPX-lkp@intel.com/ Signed-off-by: Hongling Zeng Cc: stable Link: https://patch.msgid.link/20260518022939.16881-1-zenghongling@kylinos.cn Signed-off-by: Greg Kroah-Hartman --- drivers/gpib/cb7210/cb7210.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/gpib/cb7210/cb7210.c b/drivers/gpib/cb7210/cb7210.c index 6dd8637c5964..673b5bfe2e7d 100644 --- a/drivers/gpib/cb7210/cb7210.c +++ b/drivers/gpib/cb7210/cb7210.c @@ -1049,7 +1049,8 @@ static int cb_isa_attach(struct gpib_board *board, const struct gpib_board_confi if (!request_region(config->ibbase, cb7210_iosize, DRV_NAME)) { dev_err(board->gpib_dev, "ioports starting at 0x%x are already in use\n", config->ibbase); - return -EBUSY; + retval = -EBUSY; + goto err_release_region; } nec_priv->iobase = config->ibbase; cb_priv->fifo_iobase = nec7210_iobase(cb_priv); @@ -1062,11 +1063,16 @@ static int cb_isa_attach(struct gpib_board *board, const struct gpib_board_confi // install interrupt handler if (request_irq(config->ibirq, cb7210_interrupt, isr_flags, DRV_NAME, board)) { dev_err(board->gpib_dev, "failed to obtain IRQ %d\n", config->ibirq); - return -EBUSY; + retval = -EBUSY; + goto err_release_region; } cb_priv->irq = config->ibirq; return cb7210_init(cb_priv, board); + +err_release_region: + release_region(nec7210_iobase(cb_priv), cb7210_iosize); + return retval; } static void cb_isa_detach(struct gpib_board *board) From 36770417153644bc88281c7284730ef1d14d8d3c Mon Sep 17 00:00:00 2001 From: Xiaolei Wang Date: Mon, 18 May 2026 15:34:05 +0800 Subject: [PATCH 149/337] misc: rp1: Send IACK on IRQ activate to fix kdump/kexec MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After a kexec/kdump reboot, the macb Ethernet controller fails to receive any packets, causing DHCP to hang indefinitely and the network interface to be unusable despite link being up. The root cause is that RP1's level-triggered MSI-X interrupt sources (such as macb on hwirq 6) may have their internal state machines stuck in the "waiting for IACK" state. This happens because the previous kernel crashed before sending the acknowledgment for a pending level interrupt. In this stuck state, RP1 will not generate new MSI-X writes even though the interrupt source remains asserted. Since no new MSI-X is sent, the GIC never sees a new edge, the chained IRQ handler is never invoked, and the interrupt is permanently lost. Fix this by sending MSIX_CFG_IACK in rp1_irq_activate(). This unconditionally resets the MSI-X state machine back to idle when a child device requests its interrupt. If the interrupt source is still asserted, RP1 will immediately issue a new MSI-X with the freshly configured msg_addr/msg_data, and normal interrupt delivery resumes. Writing IACK when the state machine is already idle (i.e., on a normal cold boot) is harmless — it has no effect. Fixes: 49d63971f963 ("misc: rp1: RaspberryPi RP1 misc driver") Cc: stable Signed-off-by: Xiaolei Wang Link: https://patch.msgid.link/20260518073405.2115003-1-xiaolei.wang@windriver.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/rp1/rp1_pci.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/misc/rp1/rp1_pci.c b/drivers/misc/rp1/rp1_pci.c index d210da84c30a..81685e3f3296 100644 --- a/drivers/misc/rp1/rp1_pci.c +++ b/drivers/misc/rp1/rp1_pci.c @@ -143,6 +143,7 @@ static int rp1_irq_activate(struct irq_domain *d, struct irq_data *irqd, struct rp1_dev *rp1 = d->host_data; msix_cfg_set(rp1, (unsigned int)irqd->hwirq, MSIX_CFG_ENABLE); + msix_cfg_set(rp1, (unsigned int)irqd->hwirq, MSIX_CFG_IACK); return 0; } From e1a9d791fd66ab2431b9e6f6f835823809869047 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 22 May 2026 12:16:21 +0200 Subject: [PATCH 150/337] USB: serial: cypress_m8: fix memory corruption with small endpoint Make sure that the interrupt-out endpoint max packet size is at least eight bytes to avoid user-controlled slab corruption or NULL-pointer dereference should a malicious device report a smaller size. Fixes: 3416eaa1f8f8 ("USB: cypress_m8: Packet format is separate from characteristic size") Cc: stable@vger.kernel.org # 2.6.26 Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/cypress_m8.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c index afff1a0f4298..0b8a4e9d7bc5 100644 --- a/drivers/usb/serial/cypress_m8.c +++ b/drivers/usb/serial/cypress_m8.c @@ -445,6 +445,14 @@ static int cypress_generic_port_probe(struct usb_serial_port *port) return -ENODEV; } + /* + * The buffer must be large enough for the one or two-byte header (and + * following data), but assume anything smaller than eight bytes is + * broken. + */ + if (port->interrupt_out_size < 8) + return -EINVAL; + priv = kzalloc_obj(struct cypress_private); if (!priv) return -ENOMEM; From dab48a7e74e6a394f3aa0461a2b1fb0c7b38fcb8 Mon Sep 17 00:00:00 2001 From: Thomas Fourier Date: Fri, 22 May 2026 10:54:04 +0200 Subject: [PATCH 151/337] Input: ims-pcu - fix usb_free_coherent() size in ims_pcu_buffers_free() The input buffer size is pcu->max_in_size, but pcu->max_out_size is passed to usb_free_coherent(). Change size to match the allocation size. Fixes: 628329d52474 ("Input: add IMS Passenger Control Unit driver") Cc: stable@vger.kernel.org Signed-off-by: Thomas Fourier Link: https://patch.msgid.link/20260522085412.45430-2-fourier.thomas@gmail.com Signed-off-by: Dmitry Torokhov --- drivers/input/misc/ims-pcu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c index 4c022a36dbe8..7a1cb9333f53 100644 --- a/drivers/input/misc/ims-pcu.c +++ b/drivers/input/misc/ims-pcu.c @@ -1624,7 +1624,7 @@ static void ims_pcu_buffers_free(struct ims_pcu *pcu) usb_kill_urb(pcu->urb_in); usb_free_urb(pcu->urb_in); - usb_free_coherent(pcu->udev, pcu->max_out_size, + usb_free_coherent(pcu->udev, pcu->max_in_size, pcu->urb_in_buf, pcu->read_dma); kfree(pcu->urb_out_buf); From 7205b58702273baf21d6ba7992e6ba15852325f7 Mon Sep 17 00:00:00 2001 From: David Jeffery Date: Fri, 15 May 2026 14:09:41 -0400 Subject: [PATCH 152/337] scsi: core: Run queues for all non-SDEV_DEL devices from scsi_run_host_queues While a SCSI host is in a recovery state, scsi_mq_requeue_cmd() will not set the requeue list for a requeued command to be kicked in the future. The expectation is a call to scsi_run_host_queues() will kick all SCSI devices once the recovery state is cleared. However, scsi_run_host_queues() uses shost_for_each_device() which uses scsi_device_get() and so will ignore devices in a partially removed state like SDEV_CANCEL. But these devices may also have requeued requests, leaving their requests stuck from not being kicked and causing the removal process of the device to hang. scsi_run_host_queues() needs to run against more devices than the macro shost_for_each_device() allows. Instead of using the too limiting scsi_device_get() state checks, only ignore devices in SDEV_DEL state or when unable to acquire a reference. Attempt to run the queues for all other devices when scsi_run_host_queues() is called. Fixes: 8b566edbdbfb ("scsi: core: Only kick the requeue list if necessary") Signed-off-by: David Jeffery Reviewed-by: Bart Van Assche Link: https://patch.msgid.link/20260515180941.9698-1-djeffery@redhat.com Signed-off-by: Martin K. Petersen --- drivers/scsi/scsi_lib.c | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 6e8c7a42603e..85eef401925a 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -575,10 +575,33 @@ void scsi_requeue_run_queue(struct work_struct *work) void scsi_run_host_queues(struct Scsi_Host *shost) { - struct scsi_device *sdev; + struct scsi_device *sdev, *prev = NULL; + unsigned long flags; - shost_for_each_device(sdev, shost) + spin_lock_irqsave(shost->host_lock, flags); + __shost_for_each_device(sdev, shost) { + /* + * Only skip devices so deep into removal they will never need + * another kick to their queues. Thus scsi_device_get() cannot + * be used as it would skip devices in SDEV_CANCEL state which + * may need a queue kick. + */ + if (sdev->sdev_state == SDEV_DEL || + !get_device(&sdev->sdev_gendev)) + continue; + spin_unlock_irqrestore(shost->host_lock, flags); + + if (prev) + put_device(&prev->sdev_gendev); scsi_run_queue(sdev->request_queue); + + prev = sdev; + + spin_lock_irqsave(shost->host_lock, flags); + } + spin_unlock_irqrestore(shost->host_lock, flags); + if (prev) + put_device(&prev->sdev_gendev); } static void scsi_uninit_cmd(struct scsi_cmnd *cmd) From adda8a44e1e43aceba058839f56fa1c599f6f99b Mon Sep 17 00:00:00 2001 From: Alexander Perlis Date: Tue, 12 May 2026 18:12:54 -0500 Subject: [PATCH 153/337] scsi: devinfo: Add BLIST_NO_RSOC for Promise VTrak E310f The extremely slow boots reported July 2014 in bug 79901: https://bugzilla.kernel.org/show_bug.cgi?id=79901 for Promise VTrak E610f 3U 16-bay FC RAID enclosure occur also with the Promise VTrak E310f 2U 12-bay FC RAID enclosure. The 2014 patch: https://bugzilla.kernel.org/attachment.cgi?id=144101&action=diff added support for the BLIST_NO_RSOC flag and specified that flag for the Promise VTrak E610f. This current patch simply adds the E310f to that same list. One curiosity is the additional BLIST_SPARSELUN flag. This was also in the 2014 patch for the E610f, and was already in place for *all* Promise devices since 2007 due to commit e0b2e597d5dd ("[SCSI] stex: fix id mapping issue") which added the line: {"Promise", "", NULL, BLIST_SPARSELUN} The 2007 commit message talks of issues with SuperTrak EX (stex) but the added line did not limit itself to that particular device family. The current patch for E310F, like the 2014 patch for E610f, adds BLIST_NO_RSOC while preserving BLIST_SPARSELUN from 2007. Signed-off-by: Alexander Perlis Suggested-by: Nikkos Svoboda Link: https://patch.msgid.link/20260512231254.27530-1-aperlis@math.lsu.edu Signed-off-by: Martin K. Petersen --- drivers/scsi/scsi_devinfo.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/scsi/scsi_devinfo.c b/drivers/scsi/scsi_devinfo.c index 68a992494b12..c6defe1c3152 100644 --- a/drivers/scsi/scsi_devinfo.c +++ b/drivers/scsi/scsi_devinfo.c @@ -218,6 +218,7 @@ static struct { {"PIONEER", "CD-ROM DRM-602X", NULL, BLIST_FORCELUN | BLIST_SINGLELUN}, {"PIONEER", "CD-ROM DRM-604X", NULL, BLIST_FORCELUN | BLIST_SINGLELUN}, {"PIONEER", "CD-ROM DRM-624X", NULL, BLIST_FORCELUN | BLIST_SINGLELUN}, + {"Promise", "VTrak E310f", NULL, BLIST_SPARSELUN | BLIST_NO_RSOC}, {"Promise", "VTrak E610f", NULL, BLIST_SPARSELUN | BLIST_NO_RSOC}, {"Promise", "", NULL, BLIST_SPARSELUN}, {"QEMU", "QEMU CD-ROM", NULL, BLIST_SKIP_VPD_PAGES}, From a4719ae23fb5b1b6229120c7ea4b6143a501a62e Mon Sep 17 00:00:00 2001 From: "Milan P. Gandhi" Date: Thu, 14 May 2026 13:27:54 +0530 Subject: [PATCH 154/337] scsi: megaraid_sas: Fix NULL pointer dereference on firmware duplicate completion Add NULL check for scmd_local in the MPI2_FUNCTION_SCSI_IO_REQUEST case to handle firmware duplicate/stale completions. When firmware sends a duplicate completion for a command that was already processed and returned to the pool, the driver accesses NULL scmd pointer causing a crash. Timeline of the bug: 1. Command completes normally, megasas_return_cmd_fusion() called 2. This sets cmd->scmd = NULL and clears io_request with memset(..., 0, ...) 3. Firmware sends duplicate/stale completion for same SMID (firmware bug) 4. Driver processes reply descriptor again 5. Cleared io_request has Function = 0 (MPI2_FUNCTION_SCSI_IO_REQUEST) 6. Switch statement matches SCSI_IO_REQUEST case by accident 7. Accesses megasas_priv(NULL scmd)->status -> crash at offset 0x228 The offset 0x228 = sizeof(struct scsi_cmnd) 0x220 + offsetof(status) 0x8. This issue was observed on PERC H330 Mini running firmware 25.5.9.0001 after 3+ days of heavy I/O load. Crash signature: BUG: unable to handle kernel NULL pointer dereference at 0x228 RIP: complete_cmd_fusion+0x428 Function: megasas_priv(cmd_fusion->scmd)->status Add defensive check to skip processing when scmd_local is NULL. This handles duplicate completions from firmware and prevents accessing freed command structures. The check protects all scmd_local uses in both the SCSI_IO path and the fallthrough LDIO path. Signed-off-by: Milan P. Gandhi Link: https://patch.msgid.link/agWAgtk6rtHqNWb5@machine1 Signed-off-by: Martin K. Petersen --- drivers/scsi/megaraid/megaraid_sas_fusion.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/scsi/megaraid/megaraid_sas_fusion.c b/drivers/scsi/megaraid/megaraid_sas_fusion.c index 2699e4e09b5b..056cbe50e19e 100644 --- a/drivers/scsi/megaraid/megaraid_sas_fusion.c +++ b/drivers/scsi/megaraid/megaraid_sas_fusion.c @@ -3612,6 +3612,15 @@ complete_cmd_fusion(struct megasas_instance *instance, u32 MSIxIndex, complete(&cmd_fusion->done); break; case MPI2_FUNCTION_SCSI_IO_REQUEST: /*Fast Path IO.*/ + /* + * Firmware can send stale/duplicate completions for + * commands already returned to the pool. scmd_local + * would be NULL for such cases. Skip processing to + * avoid NULL pointer access. + */ + if (!scmd_local) + break; + /* Update load balancing info */ if (fusion->load_balance_info && (megasas_priv(cmd_fusion->scmd)->status & From e4bb73bf3ac11b4a93634660345b9d764a4a80df Mon Sep 17 00:00:00 2001 From: "Ewan D. Milne" Date: Tue, 19 May 2026 16:53:56 -0400 Subject: [PATCH 155/337] scsi: scsi_debug: Add missing newline in scsi_debug_device_reset() A "\n" at the end of the sdev_printk() string appears to have been inadvertently removed. Add it back for correct log message formatting. Fixes: a743b120227a ("scsi: scsi_debug: Stop printing extra function name in debug logs") Assisted-by: Claude:claude-opus-4-6 Signed-off-by: Ewan D. Milne Reviewed-by: Bart Van Assche Reviewed-by: John Garry Link: https://patch.msgid.link/20260519205356.1040855-1-emilne@redhat.com Signed-off-by: Martin K. Petersen --- drivers/scsi/scsi_debug.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c index 1515495fd9ea..040c5e1e713a 100644 --- a/drivers/scsi/scsi_debug.c +++ b/drivers/scsi/scsi_debug.c @@ -6953,7 +6953,7 @@ static int scsi_debug_device_reset(struct scsi_cmnd *SCpnt) ++num_dev_resets; if (SDEBUG_OPT_ALL_NOISE & sdebug_opts) - sdev_printk(KERN_INFO, sdp, "doing device reset"); + sdev_printk(KERN_INFO, sdp, "doing device reset\n"); scsi_debug_stop_all_queued(sdp); if (devip) { From a9a39233ec1fc9f97ea1340a4d09bb7ec2be5153 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Wed, 20 May 2026 09:30:15 -0400 Subject: [PATCH 156/337] scsi: scsi_transport_fc: Widen FPIN pname walker counter to u32 An adjacent Fibre Channel fabric actor that can deliver an FPIN ELS frame to an lpfc or qla2xxx Linux initiator can trigger a non-return in the generic FC transport. This is not a local userspace or IP network path; the attacker must be able to inject fabric traffic, for example as a compromised switch or fabric controller, or as a same-zone N_Port on a fabric that permits source spoofing. The Link-Integrity and Peer-Congestion FPIN walkers used a u8 loop counter against the 32-bit on-wire pname_count field, and did not bound pname_count by the descriptor body already validated by the TLV walker. A pname_count of 256 therefore wraps the counter and keeps the loop condition true indefinitely. Factor the shared pname_list[] walk into one helper, widen the counter to u32, and clamp pname_count against the entries that fit in the descriptor body before iterating. Fixes: 3dcfe0de5a97 ("scsi: fc: Parse FPIN packets and update statistics") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Michael Bommarito Reviewed-by: Christoph Hellwig Reviewed-by: John Garry Link: https://patch.msgid.link/20260520133015.1018937-1-michael.bommarito@gmail.com Signed-off-by: Martin K. Petersen --- drivers/scsi/scsi_transport_fc.c | 77 +++++++++++++++++--------------- 1 file changed, 41 insertions(+), 36 deletions(-) diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c index dce95e361daf..173ed6373f04 100644 --- a/drivers/scsi/scsi_transport_fc.c +++ b/drivers/scsi/scsi_transport_fc.c @@ -737,6 +737,37 @@ fc_cn_stats_update(u16 event_type, struct fc_fpin_stats *stats) } } +static void +fc_fpin_pname_stats_update(struct Scsi_Host *shost, + struct fc_rport *attach_rport, u16 event_type, + u32 desc_len, u32 fixed_len, u32 pname_count, + __be64 *pname_list, + void (*stats_update)(u16 event_type, + struct fc_fpin_stats *stats)) +{ + u32 i; + struct fc_rport *rport; + u64 wwpn; + + if (desc_len < fixed_len) + pname_count = 0; + else + pname_count = min(pname_count, (desc_len - fixed_len) / + sizeof(pname_list[0])); + + for (i = 0; i < pname_count; i++) { + wwpn = be64_to_cpu(pname_list[i]); + rport = fc_find_rport_by_wwpn(shost, wwpn); + if (rport && + (rport->roles & FC_PORT_ROLE_FCP_TARGET || + rport->roles & FC_PORT_ROLE_NVME_TARGET)) { + if (rport == attach_rport) + continue; + stats_update(event_type, &rport->fpin_stats); + } + } +} + /* * fc_fpin_li_stats_update - routine to update Link Integrity * event statistics. @@ -747,13 +778,11 @@ fc_cn_stats_update(u16 event_type, struct fc_fpin_stats *stats) static void fc_fpin_li_stats_update(struct Scsi_Host *shost, struct fc_tlv_desc *tlv) { - u8 i; struct fc_rport *rport = NULL; struct fc_rport *attach_rport = NULL; struct fc_host_attrs *fc_host = shost_to_fc_host(shost); struct fc_fn_li_desc *li_desc = (struct fc_fn_li_desc *)tlv; u16 event_type = be16_to_cpu(li_desc->event_type); - u64 wwpn; rport = fc_find_rport_by_wwpn(shost, be64_to_cpu(li_desc->attached_wwpn)); @@ -764,22 +793,11 @@ fc_fpin_li_stats_update(struct Scsi_Host *shost, struct fc_tlv_desc *tlv) fc_li_stats_update(event_type, &attach_rport->fpin_stats); } - if (be32_to_cpu(li_desc->pname_count) > 0) { - for (i = 0; - i < be32_to_cpu(li_desc->pname_count); - i++) { - wwpn = be64_to_cpu(li_desc->pname_list[i]); - rport = fc_find_rport_by_wwpn(shost, wwpn); - if (rport && - (rport->roles & FC_PORT_ROLE_FCP_TARGET || - rport->roles & FC_PORT_ROLE_NVME_TARGET)) { - if (rport == attach_rport) - continue; - fc_li_stats_update(event_type, - &rport->fpin_stats); - } - } - } + fc_fpin_pname_stats_update(shost, attach_rport, event_type, + be32_to_cpu(li_desc->desc_len), + FC_TLV_DESC_LENGTH_FROM_SZ(*li_desc), + be32_to_cpu(li_desc->pname_count), + li_desc->pname_list, fc_li_stats_update); if (fc_host->port_name == be64_to_cpu(li_desc->attached_wwpn)) fc_li_stats_update(event_type, &fc_host->fpin_stats); @@ -827,13 +845,11 @@ static void fc_fpin_peer_congn_stats_update(struct Scsi_Host *shost, struct fc_tlv_desc *tlv) { - u8 i; struct fc_rport *rport = NULL; struct fc_rport *attach_rport = NULL; struct fc_fn_peer_congn_desc *pc_desc = (struct fc_fn_peer_congn_desc *)tlv; u16 event_type = be16_to_cpu(pc_desc->event_type); - u64 wwpn; rport = fc_find_rport_by_wwpn(shost, be64_to_cpu(pc_desc->attached_wwpn)); @@ -844,22 +860,11 @@ fc_fpin_peer_congn_stats_update(struct Scsi_Host *shost, fc_cn_stats_update(event_type, &attach_rport->fpin_stats); } - if (be32_to_cpu(pc_desc->pname_count) > 0) { - for (i = 0; - i < be32_to_cpu(pc_desc->pname_count); - i++) { - wwpn = be64_to_cpu(pc_desc->pname_list[i]); - rport = fc_find_rport_by_wwpn(shost, wwpn); - if (rport && - (rport->roles & FC_PORT_ROLE_FCP_TARGET || - rport->roles & FC_PORT_ROLE_NVME_TARGET)) { - if (rport == attach_rport) - continue; - fc_cn_stats_update(event_type, - &rport->fpin_stats); - } - } - } + fc_fpin_pname_stats_update(shost, attach_rport, event_type, + be32_to_cpu(pc_desc->desc_len), + FC_TLV_DESC_LENGTH_FROM_SZ(*pc_desc), + be32_to_cpu(pc_desc->pname_count), + pc_desc->pname_list, fc_cn_stats_update); } /* From 9eed1bd59937e6828b00d2f2dfef631d964f3636 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Mon, 18 May 2026 10:43:07 -0400 Subject: [PATCH 157/337] scsi: fcoe: Reject FIP descriptors with zero fip_dlen in CVL walker drivers/scsi/fcoe/fcoe_ctlr.c::fcoe_ctlr_recv_clr_vlink() advanced the descriptor cursor by an attacker-supplied fip_dlen without ever requiring dlen >= sizeof(struct fip_desc) in the default branch. The named descriptor cases (FIP_DT_MAC, FIP_DT_NAME, FIP_DT_VN_ID) checked their per-type minimum lengths, but a FIP_DT_NON_CRITICAL descriptor (fip_dtype >= 128, which the standard requires receivers to silently ignore) skipped that check entirely. An unauthenticated L2 peer on the FCoE control VLAN could hang fcoe_ctlr_recv_work on an fcoe, qedf, or bnx2fc initiator indefinitely by emitting one FIP CVL frame whose single descriptor had fip_dtype == FIP_DT_NON_CRITICAL and fip_dlen == 0: the cursor advanced zero bytes per iteration and the loop condition rlen >= sizeof(*desc) stayed true forever, blocking every subsequent FIP frame on that controller. Tighten the outer dlen guard to also reject dlen < sizeof(struct fip_desc), so a malformed descriptor whose length cannot even cover the descriptor header is rejected before the switch. This is the same lower-bound the named cases already apply and is the minimum scope that closes the loop. Fixes: 97c8389d54b9 ("[SCSI] fcoe, libfcoe: Add support for FIP. FCoE discovery and keep-alive.") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Michael Bommarito Reviewed-by: Hannes Reinecke Link: https://patch.msgid.link/20260518144307.2820961-1-michael.bommarito@gmail.com Signed-off-by: Martin K. Petersen --- drivers/scsi/fcoe/fcoe_ctlr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/fcoe/fcoe_ctlr.c b/drivers/scsi/fcoe/fcoe_ctlr.c index 02cd4410efca..496ddd45f74d 100644 --- a/drivers/scsi/fcoe/fcoe_ctlr.c +++ b/drivers/scsi/fcoe/fcoe_ctlr.c @@ -1385,7 +1385,7 @@ static void fcoe_ctlr_recv_clr_vlink(struct fcoe_ctlr *fip, while (rlen >= sizeof(*desc)) { dlen = desc->fip_dlen * FIP_BPW; - if (dlen > rlen) + if (dlen < sizeof(*desc) || dlen > rlen) goto err; /* Drop CVL if there are duplicate critical descriptors */ if ((desc->fip_dtype < 32) && From 778c2ab142c625a8a8afa570e0f9b7873f445d99 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Sat, 18 Apr 2026 11:49:27 -0400 Subject: [PATCH 158/337] scsi: target: iscsi: Fix CRC overread and double-free in iscsit_handle_text_cmd() Two latent bugs in the Text-phase handler, both present since the original LIO integration in commit e48354ce078c ("iscsi-target: Add iSCSI fabric support for target v4.1"): 1) DataDigest CRC buffer overread (4 bytes past text_in). text_in is kzalloc()'d at ALIGN(payload_length, 4). rx_size is then incremented by ISCSI_CRC_LEN to make room for the received DataDigest in the iovec, but the same (now-bumped) rx_size is passed as the buffer length to iscsit_crc_buf(): if (conn->conn_ops->DataDigest) { ... rx_size += ISCSI_CRC_LEN; } ... if (conn->conn_ops->DataDigest) { data_crc = iscsit_crc_buf(text_in, rx_size, 0, NULL); iscsit_crc_buf() walks rx_size bytes of text_in with crc32c(), so when DataDigest is negotiated it reads 4 bytes past the end of the text_in allocation. KASAN reproduces this directly on the unpatched mainline tree as slab-out-of-bounds in crc32c() called from the Text PDU path. The OOB bytes feed crc32c() and are then compared against the initiator-supplied checksum, so the value does not flow back to the attacker, but the kernel does read past the buffer on every Text PDU with DataDigest=CRC32C. Fix by passing the actual padded payload length (ALIGN(payload_length, 4)) that was used for the kzalloc(). 2) Stale cmd->text_in_ptr re-free (double-free) on ERL>0 bad DataDigest drop. On DataDigest mismatch with ErrorRecoveryLevel > 0 the handler silently drops the PDU and lets the initiator plug the CmdSN gap: kfree(text_in); return 0; cmd->text_in_ptr still points at the freed buffer. The next Text Request on the same ITT re-enters iscsit_setup_text_cmd(), which unconditionally does kfree(cmd->text_in_ptr); cmd->text_in_ptr = NULL; freeing the same pointer a second time. Session teardown via iscsit_release_cmd() has the same shape and hits the same double-free if the connection is dropped before a second Text Request arrives. On an unmodified mainline tree the bug-1 CRC overread fires first on the initial valid Text Request and perturbs the subsequent state, so #4 was isolated by building a kernel with only the bug-1 hunk of this patch applied plus temporary printk() observability around the three relevant kfree() sites. The observability prints are not part of this patch. On that build, a three-PDU Text Request sequence after login produces two back-to-back splats: BUG: KASAN: double-free in iscsit_setup_text_cmd+0x?? BUG: KASAN: double-free in iscsit_release_cmd+0x?? showing the same pointer freed in the ERL>0 drop path and again in iscsit_setup_text_cmd() (next Text Request on the same ITT) and once more in iscsit_release_cmd() (session teardown). On distro kernels with CONFIG_SLAB_FREELIST_HARDENED=y (default) the double-free becomes a remote kernel BUG(); on non-hardened kernels it corrupts the slab freelist. Fix by clearing cmd->text_in_ptr after the kfree() in the ERL>0 drop path. With both hunks applied #4 is directly observable on the stock tree without observability printks; fixing bug-1 alone would mask #4 less, not more, so the hunks are submitted together. Both fixes are one-liners. The Text PDU state machine is unchanged and the wire protocol is unaffected. Fixes: e48354ce078c ("iscsi-target: Add iSCSI fabric support for target v4.1") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Michael Bommarito Tested-by: John Garry Reviewed-by: John Garry Signed-off-by: Martin K. Petersen --- drivers/target/iscsi/iscsi_target.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c index cb832fd523af..62ada3a52210 100644 --- a/drivers/target/iscsi/iscsi_target.c +++ b/drivers/target/iscsi/iscsi_target.c @@ -2295,7 +2295,9 @@ iscsit_handle_text_cmd(struct iscsit_conn *conn, struct iscsit_cmd *cmd, goto reject; if (conn->conn_ops->DataDigest) { - data_crc = iscsit_crc_buf(text_in, rx_size, 0, NULL); + data_crc = iscsit_crc_buf(text_in, + ALIGN(payload_length, 4), + 0, NULL); if (checksum != data_crc) { pr_err("Text data CRC32C DataDigest" " 0x%08x does not match computed" @@ -2314,6 +2316,7 @@ iscsit_handle_text_cmd(struct iscsit_conn *conn, struct iscsit_cmd *cmd, " Command CmdSN: 0x%08x due to" " DataCRC error.\n", hdr->cmdsn); kfree(text_in); + cmd->text_in_ptr = NULL; return 0; } } else { From bf33e01f88388c43e285492a63e539df6ffed64c Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Mon, 11 May 2026 14:49:14 -0400 Subject: [PATCH 159/337] scsi: target: iscsi: Bound iscsi_encode_text_output() appends to rsp_buf iscsi_encode_text_output() concatenates "key=value\0" records into login->rsp_buf, an 8192-byte kzalloc(MAX_KEY_VALUE_PAIRS) buffer allocated in iscsit_alloc_login_setup_buffer(). The three sprintf() call sites in this function (lines 1398, 1411, 1424 in v7.1-rc2) never check the remaining buffer capacity: *length += sprintf(output_buf, "%s=%s", er->key, er->value); *length += 1; output_buf = textbuf + *length; The 8192-byte ceiling at iscsi_target_check_login_request() bounds the *input* Login PDU payload, but a single PDU can carry up to 2048 minimal four-byte "a=b\0" pairs, each unknown key expanding to a 16-byte "a=NotUnderstood\0" output record via iscsi_add_notunderstood_response(). 2048 * 16 = 32 KiB of output into an 8 KiB buffer, producing a ~24 KiB heap overrun in the kmalloc-8k slab. The fix introduces a static iscsi_encode_text_record() helper that uses snprintf() with a per-call bounds check against the remaining buffer, and threads a u32 textbuf_size parameter through iscsi_encode_text_output(). Both call sites in iscsi_target_handle_csg_zero() (PHASE_SECURITY) and iscsi_target_handle_csg_one() (PHASE_OPERATIONAL) pass MAX_KEY_VALUE_PAIRS. On overflow the encoder logs the condition, calls iscsi_release_extra_responses() to drop queued records, and returns -1; both caller sites now emit ISCSI_STATUS_CLS_INITIATOR_ERR / ISCSI_LOGIN_STATUS_INIT_ERR via iscsit_tx_login_rsp() before returning, so the initiator sees an explicit failed-login response rather than a silent connection drop. (Prior to this patch only the PHASE_OPERATIONAL caller did that; the PHASE_SECURITY caller is converted to the same shape.) Fixes: e48354ce078c ("iscsi-target: Add iSCSI fabric support for target v4.1") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Michael Bommarito Tested-by: John Garry Reviewed-by: John Garry Signed-off-by: Martin K. Petersen --- drivers/target/iscsi/iscsi_target_nego.c | 7 ++- .../target/iscsi/iscsi_target_parameters.c | 62 ++++++++++++++----- .../target/iscsi/iscsi_target_parameters.h | 2 +- 3 files changed, 55 insertions(+), 16 deletions(-) diff --git a/drivers/target/iscsi/iscsi_target_nego.c b/drivers/target/iscsi/iscsi_target_nego.c index 832588f21f91..b03ed154ca34 100644 --- a/drivers/target/iscsi/iscsi_target_nego.c +++ b/drivers/target/iscsi/iscsi_target_nego.c @@ -899,10 +899,14 @@ static int iscsi_target_handle_csg_zero( SENDER_TARGET, login->rsp_buf, &login->rsp_length, + MAX_KEY_VALUE_PAIRS, conn->param_list, conn->tpg->tpg_attrib.login_keys_workaround); - if (ret < 0) + if (ret < 0) { + iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR, + ISCSI_LOGIN_STATUS_INIT_ERR); return -1; + } if (!iscsi_check_negotiated_keys(conn->param_list)) { bool auth_required = iscsi_conn_auth_required(conn); @@ -986,6 +990,7 @@ static int iscsi_target_handle_csg_one(struct iscsit_conn *conn, struct iscsi_lo SENDER_TARGET, login->rsp_buf, &login->rsp_length, + MAX_KEY_VALUE_PAIRS, conn->param_list, conn->tpg->tpg_attrib.login_keys_workaround); if (ret < 0) { diff --git a/drivers/target/iscsi/iscsi_target_parameters.c b/drivers/target/iscsi/iscsi_target_parameters.c index 4ed578c7b98d..2b318b13268e 100644 --- a/drivers/target/iscsi/iscsi_target_parameters.c +++ b/drivers/target/iscsi/iscsi_target_parameters.c @@ -1371,19 +1371,42 @@ int iscsi_decode_text_input( return -1; } +/* + * Append "key=value" plus a trailing NUL into @textbuf at *@length. + * Returns 0 on success and advances *@length, or -EMSGSIZE if the + * record (including the NUL) would not fit in the remaining buffer. + */ +static int iscsi_encode_text_record(char *textbuf, u32 *length, + u32 textbuf_size, + const char *key, const char *value) +{ + int n; + u32 avail; + + if (*length >= textbuf_size) + return -EMSGSIZE; + + avail = textbuf_size - *length; + n = snprintf(textbuf + *length, avail, "%s=%s", key, value); + if (n < 0 || (u32)n + 1 > avail) + return -EMSGSIZE; + + *length += n + 1; + return 0; +} + int iscsi_encode_text_output( u8 phase, u8 sender, char *textbuf, u32 *length, + u32 textbuf_size, struct iscsi_param_list *param_list, bool keys_workaround) { - char *output_buf = NULL; struct iscsi_extra_response *er; struct iscsi_param *param; - - output_buf = textbuf + *length; + int ret; if (iscsi_enforce_integrity_rules(phase, param_list) < 0) return -1; @@ -1395,10 +1418,12 @@ int iscsi_encode_text_output( !IS_PSTATE_RESPONSE_SENT(param) && !IS_PSTATE_REPLY_OPTIONAL(param) && (param->phase & phase)) { - *length += sprintf(output_buf, "%s=%s", - param->name, param->value); - *length += 1; - output_buf = textbuf + *length; + ret = iscsi_encode_text_record(textbuf, length, + textbuf_size, + param->name, + param->value); + if (ret < 0) + goto err_overflow; SET_PSTATE_RESPONSE_SENT(param); pr_debug("Sending key: %s=%s\n", param->name, param->value); @@ -1408,10 +1433,12 @@ int iscsi_encode_text_output( !IS_PSTATE_ACCEPTOR(param) && !IS_PSTATE_PROPOSER(param) && (param->phase & phase)) { - *length += sprintf(output_buf, "%s=%s", - param->name, param->value); - *length += 1; - output_buf = textbuf + *length; + ret = iscsi_encode_text_record(textbuf, length, + textbuf_size, + param->name, + param->value); + if (ret < 0) + goto err_overflow; SET_PSTATE_PROPOSER(param); iscsi_check_proposer_for_optional_reply(param, keys_workaround); @@ -1421,14 +1448,21 @@ int iscsi_encode_text_output( } list_for_each_entry(er, ¶m_list->extra_response_list, er_list) { - *length += sprintf(output_buf, "%s=%s", er->key, er->value); - *length += 1; - output_buf = textbuf + *length; + ret = iscsi_encode_text_record(textbuf, length, textbuf_size, + er->key, er->value); + if (ret < 0) + goto err_overflow; pr_debug("Sending key: %s=%s\n", er->key, er->value); } iscsi_release_extra_responses(param_list); return 0; + +err_overflow: + pr_err("iSCSI login response buffer (%u bytes) exhausted, dropping login.\n", + textbuf_size); + iscsi_release_extra_responses(param_list); + return -1; } int iscsi_check_negotiated_keys(struct iscsi_param_list *param_list) diff --git a/drivers/target/iscsi/iscsi_target_parameters.h b/drivers/target/iscsi/iscsi_target_parameters.h index c672a971fcb7..38d2238dfe08 100644 --- a/drivers/target/iscsi/iscsi_target_parameters.h +++ b/drivers/target/iscsi/iscsi_target_parameters.h @@ -43,7 +43,7 @@ extern struct iscsi_param *iscsi_find_param_from_key(char *, struct iscsi_param_ extern int iscsi_extract_key_value(char *, char **, char **); extern int iscsi_update_param_value(struct iscsi_param *, char *); extern int iscsi_decode_text_input(u8, u8, char *, u32, struct iscsit_conn *); -extern int iscsi_encode_text_output(u8, u8, char *, u32 *, +extern int iscsi_encode_text_output(u8, u8, char *, u32 *, u32, struct iscsi_param_list *, bool); extern int iscsi_check_negotiated_keys(struct iscsi_param_list *); extern void iscsi_set_connection_parameters(struct iscsi_conn_ops *, From 85db7391310b1304d2dc8ae3b0b12105a9567147 Mon Sep 17 00:00:00 2001 From: Alexandru Hossu Date: Thu, 21 May 2026 17:11:21 +0200 Subject: [PATCH 160/337] scsi: target: iscsi: Validate CHAP_R length before base64 decode chap_server_compute_hash() allocates client_digest as kzalloc(chap->digest_size) and then, for BASE64-encoded responses, passes chap_r directly to chap_base64_decode() without checking whether the input length could produce more than digest_size bytes of output. chap_base64_decode() writes to the destination unconditionally as long as there is input to consume. With MAX_RESPONSE_LENGTH set to 128 and the "0b" prefix stripped by extract_param(), up to 127 base64 characters can reach the decoder. 127 characters decode to 95 bytes. For SHA-256 (digest_size=32) this overflows client_digest by 63 bytes; for MD5 (digest_size=16) the overflow is 79 bytes. The length check at line 344 fires after the write has already happened. The HEX branch in the same switch statement already validates the length up front. Apply the same approach to the BASE64 branch: strip trailing base64 padding characters, then reject any input whose data length exceeds DIV_ROUND_UP(digest_size * 4, 3) before calling the decoder. Stripping trailing '=' before the comparison handles both padded and unpadded encodings. chap_base64_decode() already returns early on '=', so the full original string is still passed to the decoder unchanged. The mutual CHAP path decodes CHAP_C into initiatorchg_binhex, which is kzalloc(CHAP_CHALLENGE_STR_LEN). extract_param() caps initiatorchg at CHAP_CHALLENGE_STR_LEN characters, so at most CHAP_CHALLENGE_STR_LEN-1 base64 characters reach the decoder. The maximum decoded size, DIV_ROUND_UP((CHAP_CHALLENGE_STR_LEN-1) * 3, 4), is less than CHAP_CHALLENGE_STR_LEN, so no overflow is possible there. A comment is added at the call site to document this. Fixes: 1e5733883421 ("scsi: target: iscsi: Support base64 in CHAP") Cc: stable@vger.kernel.org Signed-off-by: Alexandru Hossu Reviewed-by: David Disseldorp Link: https://patch.msgid.link/20260521151121.808477-1-hossu.alexandru@gmail.com Signed-off-by: Martin K. Petersen --- drivers/target/iscsi/iscsi_target_auth.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/drivers/target/iscsi/iscsi_target_auth.c b/drivers/target/iscsi/iscsi_target_auth.c index c46c69a28e97..a3ad2d244dbe 100644 --- a/drivers/target/iscsi/iscsi_target_auth.c +++ b/drivers/target/iscsi/iscsi_target_auth.c @@ -340,13 +340,22 @@ static int chap_server_compute_hash( goto out; } break; - case BASE64: + case BASE64: { + size_t r_len = strlen(chap_r); + + while (r_len > 0 && chap_r[r_len - 1] == '=') + r_len--; + if (r_len > DIV_ROUND_UP(chap->digest_size * 4, 3)) { + pr_err("Malformed CHAP_R: base64 payload too long\n"); + goto out; + } if (chap_base64_decode(client_digest, chap_r, strlen(chap_r)) != chap->digest_size) { pr_err("Malformed CHAP_R: invalid BASE64\n"); goto out; } break; + } default: pr_err("Could not find CHAP_R\n"); goto out; @@ -473,6 +482,14 @@ static int chap_server_compute_hash( } break; case BASE64: + /* + * No overflow check needed: initiatorchg_binhex is + * CHAP_CHALLENGE_STR_LEN bytes and extract_param() caps + * initiatorchg at CHAP_CHALLENGE_STR_LEN characters, so + * the decoded output is at most DIV_ROUND_UP( + * (CHAP_CHALLENGE_STR_LEN - 1) * 3, 4) bytes, which is + * less than CHAP_CHALLENGE_STR_LEN. + */ initiatorchg_len = chap_base64_decode(initiatorchg_binhex, initiatorchg, strlen(initiatorchg)); From 4085f0dbb1ce2251c9a5938d693de6593f0ab2bd Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 22 May 2026 16:19:50 +0200 Subject: [PATCH 161/337] USB: serial: mxuport: fix memory corruption with small endpoint Make sure that the bulk-out endpoint max packet size is at least eight bytes to avoid user-controlled slab corruption should a malicious device report a smaller size. Fixes: ee467a1f2066 ("USB: serial: add Moxa UPORT 12XX/14XX/16XX driver") Cc: stable@vger.kernel.org # 3.14 Cc: Andrew Lunn Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/mxuport.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/usb/serial/mxuport.c b/drivers/usb/serial/mxuport.c index ad5fdf55a02e..c9b9928c473a 100644 --- a/drivers/usb/serial/mxuport.c +++ b/drivers/usb/serial/mxuport.c @@ -962,6 +962,14 @@ static int mxuport_calc_num_ports(struct usb_serial *serial, */ BUILD_BUG_ON(ARRAY_SIZE(epds->bulk_out) < 16); + /* + * The bulk-out buffers must be large enough for the four-byte header + * (and following data), but assume anything smaller than eight bytes + * is broken. + */ + if (usb_endpoint_maxp(epds->bulk_out[0]) < 8) + return -EINVAL; + for (i = 1; i < num_ports; ++i) epds->bulk_out[i] = epds->bulk_out[0]; From 60df93d30f9bdd27db17c4d80ed80ef718d7226b Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 22 May 2026 16:20:58 +0200 Subject: [PATCH 162/337] USB: serial: omninet: fix memory corruption with small endpoint Make sure that the bulk-out buffers are at least as large as the hardcoded transfer size to avoid user-controlled slab corruption should a malicious device report a smaller endpoint max packet size than expected. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/omninet.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c index aa1e9745f967..b59982ed8b25 100644 --- a/drivers/usb/serial/omninet.c +++ b/drivers/usb/serial/omninet.c @@ -30,6 +30,10 @@ /* This one seems to be a re-branded ZyXEL device */ #define BT_IGNITIONPRO_ID 0x2000 +#define OMNINET_HEADERLEN 4 +#define OMNINET_BULKOUTSIZE 64 +#define OMNINET_PAYLOADSIZE (OMNINET_BULKOUTSIZE - OMNINET_HEADERLEN) + /* function prototypes */ static void omninet_process_read_urb(struct urb *urb); static int omninet_prepare_write_buffer(struct usb_serial_port *port, @@ -54,6 +58,7 @@ static struct usb_serial_driver zyxel_omninet_device = { .description = "ZyXEL - omni.net usb", .id_table = id_table, .num_bulk_out = 2, + .bulk_out_size = OMNINET_BULKOUTSIZE, .calc_num_ports = omninet_calc_num_ports, .port_probe = omninet_port_probe, .port_remove = omninet_port_remove, @@ -130,10 +135,6 @@ static void omninet_port_remove(struct usb_serial_port *port) kfree(od); } -#define OMNINET_HEADERLEN 4 -#define OMNINET_BULKOUTSIZE 64 -#define OMNINET_PAYLOADSIZE (OMNINET_BULKOUTSIZE - OMNINET_HEADERLEN) - static void omninet_process_read_urb(struct urb *urb) { struct usb_serial_port *port = urb->context; From 438061ed1ad85e6743e2dce826671772d81089ec Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 22 May 2026 16:22:18 +0200 Subject: [PATCH 163/337] USB: serial: safe_serial: fix memory corruption with small endpoint Make sure that the bulk-out buffer size is at least eight bytes to avoid user-controlled slab corruption in "safe" mode should a malicious device report a smaller size. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/safe_serial.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/usb/serial/safe_serial.c b/drivers/usb/serial/safe_serial.c index 238b54993446..d267a31dcccf 100644 --- a/drivers/usb/serial/safe_serial.c +++ b/drivers/usb/serial/safe_serial.c @@ -259,6 +259,7 @@ static int safe_prepare_write_buffer(struct usb_serial_port *port, static int safe_startup(struct usb_serial *serial) { struct usb_interface_descriptor *desc; + int bulk_out_size; if (serial->dev->descriptor.bDeviceClass != CDC_DEVICE_CLASS) return -ENODEV; @@ -279,6 +280,16 @@ static int safe_startup(struct usb_serial *serial) default: return -EINVAL; } + + /* + * The bulk-out buffer needs to be large enough for the two-byte + * trailer in safe mode, but assume anything smaller than eight bytes + * is broken. + */ + bulk_out_size = serial->port[0]->bulk_out_size; + if (bulk_out_size > 0 && bulk_out_size < 8) + return -EINVAL; + return 0; } From 9f9bfc80c67f35a275820da7e83a35dface08281 Mon Sep 17 00:00:00 2001 From: Zhang Cen Date: Fri, 22 May 2026 22:54:42 +0800 Subject: [PATCH 164/337] USB: serial: cypress_m8: validate interrupt packet headers cypress_read_int_callback() parses the interrupt-in buffer according to the selected Cypress packet format. Format 1 has a two-byte status/count header and format 2 has a one-byte combined status/count header. The usb-serial core sizes the interrupt-in buffer from the endpoint descriptor's wMaxPacketSize, and successful interrupt transfers can complete short when URB_SHORT_NOT_OK is not set. Check that the completed packet contains the selected header before reading it. Malformed short reports are ignored and the interrupt URB is resubmitted through the existing retry path, preventing out-of-bounds header-byte reads. KASAN report as below: KASAN slab-out-of-bounds in cypress_read_int_callback+0x240/0x7f0 Read of size 1 Call trace: cypress_read_int_callback() (drivers/usb/serial/cypress_m8.c:1009) __usb_hcd_giveback_urb() dummy_timer() Fixes: 3416eaa1f8f8 ("USB: cypress_m8: Packet format is separate from characteristic size") Assisted-by: Codex:gpt-5.5 Signed-off-by: Zhang Cen Fixes: 3416eaa1f8f8 ("USB: cypress_m8: Packet format is separate from characteristic size") Cc: stable@vger.kernel.org # 2.6.26 [ johan: use constants in header length sanity checks ] Signed-off-by: Johan Hovold --- drivers/usb/serial/cypress_m8.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c index 0b8a4e9d7bc5..bcf302e88ca4 100644 --- a/drivers/usb/serial/cypress_m8.c +++ b/drivers/usb/serial/cypress_m8.c @@ -1025,8 +1025,8 @@ static void cypress_read_int_callback(struct urb *urb) char tty_flag = TTY_NORMAL; int bytes = 0; int result; - int i = 0; int status = urb->status; + int i; switch (status) { case 0: /* success */ @@ -1064,22 +1064,32 @@ static void cypress_read_int_callback(struct urb *urb) spin_lock_irqsave(&priv->lock, flags); result = urb->actual_length; + i = 0; switch (priv->pkt_fmt) { default: case packet_format_1: /* This is for the CY7C64013... */ + if (result < 2) + break; priv->current_status = data[0] & 0xF8; bytes = data[1] + 2; i = 2; break; case packet_format_2: /* This is for the CY7C63743... */ + if (result < 1) + break; priv->current_status = data[0] & 0xF8; bytes = (data[0] & 0x07) + 1; i = 1; break; } spin_unlock_irqrestore(&priv->lock, flags); + if (i == 0) { + dev_dbg(dev, "%s - short packet received: %d bytes\n", + __func__, result); + goto continue_read; + } if (result < bytes) { dev_dbg(dev, "%s - wrong packet size - received %d bytes but packet said %d bytes\n", From 13d33b9ef67066c77c84273fac5a1d3fde3533d1 Mon Sep 17 00:00:00 2001 From: Berkant Koc Date: Tue, 19 May 2026 22:08:17 +0200 Subject: [PATCH 165/337] drm/hyperv: validate resolution_count and fix WIN8 fallback A SYNTHVID_RESOLUTION_RESPONSE with resolution_count > 64 walks past the supported_resolution[SYNTHVID_MAX_RESOLUTION_COUNT] array in the parse loop. Bound resolution_count against the array size, folded into the existing zero-check. When the WIN10 resolution probe fails, the caller in hyperv_connect_vsp() left hv->screen_*_max / preferred_* unpopulated, which sets mode_config.max_width / max_height to 0 and makes drm_internal_framebuffer_create() reject every userspace framebuffer with -EINVAL. The pre-WIN10 branch had the same gap for preferred_width / preferred_height. Use a single post-probe fallback guarded by screen_width_max == 0 so both paths converge on the WIN8 defaults. Signed-off-by: Berkant Koc Assisted-by: Claude:claude-opus-4-7 berkoc-pipeline Fixes: 76c56a5affeb ("drm/hyperv: Add DRM driver for hyperv synthetic video device") Cc: stable@vger.kernel.org # 5.14+ Reviewed-by: Michael Kelley Tested-by: Michael Kelley Signed-off-by: Hamza Mahfooz Link: https://patch.msgid.link/6945b22419c7d404b4954a113de2ac9c900dba93.1779542874.git.me@berkoc.com --- drivers/gpu/drm/hyperv/hyperv_drm_proto.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/hyperv/hyperv_drm_proto.c b/drivers/gpu/drm/hyperv/hyperv_drm_proto.c index 051ecc526832..c3d0ff229e3d 100644 --- a/drivers/gpu/drm/hyperv/hyperv_drm_proto.c +++ b/drivers/gpu/drm/hyperv/hyperv_drm_proto.c @@ -391,8 +391,11 @@ static int hyperv_get_supported_resolution(struct hv_device *hdev) return -ETIMEDOUT; } - if (msg->resolution_resp.resolution_count == 0) { - drm_err(dev, "No supported resolutions\n"); + if (msg->resolution_resp.resolution_count == 0 || + msg->resolution_resp.resolution_count > + SYNTHVID_MAX_RESOLUTION_COUNT) { + drm_err(dev, "Invalid resolution count: %d\n", + msg->resolution_resp.resolution_count); return -ENODEV; } @@ -508,9 +511,13 @@ int hyperv_connect_vsp(struct hv_device *hdev) ret = hyperv_get_supported_resolution(hdev); if (ret) drm_err(dev, "Failed to get supported resolution from host, use default\n"); - } else { + } + + if (!hv->screen_width_max) { hv->screen_width_max = SYNTHVID_WIDTH_WIN8; hv->screen_height_max = SYNTHVID_HEIGHT_WIN8; + hv->preferred_width = SYNTHVID_WIDTH_WIN8; + hv->preferred_height = SYNTHVID_HEIGHT_WIN8; } hv->mmio_megabytes = hdev->channel->offermsg.offer.mmio_megabytes; From 7f87763f47a3c22fb50265a00619ef10f2394b18 Mon Sep 17 00:00:00 2001 From: Berkant Koc Date: Sat, 23 May 2026 15:27:47 +0200 Subject: [PATCH 166/337] drm/hyperv: validate VMBus packet size in receive callback hyperv_receive_sub() reads msg->vid_hdr.type and dispatches into one of four message-type branches without knowing how many bytes the host wrote into hv->recv_buf. The completion path then runs memcpy(hv->init_buf, msg, VMBUS_MAX_PACKET_SIZE), so the consumer that wakes on wait_for_completion_timeout() can read up to 16 KiB of residue from a prior message as if it were the response payload. Pass bytes_recvd into hyperv_receive_sub() and reject any packet that does not cover the pipe + synthvid header. A single switch on msg->vid_hdr.type then computes the type-specific payload size: the three completion-driving types (SYNTHVID_VERSION_RESPONSE, SYNTHVID_RESOLUTION_RESPONSE, SYNTHVID_VRAM_LOCATION_ACK) fall through to a shared exit that requires that size before memcpy/complete, while SYNTHVID_FEATURE_CHANGE validates its own payload and returns before reading is_dirt_needed. Unknown types are dropped. SYNTHVID_RESOLUTION_RESPONSE is variable length: the host fills resolution_count entries, not the full SYNTHVID_MAX_RESOLUTION_COUNT array. Validate the fixed prefix first so resolution_count can be read, bound it against the array, then require only the count-sized array, so the shorter responses the host actually sends are accepted. Only run the sub-handler when vmbus_recvpacket() returned success. The memcpy length is bytes_recvd, which is bounded by VMBUS_MAX_PACKET_SIZE only on a successful receive; on -ENOBUFS vmbus_recvpacket() instead reports the required length, which can exceed hv->recv_buf, so copying bytes_recvd would read and write past the 16 KiB buffers. Gating on the success return keeps the copy bounded. The nonzero-return path is itself a malformed-message case and is now logged rather than silently skipped; channel recovery is not attempted. Rejected packets are reported via drm_err_ratelimited() rather than silently dropped, matching the CoCo-hardened pattern in hv_kvp_onchannelcallback(). Fixes: 76c56a5affeb ("drm/hyperv: Add DRM driver for hyperv synthetic video device") Cc: stable@vger.kernel.org # 5.14+ Signed-off-by: Berkant Koc Assisted-by: Claude:claude-opus-4-7 berkoc-pipeline Reviewed-by: Michael Kelley Tested-by: Michael Kelley Signed-off-by: Hamza Mahfooz Link: https://patch.msgid.link/8200dbc199c7a9b75ac7e8af6c748d2189b5ebd5.1779542874.git.me@berkoc.com --- drivers/gpu/drm/hyperv/hyperv_drm_proto.c | 100 +++++++++++++++++++--- 1 file changed, 87 insertions(+), 13 deletions(-) diff --git a/drivers/gpu/drm/hyperv/hyperv_drm_proto.c b/drivers/gpu/drm/hyperv/hyperv_drm_proto.c index c3d0ff229e3d..4e6f703a1b33 100644 --- a/drivers/gpu/drm/hyperv/hyperv_drm_proto.c +++ b/drivers/gpu/drm/hyperv/hyperv_drm_proto.c @@ -420,30 +420,92 @@ static int hyperv_get_supported_resolution(struct hv_device *hdev) return 0; } -static void hyperv_receive_sub(struct hv_device *hdev) +static void hyperv_receive_sub(struct hv_device *hdev, u32 bytes_recvd) { struct hyperv_drm_device *hv = hv_get_drvdata(hdev); struct synthvid_msg *msg; + size_t hdr_size; + size_t need; if (!hv) return; - msg = (struct synthvid_msg *)hv->recv_buf; - - /* Complete the wait event */ - if (msg->vid_hdr.type == SYNTHVID_VERSION_RESPONSE || - msg->vid_hdr.type == SYNTHVID_RESOLUTION_RESPONSE || - msg->vid_hdr.type == SYNTHVID_VRAM_LOCATION_ACK) { - memcpy(hv->init_buf, msg, VMBUS_MAX_PACKET_SIZE); - complete(&hv->wait); + hdr_size = sizeof(struct pipe_msg_hdr) + + sizeof(struct synthvid_msg_hdr); + if (bytes_recvd < hdr_size) { + drm_err_ratelimited(&hv->dev, + "synthvid packet too small for header: %u\n", + bytes_recvd); return; } - if (msg->vid_hdr.type == SYNTHVID_FEATURE_CHANGE) { + msg = (struct synthvid_msg *)hv->recv_buf; + need = hdr_size; + + switch (msg->vid_hdr.type) { + case SYNTHVID_VERSION_RESPONSE: + need += sizeof(struct synthvid_version_resp); + break; + case SYNTHVID_RESOLUTION_RESPONSE: + /* + * The resolution response is variable length: the host + * fills resolution_count entries, not the full + * SYNTHVID_MAX_RESOLUTION_COUNT array. Require the fixed + * prefix first so resolution_count can be read, then + * demand exactly the count-sized array. + */ + need += offsetof(struct synthvid_supported_resolution_resp, + supported_resolution); + if (bytes_recvd < need) + break; + if (msg->resolution_resp.resolution_count > + SYNTHVID_MAX_RESOLUTION_COUNT) { + drm_err_ratelimited(&hv->dev, + "synthvid resolution count too large: %u\n", + msg->resolution_resp.resolution_count); + return; + } + need += msg->resolution_resp.resolution_count * + sizeof(struct hvd_screen_info); + break; + case SYNTHVID_VRAM_LOCATION_ACK: + need += sizeof(struct synthvid_vram_location_ack); + break; + case SYNTHVID_FEATURE_CHANGE: + /* + * Not a completion-driving message: validate its own payload + * and consume it here rather than falling through to the + * memcpy/complete shared by the wait-event responses. + */ + if (bytes_recvd < need + + sizeof(struct synthvid_feature_change)) { + drm_err_ratelimited(&hv->dev, + "synthvid feature change packet too small: %u\n", + bytes_recvd); + return; + } hv->dirt_needed = msg->feature_chg.is_dirt_needed; if (hv->dirt_needed) hyperv_hide_hw_ptr(hv->hdev); + return; + default: + return; } + + /* + * Shared completion path for the wait-event responses + * (VERSION_RESPONSE, RESOLUTION_RESPONSE, VRAM_LOCATION_ACK): + * require the type-specific payload before handing the buffer to + * the waiter. + */ + if (bytes_recvd < need) { + drm_err_ratelimited(&hv->dev, + "synthvid packet too small for type %u: %u < %zu\n", + msg->vid_hdr.type, bytes_recvd, need); + return; + } + memcpy(hv->init_buf, msg, bytes_recvd); + complete(&hv->wait); } static void hyperv_receive(void *ctx) @@ -464,9 +526,21 @@ static void hyperv_receive(void *ctx) ret = vmbus_recvpacket(hdev->channel, recv_buf, VMBUS_MAX_PACKET_SIZE, &bytes_recvd, &req_id); - if (bytes_recvd > 0 && - recv_buf->pipe_hdr.type == PIPE_MSG_DATA) - hyperv_receive_sub(hdev); + if (ret) { + /* + * A nonzero return (e.g. -ENOBUFS for an oversized + * packet) is itself a malformed message: bytes_recvd + * then reports the required length rather than a copied + * payload, so it must not be forwarded to the + * sub-handler. Channel recovery is not attempted. + */ + drm_err_ratelimited(&hv->dev, + "vmbus_recvpacket failed: %d (need %u)\n", + ret, bytes_recvd); + } else if (bytes_recvd > 0 && + recv_buf->pipe_hdr.type == PIPE_MSG_DATA) { + hyperv_receive_sub(hdev, bytes_recvd); + } } while (bytes_recvd > 0 && ret == 0); } From 7c2eee9c136734825ff524dd8b2146438a4f8250 Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Wed, 13 May 2026 13:51:22 +0300 Subject: [PATCH 167/337] memblock: don't touch memblock arrays when memblock_free() is called late When memblock_free() is called after memblock_discard() on architectures that don't select ARCH_KEEP_MEMBLOCK, it tries to update memblock.reserved that was already discarded and it causes use-after-free, for example [ 8.514775] BUG: KASAN: use-after-free in memblock_isolate_range+0x4ac/0x650 [ 8.514775] Read of size 8 at addr ffff88a07fe6a000 by task swapper/0/1 [ 8.514775] Call Trace: [ 8.514775] [ 8.514775] kasan_report+0xb2/0x1b0 [ 8.514775] memblock_isolate_range+0x4ac/0x650 [ 8.514775] memblock_phys_free+0xc4/0x190 [ 8.514775] housekeeping_late_init+0x257/0x280 [ 8.514775] do_one_initcall+0xaa/0x470 [ 8.514775] do_initcalls+0x1b4/0x1f0 [ 8.514775] kernel_init_freeable+0x4b5/0x550 [ 8.514775] kernel_init+0x1c/0x150 [ 8.514775] ret_from_fork+0x5dc/0x8e0 [ 8.514775] ret_from_fork_asm+0x1a/0x30 [ 8.514775] Make sure memblock_free() updates memblock.reserved only when called early enough or when ARCH_KEEP_MEMBLOCK is enabled. Reported-by: Waiman Long Reported-by: Breno Leitao Closes: https://lore.kernel.org/all/20260505051821.1107133-1-longman@redhat.com Tested-by: Waiman Long Tested-by: Breno Leitao Fixes: 87ce9e83ab8b ("memblock, treewide: make memblock_free() handle late freeing") Link: https://patch.msgid.link/20260513105122.502506-1-rppt@kernel.org Signed-off-by: Mike Rapoport (Microsoft) --- mm/memblock.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/mm/memblock.c b/mm/memblock.c index a6a1c91e276d..ccd43f3abb82 100644 --- a/mm/memblock.c +++ b/mm/memblock.c @@ -989,13 +989,15 @@ void __init_memblock memblock_free(void *ptr, size_t size) int __init_memblock memblock_phys_free(phys_addr_t base, phys_addr_t size) { phys_addr_t end = base + size - 1; - int ret; + int ret = 0; memblock_dbg("%s: [%pa-%pa] %pS\n", __func__, &base, &end, (void *)_RET_IP_); kmemleak_free_part_phys(base, size); - ret = memblock_remove_range(&memblock.reserved, base, size); + + if (!slab_is_available() || IS_ENABLED(CONFIG_ARCH_KEEP_MEMBLOCK)) + ret = memblock_remove_range(&memblock.reserved, base, size); if (slab_is_available()) __free_reserved_area(base, base + size, -1); From 80501dff814eeccebf44a59340c3fe3a205eb120 Mon Sep 17 00:00:00 2001 From: "Borislav Petkov (AMD)" Date: Wed, 20 May 2026 13:25:07 -0700 Subject: [PATCH 168/337] Documentation/arch/x86: Hide clearcpuid= This option was never meant to be used in production because it solely clears the X86_FEATURE kernel-internal representation of what CPUID bits it has detected and doesn't do any *proper* feature disablement like clearing CR4.CET in the user shadow stack case, for example. So remove its documentation so that it doesn't get used in production and people get silly ideas. It is meant strictly for debugging; and if a chicken bit for properly disabling a feature is warranted, then that would need proper enablement. No functional changes. Signed-off-by: Borislav Petkov (AMD) Signed-off-by: Ingo Molnar Cc: Mathias Krause Cc: Linus Torvalds Link: https://patch.msgid.link/20260520202508.160112-1-bp@kernel.org --- .../admin-guide/kernel-parameters.txt | 18 ------------------ Documentation/arch/x86/cpuinfo.rst | 4 ++++ 2 files changed, 4 insertions(+), 18 deletions(-) diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index 4d0f545fb3ec..97007f4f69d4 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -789,24 +789,6 @@ Kernel parameters cio_ignore= [S390] See Documentation/arch/s390/common_io.rst for details. - clearcpuid=X[,X...] [X86] - Disable CPUID feature X for the kernel. See - arch/x86/include/asm/cpufeatures.h for the valid bit - numbers X. Note the Linux-specific bits are not necessarily - stable over kernel options, but the vendor-specific - ones should be. - X can also be a string as appearing in the flags: line - in /proc/cpuinfo which does not have the above - instability issue. However, not all features have names - in /proc/cpuinfo. - Note that using this option will taint your kernel. - Also note that user programs calling CPUID directly - or using the feature without checking anything - will still see it. This just prevents it from - being used by the kernel or shown in /proc/cpuinfo. - Also note the kernel might malfunction if you disable - some critical bits. - clk_ignore_unused [CLK] Prevents the clock framework from automatically gating diff --git a/Documentation/arch/x86/cpuinfo.rst b/Documentation/arch/x86/cpuinfo.rst index 9f2e47c4b1c8..17fce95367e6 100644 --- a/Documentation/arch/x86/cpuinfo.rst +++ b/Documentation/arch/x86/cpuinfo.rst @@ -187,6 +187,10 @@ to disable features using the feature number as defined in Protection can be disabled using clearcpuid=514. The number 514 is calculated from #define X86_FEATURE_UMIP (16*32 + 2). +DO NOT USE this cmdline option in production - it is meant to be used only as +a quick'n'dirty debugging aid to rule out a feature-enabling code is the +culprit. If you use it, it'll taint the kernel. + In addition, there exists a variety of custom command-line parameters that disable specific features. The list of parameters includes, but is not limited to, nofsgsbase, nosgx, noxsave, etc. 5-level paging can also be disabled using From 44e151be23deb788d9f6124de93823faf6e04e99 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 25 May 2026 10:14:42 +0300 Subject: [PATCH 169/337] accel/ivpu: prevent uninitialized data bug in debugfs The simple_write_to_buffer() will only initialize data starting from the *pos offset so if it's non-zero then the first part of the buffer uninitialized. Really, if *pos is non-zero then this code won't work so just check for that at the start of the function. Fixes: 320323d2e545 ("accel/ivpu: Add debugfs interface for setting HWS priority bands") Signed-off-by: Dan Carpenter Reviewed-by: Karol Wachowski Signed-off-by: Karol Wachowski Link: https://patch.msgid.link/ahP24m6Mii9EDL7Q@stanley.mountain --- drivers/accel/ivpu/ivpu_debugfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/accel/ivpu/ivpu_debugfs.c b/drivers/accel/ivpu/ivpu_debugfs.c index 189dbe94cf14..dc20bc73c6ed 100644 --- a/drivers/accel/ivpu/ivpu_debugfs.c +++ b/drivers/accel/ivpu/ivpu_debugfs.c @@ -450,7 +450,7 @@ priority_bands_fops_write(struct file *file, const char __user *user_buf, size_t u32 band; int ret; - if (size >= sizeof(buf)) + if (*pos != 0 || size >= sizeof(buf)) return -EINVAL; ret = simple_write_to_buffer(buf, sizeof(buf) - 1, pos, user_buf, size); From 539b7cf1f77a5135241f410777b3c780c6e23994 Mon Sep 17 00:00:00 2001 From: Niklas Schnelle Date: Thu, 21 May 2026 13:10:44 +0200 Subject: [PATCH 170/337] s390/configs: Enable IOMMUFD and VFIO cdev in defconfigs Enable IOMMUFD and VFIO cdev such that PCI pass-through to QEMU/KVM can optionally utilize native IOMMUFD. Note that because the defconfigs do not enable IOMMUFD_VFIO_CONTAINER the default PCI pass-through using VFIO with the existing container interface is not affected. Signed-off-by: Niklas Schnelle Acked-by: Heiko Carstens Acked-by: Matthew Rosato Signed-off-by: Alexander Gordeev --- arch/s390/configs/debug_defconfig | 4 ++++ arch/s390/configs/defconfig | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/arch/s390/configs/debug_defconfig b/arch/s390/configs/debug_defconfig index c28f9a7d0bd8..730c90b4a876 100644 --- a/arch/s390/configs/debug_defconfig +++ b/arch/s390/configs/debug_defconfig @@ -56,6 +56,10 @@ CONFIG_EXPOLINE_AUTO=y CONFIG_CHSC_SCH=y CONFIG_VFIO_CCW=m CONFIG_VFIO_AP=m +CONFIG_VFIO_DEVICE_CDEV=y +CONFIG_IOMMUFD_DRIVER=y +CONFIG_IOMMUFD_DRIVER_CORE=y +CONFIG_IOMMUFD=y CONFIG_CMM=m CONFIG_APPLDATA_BASE=y CONFIG_S390_HYPFS_FS=y diff --git a/arch/s390/configs/defconfig b/arch/s390/configs/defconfig index d89c988f33ea..dd5fc1426c88 100644 --- a/arch/s390/configs/defconfig +++ b/arch/s390/configs/defconfig @@ -54,6 +54,10 @@ CONFIG_EXPOLINE_AUTO=y CONFIG_CHSC_SCH=y CONFIG_VFIO_CCW=m CONFIG_VFIO_AP=m +CONFIG_VFIO_DEVICE_CDEV=y +CONFIG_IOMMUFD_DRIVER=y +CONFIG_IOMMUFD_DRIVER_CORE=y +CONFIG_IOMMUFD=y CONFIG_CMM=m CONFIG_APPLDATA_BASE=y CONFIG_S390_HYPFS_FS=y From d64b0372760e09de6d18a0616d7bc652c8c6891d Mon Sep 17 00:00:00 2001 From: George Guo Date: Sat, 9 May 2026 10:44:15 +0800 Subject: [PATCH 171/337] kho: fix KHO_TREE_MAX_DEPTH for non-4KB page sizes KHO_TREE_MAX_DEPTH is calculated as: DIV_ROUND_UP(KHO_ORDER_0_LOG2 - KHO_BITMAP_SIZE_LOG2, KHO_TABLE_SIZE_LOG2) + 1 For systems with 16KB pages (e.g. arm64 with CONFIG_ARM64_16K_PAGES=y or LoongArch), this gives a depth of 4. Since levels are 0 based, with depth = 4 the effective top level is 3 and the top-level shift at bit 39. PAGE_SHIFT = 14 KHO_BITMAP_SIZE_LOG2 = PAGE_SHIFT + 3 = 17 KHO_TABLE_SIZE_LOG2 = log(2; (1 << PAGE_SHIFT) / 8) = 11 shift = ((3 - 1) * KHO_TABLE_SIZE_LOG2) + KHO_BITMAP_SIZE_LOG2 = 39 The order-0 bit sits at bit 50 (KHO_ORDER_0_LOG2 = 64 - PAGE_SHIFT = 50). When inserting or reading a key, the index extracted at the top level is: (1 << 50) >> 39 = 2048 2048 is exactly the table size (PAGE_SIZE / sizeof(phys_addr_t) = 2048 for 16KB pages), so it wraps to 0, aliasing the order bit to index 0 and losing it silently. On the second kernel, kho_radix_decode_key() sees a key without the order bit, calls fls64() on the wrong bit, computes a wrong order and thus a garbage physical address. phys_to_page() of that address faults in kho_preserved_memory_reserve(), causing a kernel panic early in boot. Fix by adding +1 to the DIV_ROUND_UP numerator so the formula accounts for the order bit itself, giving depth 5 for 16KB pages. The top-level shift becomes 50, and (1 << 50) >> 50 = 1, which is nonzero and unambiguous. For 4KB and 64KB page sizes the depth is unchanged. Link: https://patch.msgid.link/20260509024415.33190-1-dongtai.guo@linux.dev Fixes: 3f2ad90060f6 ("kho: adopt radix tree for preserved memory tracking") Tested-by: Kexin Liu Signed-off-by: George Guo Reviewed-by: Pasha Tatashin [rppt: added actual math to the changelog] Signed-off-by: Mike Rapoport (Microsoft) --- include/linux/kho/abi/kexec_handover.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/kho/abi/kexec_handover.h b/include/linux/kho/abi/kexec_handover.h index 7e847a2339b0..db9bda6dd310 100644 --- a/include/linux/kho/abi/kexec_handover.h +++ b/include/linux/kho/abi/kexec_handover.h @@ -274,7 +274,7 @@ enum kho_radix_consts { * and 1 bitmap level. */ KHO_TREE_MAX_DEPTH = - DIV_ROUND_UP(KHO_ORDER_0_LOG2 - KHO_BITMAP_SIZE_LOG2, + DIV_ROUND_UP(KHO_ORDER_0_LOG2 - KHO_BITMAP_SIZE_LOG2 + 1, KHO_TABLE_SIZE_LOG2) + 1, }; From 8fd2f26fa2a33cfe8ac043f976137ecf5b567f03 Mon Sep 17 00:00:00 2001 From: "Pratyush Yadav (Google)" Date: Tue, 19 May 2026 15:33:30 +0200 Subject: [PATCH 172/337] kho: fix order calculation for kho_unpreserve_pages() Commit 91e74fa8b1bc ("kho: make sure preservations do not span multiple NUMA nodes") made sure preservations from kho_preserve_pages() do not span multiple NUMA nodes. If they do, the order is reduced and tried again. The same logic was not implemented for kho_unpreserve_pages(). This can result in unpreserve calculating a different order than preserve, and thus not actually unpreserving the pages. Fix this by moving the order calculation logic to __kho_preserve_pages_order() and use it from both preserve and unpreserve paths. Move __kho_unpreserve() down to avoid having a forward declaration. Its users are further down in the file anyway. Also, it results in grouping for all the page-level preservation and unpreservation functions. This unfortunately makes the diff hard to read, but the main change in __kho_unpreserve() is to call __kho_preserve_pages_order() instead of open-coding the order calculation. Fixes: 91e74fa8b1bc ("kho: make sure preservations do not span multiple NUMA nodes") Cc: stable@vger.kernel.org Signed-off-by: Pratyush Yadav (Google) Reviewed-by: Samiullah Khawaja Reviewed-by: Pasha Tatashin Link: https://patch.msgid.link/20260519133332.2498092-1-pratyush@kernel.org Signed-off-by: Mike Rapoport (Microsoft) --- kernel/liveupdate/kexec_handover.c | 56 +++++++++++++++++------------- 1 file changed, 32 insertions(+), 24 deletions(-) diff --git a/kernel/liveupdate/kexec_handover.c b/kernel/liveupdate/kexec_handover.c index 2592f7ca16e2..1b592d86dc48 100644 --- a/kernel/liveupdate/kexec_handover.c +++ b/kernel/liveupdate/kexec_handover.c @@ -357,20 +357,6 @@ int kho_radix_walk_tree(struct kho_radix_tree *tree, } EXPORT_SYMBOL_GPL(kho_radix_walk_tree); -static void __kho_unpreserve(struct kho_radix_tree *tree, - unsigned long pfn, unsigned long end_pfn) -{ - unsigned int order; - - while (pfn < end_pfn) { - order = min(count_trailing_zeros(pfn), ilog2(end_pfn - pfn)); - - kho_radix_del_page(tree, pfn, order); - - pfn += 1 << order; - } -} - /* For physically contiguous 0-order pages. */ static void kho_init_pages(struct page *page, unsigned long nr_pages) { @@ -860,6 +846,37 @@ void kho_unpreserve_folio(struct folio *folio) } EXPORT_SYMBOL_GPL(kho_unpreserve_folio); +static unsigned int __kho_preserve_pages_order(unsigned long start_pfn, + unsigned long end_pfn) +{ + unsigned int order = min(count_trailing_zeros(start_pfn), + ilog2(end_pfn - start_pfn)); + + /* + * Make sure all the pages in a single preservation are in the same NUMA + * node. The restore machinery can not cope with a preservation spanning + * multiple NUMA nodes. + */ + while (pfn_to_nid(start_pfn) != pfn_to_nid(start_pfn + (1UL << order) - 1)) + order--; + + return order; +} + +static void __kho_unpreserve(struct kho_radix_tree *tree, + unsigned long pfn, unsigned long end_pfn) +{ + unsigned int order; + + while (pfn < end_pfn) { + order = __kho_preserve_pages_order(pfn, end_pfn); + + kho_radix_del_page(tree, pfn, order); + + pfn += 1 << order; + } +} + /** * kho_preserve_pages - preserve contiguous pages across kexec * @page: first page in the list. @@ -885,16 +902,7 @@ int kho_preserve_pages(struct page *page, unsigned long nr_pages) } while (pfn < end_pfn) { - unsigned int order = - min(count_trailing_zeros(pfn), ilog2(end_pfn - pfn)); - - /* - * Make sure all the pages in a single preservation are in the - * same NUMA node. The restore machinery can not cope with a - * preservation spanning multiple NUMA nodes. - */ - while (pfn_to_nid(pfn) != pfn_to_nid(pfn + (1UL << order) - 1)) - order--; + unsigned int order = __kho_preserve_pages_order(pfn, end_pfn); err = kho_radix_add_page(tree, pfn, order); if (err) { From 5c4063c87a619e4df954c179d24628636f5db15f Mon Sep 17 00:00:00 2001 From: Janusz Krzysztofik Date: Fri, 8 May 2026 14:23:51 +0200 Subject: [PATCH 173/337] drm/i915: Fix potential UAF in TTM object purge MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TLDR: The bo->ttm object might be changed by calling ttm_bo_validate(), move casting it to an i915_tt object later to actually get the right pointer. A user reported hitting the following bug under heavy use on DG2: [26620.095550] Oops: general protection fault, probably for non-canonical address 0xa56b6b6b6b6b6b8b: 0000 1 SMP NOPTI [26620.095556] CPU: 2 UID: 0 PID: 631 Comm: Xorg Not tainted 6.18.8 #1 PREEMPT(lazy) [26620.095558] Hardware name: ASRock B850M Steel Legend WiFi/B850M Steel Legend WiFi, BIOS 3.50 09/18/2025 [26620.095559] RIP: 0010:i915_ttm_purge+0x84/0x100 [i915] [26620.095604] Code: 00 00 00 48 8d 54 24 10 48 89 e6 48 89 fb e8 83 aa ae ff 85 c0 75 6f 48 83 bb a8 01 00 00 00 74 2c 48 8b 45 78 48 85 c0 74 23 <48> 8b 78 20 48 c7 c2 ff ff ff ff 31 f6 e8 7a 73 e3 e0 48 8b 7d 78 [26620.095605] RSP: 0018:ffffc90005fd7430 EFLAGS: 00010282 [26620.095607] RAX: a56b6b6b6b6b6b6b RBX: ffff8881f46c3dc0 RCX: 0000000000000000 [26620.095608] RDX: 0000000000000000 RSI: 0000000000000246 RDI: 00000000ffffffff [26620.095609] RBP: ffff888289610f00 R08: 0000000000000001 R09: ffff88823b022000 [26620.095609] R10: ffff888103029b28 R11: ffff8881fc7f3800 R12: ffff88810b6150d0 [26620.095609] R13: ffff888289610f00 R14: 0000000000000000 R15: ffff8881f46c3dc0 [26620.095610] FS: 00007f1004d86900(0000) GS:ffff88901c858000(0000) knlGS:0000000000000000 [26620.095611] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [26620.095611] CR2: 00007f0fdf489000 CR3: 000000035b0c1000 CR4: 0000000000750ef0 [26620.095612] PKRU: 55555554 [26620.095612] Call Trace: [26620.095615] [26620.095615] i915_ttm_move+0x2b9/0x420 [i915] [26620.095642] ? ttm_tt_init+0x65/0x80 [ttm] [26620.095644] ? i915_ttm_tt_create+0xc6/0x150 [i915] [26620.095667] ttm_bo_handle_move_mem+0xb6/0x160 [ttm] [26620.095669] ttm_bo_evict+0x100/0x150 [ttm] [26620.095671] ? preempt_count_add+0x64/0xa0 [26620.095673] ? _raw_spin_lock+0xe/0x30 [26620.095675] ? _raw_spin_unlock+0xd/0x30 [26620.095675] ? i915_gem_object_evictable+0xb7/0xd0 [i915] [26620.095704] ttm_bo_evict_cb+0x6e/0xd0 [ttm] [26620.095705] ttm_lru_walk_for_evict+0xa6/0x200 [ttm] [26620.095708] ttm_bo_alloc_resource+0x185/0x4f0 [ttm] [26620.095709] ? init_object+0x62/0xd0 [26620.095712] ttm_bo_validate+0x7a/0x180 [ttm] [26620.095713] ? _raw_spin_unlock_irqrestore+0x16/0x30 [26620.095714] __i915_ttm_get_pages+0xb0/0x170 [i915] [26620.095737] i915_ttm_get_pages+0x9f/0x150 [i915] [26620.095759] ? i915_gem_do_execbuffer+0xedc/0x2b40 [i915] [26620.095786] ? alloc_debug_processing+0xd0/0x100 [26620.095787] ? _raw_spin_unlock_irqrestore+0x16/0x30 [26620.095788] ? i915_vma_instance+0xa0/0x4e0 [i915] [26620.095822] __i915_gem_object_get_pages+0x2f/0x40 [i915] [26620.095848] i915_vma_pin_ww+0x706/0x980 [i915] [26620.095875] ? i915_gem_do_execbuffer+0xedc/0x2b40 [i915] [26620.095904] eb_validate_vmas+0x170/0xa00 [i915] [26620.095930] i915_gem_do_execbuffer+0x1201/0x2b40 [i915] [26620.095953] ? alloc_debug_processing+0xd0/0x100 [26620.095954] ? _raw_spin_unlock_irqrestore+0x16/0x30 [26620.095955] ? i915_gem_execbuffer2_ioctl+0xc9/0x240 [i915] [26620.095977] ? __wake_up_sync_key+0x32/0x50 [26620.095979] ? i915_gem_execbuffer2_ioctl+0xc9/0x240 [i915] [26620.096001] ? __slab_alloc.isra.0+0x67/0xc0 [26620.096003] i915_gem_execbuffer2_ioctl+0x11a/0x240 [i915] Results from decode_stacktrace.sh pointed to dereference of a file pointer field of a i915 TTM page vector container associated with an object being purged on eviction. That path is taken when the object is marked as no longer needed. Code analysis revealed a possibility of the i915 TTM page vector container being replaced with a new instance inside a function that purges content of the object, should it be still busy. That function is called, indirectly via a more general function that changes the object's placement and caching policy, before the problematic dereference, but still after a pointer to the container is captured, rendering the pointer no longer valid. Fix the issue by capturing the pointer to the container only after its potential replacement. v2: Move the container_of() inside the if block (Sebastian), - a simplified version of the commit description that explains briefly why the change is necessary (Christian). Closes: https://gitlab.freedesktop.org/drm/i915/kernel/-/work_items/14882 Fixes: 7ae034590ceae ("drm/i915/ttm: add tt shmem backend") Signed-off-by: Janusz Krzysztofik Cc: stable@vger.kernel.org # v5.17+ Cc: Matthew Auld Cc: Thomas Hellström Cc: Sebastian Brzezinka Cc: Christian König Reviewed-by: Andi Shyti Reviewed-by: Christian König Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20260508122612.469227-2-janusz.krzysztofik@linux.intel.com (cherry picked from commit 4462966a93eb185849b7f174f0d0de53476d00a4) Signed-off-by: Tvrtko Ursulin --- drivers/gpu/drm/i915/gem/i915_gem_ttm.c | 28 ++++++++++++++----------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/i915/gem/i915_gem_ttm.c b/drivers/gpu/drm/i915/gem/i915_gem_ttm.c index de70517b4ef2..df3fcc2b1248 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_ttm.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_ttm.c @@ -419,8 +419,6 @@ void i915_ttm_free_cached_io_rsgt(struct drm_i915_gem_object *obj) int i915_ttm_purge(struct drm_i915_gem_object *obj) { struct ttm_buffer_object *bo = i915_gem_to_ttm(obj); - struct i915_ttm_tt *i915_tt = - container_of(bo->ttm, typeof(*i915_tt), ttm); struct ttm_operation_ctx ctx = { .interruptible = true, .no_wait_gpu = false, @@ -435,16 +433,22 @@ int i915_ttm_purge(struct drm_i915_gem_object *obj) if (ret) return ret; - if (bo->ttm && i915_tt->filp) { - /* - * The below fput(which eventually calls shmem_truncate) might - * be delayed by worker, so when directly called to purge the - * pages(like by the shrinker) we should try to be more - * aggressive and release the pages immediately. - */ - shmem_truncate_range(file_inode(i915_tt->filp), - 0, (loff_t)-1); - fput(fetch_and_zero(&i915_tt->filp)); + if (bo->ttm) { + struct i915_ttm_tt *i915_tt = + container_of(bo->ttm, typeof(*i915_tt), ttm); + + if (i915_tt->filp) { + /* + * The below fput(which eventually calls shmem_truncate) + * might be delayed by worker, so when directly called + * to purge the pages(like by the shrinker) we should + * try to be more aggressive and release the pages + * immediately. + */ + shmem_truncate_range(file_inode(i915_tt->filp), + 0, (loff_t)-1); + fput(fetch_and_zero(&i915_tt->filp)); + } } obj->write_domain = 0; From 202e77cf2e839e1adc804433322dc5c9ee511c9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Grzelak?= Date: Thu, 16 Apr 2026 18:37:44 +0200 Subject: [PATCH 174/337] drm/i915/aux: use polling when irqs are unavailable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PTL with physically disconnected display was observed to have 40s longer execution time when testing xe_fault_injection@xe_guc_mmio_send_recv. The issue has not been seen when reverting commit 40a9f77a28fa ("Revert "drm/i915/dp: change aux_ctl reg read to polling read""). Apparently the configuration suffers from not having AUX enabled when using interrupts. One probable cause can be xe enabling interrupts too late: interrupts need memory allocations which currently can't be done before the display FB takeover is done. As for now, use polling for AUX in case interrupts are unavailable. Fixes: 40a9f77a28fa ("Revert "drm/i915/dp: change aux_ctl reg read to polling read"") Suggested-by: Ville Syrjälä Signed-off-by: Michał Grzelak Signed-off-by: Ville Syrjälä Link: https://patch.msgid.link/20260416163744.288107-1-michal.grzelak@intel.com (cherry picked from commit 05e0550b65cd1604bd515fbc65f522bce4c10a87) Signed-off-by: Tvrtko Ursulin --- drivers/gpu/drm/i915/display/intel_dp_aux.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp_aux.c b/drivers/gpu/drm/i915/display/intel_dp_aux.c index b20ec3e589fa..9c9b6410366d 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_aux.c +++ b/drivers/gpu/drm/i915/display/intel_dp_aux.c @@ -12,6 +12,7 @@ #include "intel_dp.h" #include "intel_dp_aux.h" #include "intel_dp_aux_regs.h" +#include "intel_parent.h" #include "intel_pps.h" #include "intel_quirks.h" #include "intel_tc.h" @@ -60,18 +61,29 @@ intel_dp_aux_wait_done(struct intel_dp *intel_dp) struct intel_display *display = to_intel_display(intel_dp); i915_reg_t ch_ctl = intel_dp->aux_ch_ctl_reg(intel_dp); const unsigned int timeout_ms = 10; + bool done = true; u32 status; - bool done; + int ret; + if (intel_parent_irq_enabled(display)) { #define C (((status = intel_de_read_notrace(display, ch_ctl)) & DP_AUX_CH_CTL_SEND_BUSY) == 0) - done = wait_event_timeout(display->gmbus.wait_queue, C, - msecs_to_jiffies_timeout(timeout_ms)); + done = wait_event_timeout(display->gmbus.wait_queue, C, + msecs_to_jiffies_timeout(timeout_ms)); + +#undef C + } else { + ret = intel_de_wait_ms(display, ch_ctl, + DP_AUX_CH_CTL_SEND_BUSY, 0, + timeout_ms, &status); + + if (ret == -ETIMEDOUT) + done = false; + } if (!done) drm_err(display->drm, "%s: did not complete or timeout within %ums (status 0x%08x)\n", intel_dp->aux.name, timeout_ms, status); -#undef C return status; } From d196136a988051173f68f91de0b5a1bd32122dd7 Mon Sep 17 00:00:00 2001 From: Pranay Samala Date: Tue, 19 May 2026 13:23:08 +0530 Subject: [PATCH 175/337] drm/i915/color: Fix HDR pre-CSC LUT programming loop The integer lut programming loop never executes completely due to incorrect condition (i++ > 130). Fix to properly program 129th+ entries for values > 1.0. Cc: #v6.19 Fixes: 82caa1c8813f ("drm/i915/color: Program Pre-CSC registers") Signed-off-by: Pranay Samala Signed-off-by: Chaitanya Kumar Borah Reviewed-by: Uma Shankar Signed-off-by: Suraj Kandpal Link: https://patch.msgid.link/20260519075308.383877-1-pranay.samala@intel.com (cherry picked from commit f33862ec3e8849ad7c0a3dd46719083b13ade248) Signed-off-by: Tvrtko Ursulin --- drivers/gpu/drm/i915/display/intel_color.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/display/intel_color.c b/drivers/gpu/drm/i915/display/intel_color.c index e7950655434b..6d1cffc6d2be 100644 --- a/drivers/gpu/drm/i915/display/intel_color.c +++ b/drivers/gpu/drm/i915/display/intel_color.c @@ -3976,7 +3976,7 @@ xelpd_program_plane_pre_csc_lut(struct intel_dsb *dsb, intel_de_write_dsb(display, dsb, PLANE_PRE_CSC_GAMC_DATA_ENH(pipe, plane, 0), (1 << 24)); - } while (i++ > 130); + } while (i++ < 130); } else { for (i = 0; i < lut_size; i++) { u32 v = (i * ((1 << 24) - 1)) / (lut_size - 1); From 8bb9093df555f9e89fdbe1405118b11384c03e04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jouni=20H=C3=B6gander?= Date: Wed, 20 May 2026 13:49:43 +0300 Subject: [PATCH 176/337] drm/i915/psr: Block DC states on vblank enable when Panel Replay supported MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently we are blocking DC states only when Panel Replay is enabled on vblank enable. It may happen that Panel Replay is getting enabled when vblank is already enabled. Fix this by blocking DC states always if Panel Replay is supported. While at it take care of possible dual eDP case by looping all encoders supporting PSR. Fixes: 0c427ac78a1d ("drm/i915/psr: Add interface to notify PSR of vblank enable/disable") Cc: # v6.16+ Signed-off-by: Jouni Högander Reviewed-by: Michał Grzelak Link: https://patch.msgid.link/20260520104944.239797-1-jouni.hogander@intel.com (cherry picked from commit eb5911f990554f7ce947dd53df00c114362e4465) Signed-off-by: Tvrtko Ursulin --- drivers/gpu/drm/i915/display/intel_psr.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c index 29904a037575..bd5a8c6ac6ef 100644 --- a/drivers/gpu/drm/i915/display/intel_psr.c +++ b/drivers/gpu/drm/i915/display/intel_psr.c @@ -4151,32 +4151,33 @@ void intel_psr_notify_vblank_enable_disable(struct intel_display *display, bool enable) { struct intel_encoder *encoder; + bool block_dc_states = false; for_each_intel_encoder_with_psr(display->drm, encoder) { struct intel_dp *intel_dp = enc_to_intel_dp(encoder); mutex_lock(&intel_dp->psr.lock); - if (intel_dp->psr.panel_replay_enabled) { - mutex_unlock(&intel_dp->psr.lock); - break; - } + if (CAN_PANEL_REPLAY(intel_dp)) + block_dc_states = true; - if (intel_dp->psr.enabled && intel_dp->psr.pkg_c_latency_used) + if (intel_dp->psr.enabled && !intel_dp->psr.panel_replay_enabled && + intel_dp->psr.pkg_c_latency_used) intel_psr_apply_underrun_on_idle_wa_locked(intel_dp); mutex_unlock(&intel_dp->psr.lock); - return; } /* * NOTE: intel_display_power_set_target_dc_state is used - * only by PSR * code for DC3CO handling. DC3CO target + * only by PSR code for DC3CO handling. DC3CO target * state is currently disabled in * PSR code. If DC3CO * is taken into use we need take that into account here * as well. */ - intel_display_power_set_target_dc_state(display, enable ? DC_STATE_DISABLE : - DC_STATE_EN_UPTO_DC6); + if (block_dc_states) + intel_display_power_set_target_dc_state(display, enable ? + DC_STATE_DISABLE : + DC_STATE_EN_UPTO_DC6); } static void From 3549a9649dc7c5fc586ab12f675279283cdcb2a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jouni=20H=C3=B6gander?= Date: Wed, 20 May 2026 13:49:44 +0300 Subject: [PATCH 177/337] drm/i915/psr: Use DC_OFF wake reference to block DC6 on vblank enable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We are observing following warnings: *ERROR* power well DC_off state mismatch (refcount 0/enabled 1) gen9_dc_off_power_well_enabled is considering target state DC_STATE_DISABLE as DC_OFF power well being enabled. Fix this by using wakeref for the purpose. To achieve this we need to modify notification code as well. Currently it is possible that PSR gets notified vblank enable/disable twice on same status. This is currently not a problem as it is just triggering call to intel_display_power_set_target_dc_state with same target state as a parameter. When using wakeref this becomes a problem due to reference counting. Fix this storing vbank status on last notification and use that to ensure there are no more than one notification with same vblank status. v2: ensure there is no subsequent notifications with same status Fixes: aa451abcffb5 ("drm/i915/display: Prevent DC6 while vblank is enabled for Panel Replay") Cc: # v6.13+ Signed-off-by: Jouni Högander Reviewed-by: Michał Grzelak Link: https://patch.msgid.link/20260520104944.239797-2-jouni.hogander@intel.com (cherry picked from commit 35485ac56d878192a3829a58cb26503125ec7104) Signed-off-by: Tvrtko Ursulin --- .../gpu/drm/i915/display/intel_display_core.h | 1 + .../gpu/drm/i915/display/intel_display_irq.c | 8 +++++-- .../drm/i915/display/intel_display_types.h | 2 ++ drivers/gpu/drm/i915/display/intel_psr.c | 24 +++++++------------ 4 files changed, 18 insertions(+), 17 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display_core.h b/drivers/gpu/drm/i915/display/intel_display_core.h index d9baca2d5aaf..78afcd42f44c 100644 --- a/drivers/gpu/drm/i915/display/intel_display_core.h +++ b/drivers/gpu/drm/i915/display/intel_display_core.h @@ -497,6 +497,7 @@ struct intel_display { u8 vblank_enabled; int vblank_enable_count; + bool vblank_status_last_notified; struct work_struct vblank_notify_work; diff --git a/drivers/gpu/drm/i915/display/intel_display_irq.c b/drivers/gpu/drm/i915/display/intel_display_irq.c index 70c1bba7c0a8..aedf3928a089 100644 --- a/drivers/gpu/drm/i915/display/intel_display_irq.c +++ b/drivers/gpu/drm/i915/display/intel_display_irq.c @@ -1773,8 +1773,12 @@ static void intel_display_vblank_notify_work(struct work_struct *work) struct intel_display *display = container_of(work, typeof(*display), irq.vblank_notify_work); int vblank_enable_count = READ_ONCE(display->irq.vblank_enable_count); + bool vblank_status = !!vblank_enable_count; - intel_psr_notify_vblank_enable_disable(display, vblank_enable_count); + if (display->irq.vblank_status_last_notified != vblank_status) { + intel_psr_notify_vblank_enable_disable(display, vblank_status); + display->irq.vblank_status_last_notified = vblank_status; + } } int bdw_enable_vblank(struct drm_crtc *_crtc) @@ -1787,10 +1791,10 @@ int bdw_enable_vblank(struct drm_crtc *_crtc) if (gen11_dsi_configure_te(crtc, true)) return 0; + spin_lock_irqsave(&display->irq.lock, irqflags); if (crtc->vblank_psr_notify && display->irq.vblank_enable_count++ == 0) schedule_work(&display->irq.vblank_notify_work); - spin_lock_irqsave(&display->irq.lock, irqflags); bdw_enable_pipe_irq(display, pipe, GEN8_PIPE_VBLANK); spin_unlock_irqrestore(&display->irq.lock, irqflags); diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h index 9c7c357afb09..2e6a85708555 100644 --- a/drivers/gpu/drm/i915/display/intel_display_types.h +++ b/drivers/gpu/drm/i915/display/intel_display_types.h @@ -1790,6 +1790,8 @@ struct intel_psr { u8 active_non_psr_pipes; const char *no_psr_reason; + + struct ref_tracker *vblank_wakeref; }; struct intel_dp { diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c index bd5a8c6ac6ef..598fe769a402 100644 --- a/drivers/gpu/drm/i915/display/intel_psr.c +++ b/drivers/gpu/drm/i915/display/intel_psr.c @@ -4151,14 +4151,20 @@ void intel_psr_notify_vblank_enable_disable(struct intel_display *display, bool enable) { struct intel_encoder *encoder; - bool block_dc_states = false; for_each_intel_encoder_with_psr(display->drm, encoder) { struct intel_dp *intel_dp = enc_to_intel_dp(encoder); mutex_lock(&intel_dp->psr.lock); - if (CAN_PANEL_REPLAY(intel_dp)) - block_dc_states = true; + if (CAN_PANEL_REPLAY(intel_dp)) { + if (enable) + intel_dp->psr.vblank_wakeref = + intel_display_power_get(display, + POWER_DOMAIN_DC_OFF); + else + intel_display_power_put(display, POWER_DOMAIN_DC_OFF, + intel_dp->psr.vblank_wakeref); + } if (intel_dp->psr.enabled && !intel_dp->psr.panel_replay_enabled && intel_dp->psr.pkg_c_latency_used) @@ -4166,18 +4172,6 @@ void intel_psr_notify_vblank_enable_disable(struct intel_display *display, mutex_unlock(&intel_dp->psr.lock); } - - /* - * NOTE: intel_display_power_set_target_dc_state is used - * only by PSR code for DC3CO handling. DC3CO target - * state is currently disabled in * PSR code. If DC3CO - * is taken into use we need take that into account here - * as well. - */ - if (block_dc_states) - intel_display_power_set_target_dc_state(display, enable ? - DC_STATE_DISABLE : - DC_STATE_EN_UPTO_DC6); } static void From 63838c323924fe4a78b2323bd45aa1030f72ca60 Mon Sep 17 00:00:00 2001 From: Yuho Choi Date: Sun, 24 May 2026 22:47:09 -0400 Subject: [PATCH 178/337] ARM: socfpga: Fix OF node refcount leak in SMP setup socfpga_smp_prepare_cpus() looks up the Cortex-A9 SCU node with of_find_compatible_node(), which returns a node reference that must be released with of_node_put(). The function maps the SCU registers and then returns without dropping that reference, leaking the node on both the success path and the of_iomap() failure path. Drop the reference once the mapping attempt is complete. The returned MMIO mapping does not depend on keeping the device node reference held. Fixes: 122694a0c712 ("ARM: socfpga: use of_iomap to map the SCU") Cc: stable@vger.kernel.org Signed-off-by: Yuho Choi Signed-off-by: Dinh Nguyen --- arch/arm/mach-socfpga/platsmp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/mach-socfpga/platsmp.c b/arch/arm/mach-socfpga/platsmp.c index 201191cf68f3..349e6c54518e 100644 --- a/arch/arm/mach-socfpga/platsmp.c +++ b/arch/arm/mach-socfpga/platsmp.c @@ -78,6 +78,7 @@ static void __init socfpga_smp_prepare_cpus(unsigned int max_cpus) } socfpga_scu_base_addr = of_iomap(np, 0); + of_node_put(np); if (!socfpga_scu_base_addr) return; scu_enable(socfpga_scu_base_addr); From 60474437af5fe12abd20607766c809e0ad4e7245 Mon Sep 17 00:00:00 2001 From: Varadarajan Narayanan Date: Thu, 14 May 2026 12:15:31 +0530 Subject: [PATCH 179/337] spi: dt-bindings: spi-qpic-snand: Add ipq5210 compatible Since the QPIC-SPI-NAND flash controller present in ipq5210 is the same as the one found in ipq9574, document the ipq5210 compatible and with ipq9574 as the fallback. Signed-off-by: Varadarajan Narayanan Link: https://patch.msgid.link/20260514-ipq5210-nand-v1-1-cbdd7492e826@oss.qualcomm.com Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/spi/qcom,spi-qpic-snand.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/spi/qcom,spi-qpic-snand.yaml b/Documentation/devicetree/bindings/spi/qcom,spi-qpic-snand.yaml index 7d0571feb46d..829da22537d4 100644 --- a/Documentation/devicetree/bindings/spi/qcom,spi-qpic-snand.yaml +++ b/Documentation/devicetree/bindings/spi/qcom,spi-qpic-snand.yaml @@ -25,6 +25,7 @@ properties: - items: - enum: - qcom,ipq5018-snand + - qcom,ipq5210-snand - qcom,ipq5332-snand - qcom,ipq5424-snand - const: qcom,ipq9574-snand From cda64169bade79427f264e43d0f422eaed9dc116 Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Wed, 13 May 2026 22:06:01 +0200 Subject: [PATCH 180/337] x86/microcode: Do not access MSR_IA32_PLATFORM_ID when running as a guest Patch in Fixes: causes the usual: unchecked MSR access error: RDMSR from 0x17 at ... (intel_get_platform_id) Call Trace: early_init_intel early_cpu_init setup_arch _printk start_kernel x86_64_start_reservations x86_64_start_kernel common_startup_64 because the kernel is booted in a guest. In order to avoid it, this MSR access needs to be prevented when running virtualized. That is usually done by checking X86_FEATURE_HYPERVISOR but for this particular case it is too early yet. The platform ID needs to be read as early as when microcode is loaded on the BSP: load_ucode_bsp ... -> get_microcode_blob ... -> intel_find_matching_signature and by that time, CPUID leafs haven't been parsed yet. The microcode loader already has logic to check early whether the kernel is running virtualized so make that globally available to arch/x86/. The query whether running virtualized is getting more and more prominent in recent times so might as well make it an arch-global var which the rest of the code can use. Fixes: d8630b67ca1ed ("x86/cpu: Add platform ID to CPU info structure") Reported-by: Vishal Verma Signed-off-by: Borislav Petkov (AMD) Reviewed-by: Binbin Wu Reviewed-by: Xiaoyao Li Tested-by: Binbin Wu Link: https://lore.kernel.org/all/20260430020953.1405535-1-binbin.wu@linux.intel.com --- arch/x86/include/asm/processor.h | 1 + arch/x86/kernel/cpu/microcode/amd.c | 4 ++-- arch/x86/kernel/cpu/microcode/core.c | 22 ++++++++++------------ arch/x86/kernel/cpu/microcode/intel.c | 3 +++ arch/x86/kernel/cpu/microcode/internal.h | 1 - 5 files changed, 16 insertions(+), 15 deletions(-) diff --git a/arch/x86/include/asm/processor.h b/arch/x86/include/asm/processor.h index 10b5355b323e..67dd932305db 100644 --- a/arch/x86/include/asm/processor.h +++ b/arch/x86/include/asm/processor.h @@ -733,6 +733,7 @@ bool xen_set_default_idle(void); #endif void __noreturn stop_this_cpu(void *dummy); +extern bool x86_hypervisor_present; void microcode_check(struct cpuinfo_x86 *prev_info); void store_cpu_caps(struct cpuinfo_x86 *info); diff --git a/arch/x86/kernel/cpu/microcode/amd.c b/arch/x86/kernel/cpu/microcode/amd.c index e533881284a1..5c0afae75e9f 100644 --- a/arch/x86/kernel/cpu/microcode/amd.c +++ b/arch/x86/kernel/cpu/microcode/amd.c @@ -322,7 +322,7 @@ static u32 get_patch_level(void) { u32 rev, dummy __always_unused; - if (IS_ENABLED(CONFIG_MICROCODE_DBG) && hypervisor_present) { + if (IS_ENABLED(CONFIG_MICROCODE_DBG) && x86_hypervisor_present) { int cpu = smp_processor_id(); if (!microcode_rev[cpu]) { @@ -714,7 +714,7 @@ static bool __apply_microcode_amd(struct microcode_amd *mc, u32 *cur_rev, invlpg(p_addr_end); } - if (IS_ENABLED(CONFIG_MICROCODE_DBG) && hypervisor_present) + if (IS_ENABLED(CONFIG_MICROCODE_DBG) && x86_hypervisor_present) microcode_rev[smp_processor_id()] = mc->hdr.patch_id; /* verify patch application was successful */ diff --git a/arch/x86/kernel/cpu/microcode/core.c b/arch/x86/kernel/cpu/microcode/core.c index 651202e6fefb..45ca406a8112 100644 --- a/arch/x86/kernel/cpu/microcode/core.c +++ b/arch/x86/kernel/cpu/microcode/core.c @@ -57,7 +57,7 @@ bool force_minrev = IS_ENABLED(CONFIG_MICROCODE_LATE_FORCE_MINREV); u32 base_rev; u32 microcode_rev[NR_CPUS] = {}; -bool hypervisor_present; +bool __ro_after_init x86_hypervisor_present; /* * Synchronization. @@ -118,14 +118,9 @@ bool __init microcode_loader_disabled(void) /* * Disable when: * - * 1) The CPU does not support CPUID. - */ - if (!cpuid_feature()) { - dis_ucode_ldr = true; - return dis_ucode_ldr; - } - - /* + * 1) The CPU does not support CPUID, detected below in + * load_ucode_bsp(). + * * 2) Bit 31 in CPUID[1]:ECX is clear * The bit is reserved for hypervisor use. This is still not * completely accurate as XEN PV guests don't see that CPUID bit @@ -135,9 +130,7 @@ bool __init microcode_loader_disabled(void) * 3) Certain AMD patch levels are not allowed to be * overwritten. */ - hypervisor_present = native_cpuid_ecx(1) & BIT(31); - - if ((hypervisor_present && !IS_ENABLED(CONFIG_MICROCODE_DBG)) || + if ((x86_hypervisor_present && !IS_ENABLED(CONFIG_MICROCODE_DBG)) || amd_check_current_patch_level()) dis_ucode_ldr = true; @@ -179,6 +172,11 @@ void __init load_ucode_bsp(void) early_parse_cmdline(); + if (!cpuid_feature()) + dis_ucode_ldr = true; + else + x86_hypervisor_present = native_cpuid_ecx(1) & BIT(31); + if (microcode_loader_disabled()) return; diff --git a/arch/x86/kernel/cpu/microcode/intel.c b/arch/x86/kernel/cpu/microcode/intel.c index 37ac4afe0972..a4c0a0cf928b 100644 --- a/arch/x86/kernel/cpu/microcode/intel.c +++ b/arch/x86/kernel/cpu/microcode/intel.c @@ -138,6 +138,9 @@ u32 intel_get_platform_id(void) { unsigned int val[2]; + if (x86_hypervisor_present) + return 0; + /* * This can be called early. Use CPUID directly instead of * relying on cpuinfo_x86 which may not be fully initialized. diff --git a/arch/x86/kernel/cpu/microcode/internal.h b/arch/x86/kernel/cpu/microcode/internal.h index 3b93c0676b4f..a10b547eda1e 100644 --- a/arch/x86/kernel/cpu/microcode/internal.h +++ b/arch/x86/kernel/cpu/microcode/internal.h @@ -48,7 +48,6 @@ extern struct early_load_data early_data; extern struct ucode_cpu_info ucode_cpu_info[]; extern u32 microcode_rev[NR_CPUS]; extern u32 base_rev; -extern bool hypervisor_present; struct cpio_data find_microcode_in_initrd(const char *path); From 030675aa54cf757769b3db65642433d626b3ed7c Mon Sep 17 00:00:00 2001 From: Chaitanya Sabnis Date: Tue, 26 May 2026 15:52:40 +0530 Subject: [PATCH 181/337] i2c: davinci: fix division by zero on missing clock-frequency When the 'clock-frequency' property is missing from the device tree, the driver falls back to DAVINCI_I2C_DEFAULT_BUS_FREQ. However, this macro was defined in kHz (100), whereas the device tree property is expected in Hz. The probe function divided the fallback value by 1000, causing integer truncation that resulted in dev->bus_freq = 0. This triggered a deterministic division-by-zero kernel panic when calculating clock dividers later in the probe sequence. Fix this by redefining DAVINCI_I2C_DEFAULT_BUS_FREQ in Hz (100000) to match the expected device tree property unit, allowing the existing division logic to work correctly for both cases. Fixes: b04ce6385979 ("i2c: davinci: kill platform data") Reported-by: Sashiko Closes: https://lore.kernel.org/all/20260514044726.57297C2BCB7@smtp.kernel.org/ Signed-off-by: Chaitanya Sabnis Cc: # v6.14+ Reviewed-by: Bartosz Golaszewski Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20260526102240.4949-1-chaitanya.msabnis@gmail.com --- drivers/i2c/busses/i2c-davinci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-davinci.c b/drivers/i2c/busses/i2c-davinci.c index a773ba082321..66c23535656b 100644 --- a/drivers/i2c/busses/i2c-davinci.c +++ b/drivers/i2c/busses/i2c-davinci.c @@ -117,7 +117,7 @@ /* timeout for pm runtime autosuspend */ #define DAVINCI_I2C_PM_TIMEOUT 1000 /* ms */ -#define DAVINCI_I2C_DEFAULT_BUS_FREQ 100 +#define DAVINCI_I2C_DEFAULT_BUS_FREQ 100000 struct davinci_i2c_dev { struct device *dev; From 0e60dafe97eca61721f3db456f97d97a80c6c8ae Mon Sep 17 00:00:00 2001 From: Ali Ganiyev Date: Mon, 25 May 2026 10:23:47 +0900 Subject: [PATCH 182/337] ksmbd: OOB read regression in smb_check_perm_dacl() ACE-walk loops Commit d07b26f39246 ("ksmbd: require minimum ACE size in smb_check_perm_dacl()") introduced a transposed bounds check: if (offsetof(struct smb_ace, sid) + aces_size < CIFS_SID_BASE_SIZE) Since offsetof(..sid) is 8 and CIFS_SID_BASE_SIZE is 8, this evaluates to `aces_size < 0`. Because `aces_size` is always non-negative, this check becomes dead code and never breaks the loop. Worse, that commit removed the old 4-byte guard, meaning the loop now reads `ace->size` (offset 2) even when `aces_size` is 0-3 bytes. This re-opens a 2-byte heap out-of-bounds (OOB) read past the pntsd allocation during subsequent SMB2_CREATE operations. Fix this by properly transposing the comparison to require at least 16 bytes (8-byte offset + 8-byte SID base), matching the correct form used in smb_inherit_dacl(). Fixes: d07b26f39246 ("ksmbd: require minimum ACE size in smb_check_perm_dacl()") Cc: stable@vger.kernel.org Signed-off-by: Ali Ganiyev Acked-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/smbacl.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/fs/smb/server/smbacl.c b/fs/smb/server/smbacl.c index c2d9be52a311..664b1b4a3233 100644 --- a/fs/smb/server/smbacl.c +++ b/fs/smb/server/smbacl.c @@ -1446,8 +1446,8 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path, ace = (struct smb_ace *)((char *)pdacl + sizeof(struct smb_acl)); aces_size = acl_size - sizeof(struct smb_acl); for (i = 0; i < le16_to_cpu(pdacl->num_aces); i++) { - if (offsetof(struct smb_ace, sid) + - aces_size < CIFS_SID_BASE_SIZE) + if (aces_size < offsetof(struct smb_ace, sid) + + CIFS_SID_BASE_SIZE) break; ace_size = le16_to_cpu(ace->size); if (ace_size > aces_size || @@ -1467,8 +1467,8 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path, ace = (struct smb_ace *)((char *)pdacl + sizeof(struct smb_acl)); aces_size = acl_size - sizeof(struct smb_acl); for (i = 0; i < le16_to_cpu(pdacl->num_aces); i++) { - if (offsetof(struct smb_ace, sid) + - aces_size < CIFS_SID_BASE_SIZE) + if (aces_size < offsetof(struct smb_ace, sid) + + CIFS_SID_BASE_SIZE) break; ace_size = le16_to_cpu(ace->size); if (ace_size > aces_size || From 2f15dcd0d4b502c704a52f5c7de128b163677978 Mon Sep 17 00:00:00 2001 From: Aleksandr Golovnya Date: Tue, 26 May 2026 01:50:18 +0700 Subject: [PATCH 183/337] ksmbd: release ksmbd_inode ref via ksmbd_inode_put on lookup paths ksmbd_query_inode_status() and ksmbd_lookup_fd_inode() both take a reference on a ksmbd_inode via __ksmbd_inode_lookup() (which performs atomic_inc_not_zero()) and later release it using a bare atomic_dec(&ci->m_count). Unlike ksmbd_inode_put(), a bare atomic_dec() does not check whether the reference count has reached zero, so if the caller happens to drop the last reference, the ksmbd_inode is leaked: it stays in the global inode hash table with m_count == 0, future __ksmbd_inode_lookup() calls reject it via atomic_inc_not_zero(), and ksmbd_inode_free() is never invoked. The race is: T1: __ksmbd_inode_lookup() -> atomic_inc_not_zero(): m_count = 2 T2: ksmbd_inode_put() -> atomic_dec_and_test(): m_count = 1 (not freed) T1: atomic_dec(&ci->m_count) -> m_count = 0 return (LEAK) In ksmbd_lookup_fd_inode() the matched-fp path (which now also uses ksmbd_inode_put()) cannot currently reach m_count == 0 because the matched ksmbd_file holds its own reference on ci, but converting it to the proper API keeps the three call sites consistent and avoids future regressions if the locking changes. Because ksmbd_inode_put() may free the ksmbd_inode if this drops the last reference, the call must happen after up_read(&ci->m_lock) on the two affected paths in ksmbd_lookup_fd_inode(). On the no-match path this is a pure reordering; on the matched path ksmbd_fp_get() is moved above the unlock so that the returned ksmbd_file is pinned before the inode reference is released. Signed-off-by: Aleksandr Golovnya Acked-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/vfs_cache.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c index 5a232d94f567..4d2d33df6231 100644 --- a/fs/smb/server/vfs_cache.c +++ b/fs/smb/server/vfs_cache.c @@ -217,7 +217,7 @@ int ksmbd_query_inode_status(struct dentry *dentry) ret = KSMBD_INODE_STATUS_OK; up_read(&ci->m_lock); - atomic_dec(&ci->m_count); + ksmbd_inode_put(ci); return ret; } @@ -719,14 +719,14 @@ struct ksmbd_file *ksmbd_lookup_fd_inode(struct dentry *dentry) down_read(&ci->m_lock); list_for_each_entry(lfp, &ci->m_fp_list, node) { if (inode == file_inode(lfp->filp)) { - atomic_dec(&ci->m_count); lfp = ksmbd_fp_get(lfp); up_read(&ci->m_lock); + ksmbd_inode_put(ci); return lfp; } } - atomic_dec(&ci->m_count); up_read(&ci->m_lock); + ksmbd_inode_put(ci); return NULL; } From cc57232cae23c0df91b4a59d0f519141ce9b5b02 Mon Sep 17 00:00:00 2001 From: Sean Shen Date: Tue, 26 May 2026 22:07:16 +0900 Subject: [PATCH 184/337] ksmbd: fix FSCTL permission bypass by adding a permission check for FSCTL_SET_SPARSE FSCTL_SET_SPARSE in fsctl_set_sparse() modifies the file's sparse attribute and saves it through xattr without any permission checks. This exposes two issues: 1) A client on a read-only share can change the sparse attribute on files it opened, even though the share is read-only. Other FSCTL write operations already check test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE), but FSCTL_SET_SPARSE does not. 2) Even on writable shares, clients without FILE_WRITE_DATA or FILE_WRITE_ATTRIBUTES access should not modify the sparse attribute. Similar handle-level checks exist in other functions but are missing here. Add both share-level writable check and per-handle access check. Use goto out on error to avoid leaking file references. Fixes: e2f34481b24d ("cifsd: add server-side procedures for SMB3") Cc: Namjae Jeon Cc: Sergey Senozhatsky Cc: Steve French Signed-off-by: Sean Shen Acked-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/smb2pdu.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index 5128a693aca6..620bcfbbfd92 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -8202,9 +8202,20 @@ static inline int fsctl_set_sparse(struct ksmbd_work *work, u64 id, int ret = 0; __le32 old_fattr; + if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { + ksmbd_debug(SMB, "User does not have write permission\n"); + return -EACCES; + } + fp = ksmbd_lookup_fd_fast(work, id); if (!fp) return -ENOENT; + + if (!(fp->daccess & (FILE_WRITE_DATA_LE | FILE_WRITE_ATTRIBUTES_LE))) { + ret = -EACCES; + goto out; + } + idmap = file_mnt_idmap(fp->filp); old_fattr = fp->f_ci->m_fattr; From f657a6a3ba4c20bc01f5be3752d53498ee1bfe35 Mon Sep 17 00:00:00 2001 From: Balasubramani Vivekanandan Date: Fri, 22 May 2026 22:05:32 +0530 Subject: [PATCH 185/337] drm/xe: Restore IDLEDLY regiter on engine reset Wa_16023105232 programs the register IDLEDLY. The register is reset whenever the engine is reset. Therefore it should be added to the GuC save-restore register list for it to be restored after reset. Fixes: 7c53ff050ba8 ("drm/xe: Apply Wa_16023105232") Reviewed-by: Matt Roper Link: https://patch.msgid.link/20260522163531.1365540-2-balasubramani.vivekanandan@intel.com Signed-off-by: Balasubramani Vivekanandan (cherry picked from commit df1cfe24743a93b71eab27687e148ab8ae9b69e3) Signed-off-by: Rodrigo Vivi --- drivers/gpu/drm/xe/xe_guc_ads.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/gpu/drm/xe/xe_guc_ads.c b/drivers/gpu/drm/xe/xe_guc_ads.c index 2b835d48b565..5760251cb685 100644 --- a/drivers/gpu/drm/xe/xe_guc_ads.c +++ b/drivers/gpu/drm/xe/xe_guc_ads.c @@ -767,6 +767,11 @@ static unsigned int guc_mmio_regset_write(struct xe_guc_ads *ads, } } + if (XE_GT_WA(hwe->gt, 16023105232)) + guc_mmio_regset_write_one(ads, regset_map, + RING_IDLEDLY(hwe->mmio_base), + count++); + return count; } From 2e7f55eb408c3f72ee1957a0d0ad11d8648a6379 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Sun, 17 May 2026 09:17:42 -0400 Subject: [PATCH 186/337] drm/amdgpu: fix lock leak on ENOMEM in AMDGPU_GEM_OP_GET_MAPPING_INFO The AMDGPU_GEM_OP_GET_MAPPING_INFO branch of amdgpu_gem_op_ioctl() holds three cleanup-tracked resources before calling kvcalloc(): the drm_gem_object reference from drm_gem_object_lookup(), the drm_exec lock on the looked-up GEM via drm_exec_lock_obj(), and the drm_exec lock on the per-process VM root page directory via amdgpu_vm_lock_pd(). All three are released by the out_exec label that every other error path in this function jumps to. The kvcalloc() failure path returns -ENOMEM directly, skipping out_exec and leaking all three. The leaked per-process VM root PD dma_resv lock is the load-bearing leak: any subsequent operation on the same VM (further GEM ops, command-submission, eviction, TTM shrinker callbacks) blocks on the held lock. DRM_IOCTL_AMDGPU_GEM_OP is DRM_AUTH | DRM_RENDER_ALLOW, so this is an unprivileged-local denial of service against the caller's GPU context, reachable by any process with /dev/dri/renderD* access. Route the failure through out_exec so drm_exec_fini() and drm_gem_object_put() run. Reproduced on stock 7.0.0-10, Ryzen 7 5700U / Radeon Vega (Lucienne): the failing ioctl returns -ENOMEM and a second GET_MAPPING_INFO on the same fd then blocks in drm_exec_lock_obj() on the leaked dma_resv. SIGKILL on the caller does not reap the task; the fd-release path during process exit goes through amdgpu_gem_object_close() -> drm_exec_prepare_obj() on the same lock, leaving the task in D state until the box is rebooted. The patched kernel was not rebuilt and re-tested on this hardware; the fix is mechanical. Tested on a single Lucienne / Vega box only. Ziyi Guo posted an independent INT_MAX-bound check for args->num_entries in the same branch [1]; the two patches are complementary and can land in either order. Fixes: 4d82724f7f2b ("drm/amdgpu: Add mapping info option for GEM_OP ioctl") Link: https://lore.kernel.org/all/20260208000255.4073363-1-n7l8m4@u.northwestern.edu/ # [1] Signed-off-by: Michael Bommarito Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Alex Deucher (cherry picked from commit b69d3256d79de15f54c322986ff4da68f1d65b0a) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c index 123d4a09114d..06dd2e8a5b47 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c @@ -1094,8 +1094,10 @@ int amdgpu_gem_op_ioctl(struct drm_device *dev, void *data, * be retried. */ vm_entries = kvcalloc(args->num_entries, sizeof(*vm_entries), GFP_KERNEL); - if (!vm_entries) - return -ENOMEM; + if (!vm_entries) { + r = -ENOMEM; + goto out_exec; + } amdgpu_vm_bo_va_for_each_valid_mapping(bo_va, mapping) { if (num_mappings < args->num_entries) { From a1ba4594232c87c3b8defd6f89a2e40f8b08395d Mon Sep 17 00:00:00 2001 From: Ziyi Guo Date: Sun, 8 Feb 2026 00:02:55 +0000 Subject: [PATCH 187/337] drm/amdgpu: check num_entries in GEM_OP GET_MAPPING_INFO kvcalloc(args->num_entries, sizeof(*vm_entries), GFP_KERNEL) at amdgpu_gem.c:1050 uses the user-supplied num_entries directly without any upper bounds check. Since num_entries is a __u32 and sizeof(drm_amdgpu_gem_vm_entry) is 32 bytes, a large num_entries produces an allocation exceeding INT_MAX, triggering WARNING in __kvmalloc_node_noprof(), causing a kernel WARNING, TAINT_WARN, and panic on CONFIG_PANIC_ON_WARN=y systems. Add a size bounds check before we invoke the kvzalloc() to reject oversized num_entries early with -EINVAL. Fixes: 4d82724f7f2b ("drm/amdgpu: Add mapping info option for GEM_OP ioctl") Signed-off-by: Ziyi Guo Signed-off-by: Alex Deucher (cherry picked from commit 1fe7bf5457f6efd7be60b17e23163ba54341d73d) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c index 06dd2e8a5b47..fe6d988e7f24 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c @@ -1093,6 +1093,11 @@ int amdgpu_gem_op_ioctl(struct drm_device *dev, void *data, * If that number is larger than the size of the array, the ioctl must * be retried. */ + if (args->num_entries > INT_MAX / sizeof(*vm_entries)) { + r = -EINVAL; + goto out_exec; + } + vm_entries = kvcalloc(args->num_entries, sizeof(*vm_entries), GFP_KERNEL); if (!vm_entries) { r = -ENOMEM; From 4a03d23ce6ad474cb15862563bc9132e16e3e31e Mon Sep 17 00:00:00 2001 From: Sunil Khatri Date: Tue, 19 May 2026 15:02:00 +0530 Subject: [PATCH 188/337] drm/amdgpu/userq: Fix doorbell object cleanup of queue MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Unpin and unref the door bell obj if queue creation fails before initialization is complete. Signed-off-by: Sunil Khatri Reviewed-by: Christian König Signed-off-by: Alex Deucher (cherry picked from commit 8c7506f7ba945f21e5abe7f8eac0a3acca6b5330) --- drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c index f070ea37d918..2301a44a03b1 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c @@ -787,7 +787,7 @@ amdgpu_userq_create(struct drm_file *filp, union drm_amdgpu_userq *args) r = uq_funcs->mqd_create(queue, &args->in); if (r) { drm_file_err(uq_mgr->file, "Failed to create Queue\n"); - goto clean_mapping; + goto clean_doorbell_bo; } /* Update VM owner at userq submit-time for page-fault attribution. */ @@ -808,7 +808,7 @@ amdgpu_userq_create(struct drm_file *filp, union drm_amdgpu_userq *args) if (r) { drm_file_err(uq_mgr->file, "Failed to map Queue\n"); mutex_unlock(&uq_mgr->userq_mutex); - goto clean_doorbell; + goto erase_doorbell; } } @@ -831,10 +831,15 @@ amdgpu_userq_create(struct drm_file *filp, union drm_amdgpu_userq *args) args->out.queue_id = qid; return 0; -clean_doorbell: +erase_doorbell: xa_erase_irq(&adev->userq_doorbell_xa, index); clean_mqd: uq_funcs->mqd_destroy(queue); +clean_doorbell_bo: + amdgpu_bo_reserve(queue->db_obj.obj, true); + amdgpu_bo_unpin(queue->db_obj.obj); + amdgpu_bo_unreserve(queue->db_obj.obj); + amdgpu_bo_unref(&queue->db_obj.obj); clean_mapping: amdgpu_bo_reserve(fpriv->vm.root.bo, true); amdgpu_userq_buffer_vas_list_cleanup(adev, queue); From ba4c0ff47ee098c8e17d25f9dc050e6276bf9979 Mon Sep 17 00:00:00 2001 From: Sunil Khatri Date: Tue, 19 May 2026 15:12:42 +0530 Subject: [PATCH 189/337] drm/amdgpu/userq: Fix the mutex_init cleanup for fence_drv_lock MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit mutex fence_drv_lock is destroyed in amdgpu_userq_fence_driver_free also in one of the jump condition mutex_destroy is also called leading to double mutex_destroy. So rearranging the code so amdgpu_userq_fence_driver_free takes care of the clean up along with mutex_destroy. Signed-off-by: Sunil Khatri Reviewed-by: Christian König Signed-off-by: Alex Deucher (cherry picked from commit 384dbef269d101e5b671fc7b942c56734cd1d186) --- drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c index 2301a44a03b1..d6390cc5a798 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c @@ -748,12 +748,12 @@ amdgpu_userq_create(struct drm_file *filp, union drm_amdgpu_userq *args) INIT_DELAYED_WORK(&queue->hang_detect_work, amdgpu_userq_hang_detect_work); - mutex_init(&queue->fence_drv_lock); - xa_init_flags(&queue->fence_drv_xa, XA_FLAGS_ALLOC); r = amdgpu_userq_fence_driver_alloc(adev, &queue->fence_drv); if (r) goto free_queue; + xa_init_flags(&queue->fence_drv_xa, XA_FLAGS_ALLOC); + mutex_init(&queue->fence_drv_lock); /* Make sure the queue can actually run with those virtual addresses. */ r = amdgpu_bo_reserve(fpriv->vm.root.bo, false); if (r) @@ -844,7 +844,6 @@ amdgpu_userq_create(struct drm_file *filp, union drm_amdgpu_userq *args) amdgpu_bo_reserve(fpriv->vm.root.bo, true); amdgpu_userq_buffer_vas_list_cleanup(adev, queue); amdgpu_bo_unreserve(fpriv->vm.root.bo); - mutex_destroy(&queue->fence_drv_lock); free_fence_drv: amdgpu_userq_fence_driver_free(queue); free_queue: From ec78a85d95e9c37b6ca16d6ed1639fa64d5dd6dc Mon Sep 17 00:00:00 2001 From: Ivan Lipski Date: Thu, 14 May 2026 11:53:50 -0400 Subject: [PATCH 190/337] drm/amd/display: Write REFCLK to 48MHz on DCN21 [Why&How] dccg21_init() calls dccg2_init() which hardcodes 100MHz refclk values for MICROSECOND_TIME_BASE_DIV and MILLISECOND_TIME_BASE_DIV. DCN21 uses 48MHz refclk, so the wrong values corrupt DCCG timing and cause eDP link training failure on cold boot. Write the correct 48MHz values directly instead of calling dccg2_init(). v2: Fixed typo Fixes: e6e2b956fc81 ("drm/amd/display: Add missing DCCG register entries for DCN20-DCN316") Closes: https://gitlab.freedesktop.org/drm/amd/-/work_items/5272 Closes: https://gitlab.freedesktop.org/drm/amd/-/work_items/5311 Reported-by: Max Chernoff Tested-by: Max Chernoff Signed-off-by: Ivan Lipski Acked-by: Alex Deucher Signed-off-by: Alex Deucher (cherry picked from commit 08236c3ef284cd2d110e5e3d51fc9615e551f9dc) Cc: stable@vger.kernel.org --- .../drm/amd/display/dc/dccg/dcn21/dcn21_dccg.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/dccg/dcn21/dcn21_dccg.c b/drivers/gpu/drm/amd/display/dc/dccg/dcn21/dcn21_dccg.c index c4d4eea140f3..1f23dfccf07a 100644 --- a/drivers/gpu/drm/amd/display/dc/dccg/dcn21/dcn21_dccg.c +++ b/drivers/gpu/drm/amd/display/dc/dccg/dcn21/dcn21_dccg.c @@ -105,15 +105,26 @@ static void dccg21_update_dpp_dto(struct dccg *dccg, int dpp_inst, int req_dppcl * dccg2_init() unconditionally overwrites MICROSECOND_TIME_BASE_DIV to * 0x00120264, destroying the marker before it can be read. * - * Guard the call: if the S0i3 marker is present, skip dccg2_init() so the + * Guard the call: if the S0i3 marker is present, skip init so the * WA can function correctly. bios_golden_init() will handle init in that case. + * + * DCN21 uses 48MHz refclk, not 100MHz, so we must explicitly set the correct + * values (48MHz is taken from rn_clk_mgr_construct()). */ static void dccg21_init(struct dccg *dccg) { + struct dcn_dccg *dccg_dcn = TO_DCN_DCCG(dccg); + if (dccg2_is_s0i3_golden_init_wa_done(dccg)) return; - dccg2_init(dccg); + /* 48MHz refclk from rn_clk_mgr_construct() */ + REG_WRITE(MICROSECOND_TIME_BASE_DIV, 0x00120230); + REG_WRITE(MILLISECOND_TIME_BASE_DIV, 0x0010bb80); + REG_WRITE(DISPCLK_FREQ_CHANGE_CNTL, 0x0e01003c); + + if (REG(REFCLK_CNTL)) + REG_WRITE(REFCLK_CNTL, 0); } static const struct dccg_funcs dccg21_funcs = { From e984d61d92e702096058f0f828f4b2b8563b88ce Mon Sep 17 00:00:00 2001 From: Eric Huang Date: Thu, 7 May 2026 15:51:49 -0400 Subject: [PATCH 191/337] drm/amdkfd: fix NULL pointer bug in svm_range_set_attr The process_info could be NULL if user doesn't call kfd_ioctl_acquire_vm before calling kfd_ioctl_svm. Signed-off-by: Eric Huang Reviewed-by: Alex Deucher Signed-off-by: Alex Deucher (cherry picked from commit 83a26c812e0529eb040d31a76f73e33e637243d4) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdkfd/kfd_svm.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_svm.c b/drivers/gpu/drm/amd/amdkfd/kfd_svm.c index 35ec67d9739b..3841943da5ec 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_svm.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_svm.c @@ -3732,6 +3732,9 @@ svm_range_set_attr(struct kfd_process *p, struct mm_struct *mm, svms = &p->svms; + if (!process_info) + return -EINVAL; + mutex_lock(&process_info->lock); svm_range_list_lock_and_flush_work(svms, mm); From d8d9c820405eb1fcbde959de8898ad7d716a2d7b Mon Sep 17 00:00:00 2001 From: Sunil Khatri Date: Mon, 18 May 2026 17:42:15 +0530 Subject: [PATCH 192/337] drm/amdgpu: simplify return value in amdgpu_userq_get_doorbell_index MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit amdgpu_userq_get_doorbell_index returns a uint64 type index as well as a int type failure values. Simplifying this and using a int type return value and getting the index in input pointer of type uint64 type. Also since it's used at once place making it static would be better. Signed-off-by: Sunil Khatri Reviewed-by: Christian König Signed-off-by: Alex Deucher (cherry picked from commit e947ec9d0529d5f93dbdb33cd197347f6a7b2922) --- drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c | 21 +++++++++++---------- drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h | 4 ---- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c index d6390cc5a798..34c0d9ee94f2 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c @@ -536,12 +536,13 @@ void amdgpu_userq_destroy_object(struct amdgpu_userq_mgr *uq_mgr, amdgpu_bo_unref(&userq_obj->obj); } -uint64_t +static int amdgpu_userq_get_doorbell_index(struct amdgpu_userq_mgr *uq_mgr, struct amdgpu_db_info *db_info, - struct drm_file *filp) + struct drm_file *filp, + u64 *index) { - uint64_t index; + u64 doorbell_index; struct drm_gem_object *gobj; struct amdgpu_userq_obj *db_obj = db_info->db_obj; int r, db_size; @@ -588,12 +589,13 @@ amdgpu_userq_get_doorbell_index(struct amdgpu_userq_mgr *uq_mgr, goto unpin_bo; } - index = amdgpu_doorbell_index_on_bar(uq_mgr->adev, db_obj->obj, - db_info->doorbell_offset, db_size); + doorbell_index = amdgpu_doorbell_index_on_bar(uq_mgr->adev, db_obj->obj, + db_info->doorbell_offset, db_size); drm_dbg_driver(adev_to_drm(uq_mgr->adev), - "[Usermode queues] doorbell index=%lld\n", index); + "[Usermode queues] doorbell index=%lld\n", doorbell_index); amdgpu_bo_unreserve(db_obj->obj); - return index; + *index = doorbell_index; + return 0; unpin_bo: amdgpu_bo_unpin(db_obj->obj); @@ -776,10 +778,9 @@ amdgpu_userq_create(struct drm_file *filp, union drm_amdgpu_userq *args) db_info.doorbell_handle = queue->doorbell_handle; db_info.db_obj = &queue->db_obj; db_info.doorbell_offset = args->in.doorbell_offset; - index = amdgpu_userq_get_doorbell_index(uq_mgr, &db_info, filp); - if (index == (uint64_t)-EINVAL) { + r = amdgpu_userq_get_doorbell_index(uq_mgr, &db_info, filp, &index); + if (r) { drm_file_err(uq_mgr->file, "Failed to get doorbell for queue\n"); - r = -EINVAL; goto clean_mapping; } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h index 49b33e2d6932..033b8a0de6b1 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h @@ -163,10 +163,6 @@ void amdgpu_userq_evict(struct amdgpu_userq_mgr *uq_mgr); void amdgpu_userq_ensure_ev_fence(struct amdgpu_userq_mgr *userq_mgr, struct amdgpu_eviction_fence_mgr *evf_mgr); -uint64_t amdgpu_userq_get_doorbell_index(struct amdgpu_userq_mgr *uq_mgr, - struct amdgpu_db_info *db_info, - struct drm_file *filp); - u32 amdgpu_userq_get_supported_ip_mask(struct amdgpu_device *adev); bool amdgpu_userq_enabled(struct drm_device *dev); From cedee93d43f893ce67e39b57c67240965c7c5a69 Mon Sep 17 00:00:00 2001 From: Sunil Khatri Date: Mon, 18 May 2026 18:33:00 +0530 Subject: [PATCH 193/337] drm/amdgpu/userq: add amdgpu_bo_unpin when amdgpu_ttm_alloc_gart fails MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Unpin the wptr_obj->obj when amdgpu_ttm_alloc_gart fails. Signed-off-by: Sunil Khatri Reviewed-by: Christian König Signed-off-by: Alex Deucher (cherry picked from commit d8145c437ccdc2d91c579787290f82788172bea0) --- drivers/gpu/drm/amd/amdgpu/mes_userqueue.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c b/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c index 5b4121ddc78c..026940fad524 100644 --- a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c +++ b/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c @@ -81,7 +81,7 @@ mes_userq_create_wptr_mapping(struct amdgpu_device *adev, ret = amdgpu_ttm_alloc_gart(&wptr_obj->obj->tbo); if (ret) { DRM_ERROR("Failed to bind bo to GART. ret %d\n", ret); - goto fail_map; + goto fail_alloc_gart; } queue->wptr_obj.gpu_addr = amdgpu_bo_gpu_offset(wptr_obj->obj); @@ -89,6 +89,8 @@ mes_userq_create_wptr_mapping(struct amdgpu_device *adev, drm_exec_fini(&exec); return 0; +fail_alloc_gart: + amdgpu_bo_unpin(wptr_obj->obj); fail_map: amdgpu_bo_unref(&wptr_obj->obj); fail_lock: From a00caed2302c604c19a5cab781e34d7ba4fa7558 Mon Sep 17 00:00:00 2001 From: Sunil Khatri Date: Mon, 18 May 2026 18:55:25 +0530 Subject: [PATCH 194/337] drm/amdgpu/userq: reserve root bo without interruption MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix the code to make it an uninterruptible reservation for root bo. Signed-off-by: Sunil Khatri Reviewed-by: Christian König Signed-off-by: Alex Deucher (cherry picked from commit d409ab4e387d94b2e593d558b54b7bfd315e0e75) --- drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c index 34c0d9ee94f2..5da107dffcce 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c @@ -620,11 +620,7 @@ amdgpu_userq_destroy(struct amdgpu_userq_mgr *uq_mgr, struct amdgpu_usermode_que /* Cancel any pending hang detection work and cleanup */ cancel_delayed_work_sync(&queue->hang_detect_work); - r = amdgpu_bo_reserve(vm->root.bo, false); - if (r) { - drm_file_err(uq_mgr->file, "Failed to reserve root bo during userqueue destroy\n"); - return r; - } + amdgpu_bo_reserve(vm->root.bo, true); amdgpu_userq_buffer_vas_list_cleanup(adev, queue); amdgpu_bo_unreserve(vm->root.bo); From cf4aafdccefccc7f8236fed028d06725246e289e Mon Sep 17 00:00:00 2001 From: Sunil Khatri Date: Mon, 18 May 2026 19:58:08 +0530 Subject: [PATCH 195/337] drm/amdgpu/userq: make sure queue is valid in the hang_detect_work MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Thread 1: Running amdgpu_userq_destroy which eventually remove the queue from door bell and set userq_mgr = NULL. Thread2: An interrupt might have scheduled the hang_detect_work which still need userq_mgr to be valid but could get an NULL ptrs. To fix that make sure we cancel the hang_detect_work again before setting userq_mgr to NULL. Along with that we also need all the queue va to remain valid till we could be running anything on the queue and hence moving the userq_va post hang_detect handler is cancelled. Signed-off-by: Sunil Khatri Reviewed-by: Christian König Signed-off-by: Alex Deucher (cherry picked from commit 1a66ceb98b137d18d303b9889f0e7d8c4db73943) --- drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c index 5da107dffcce..8d7dad1d30eb 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c @@ -427,8 +427,6 @@ static void amdgpu_userq_cleanup(struct amdgpu_usermode_queue *queue) xa_erase_irq(&adev->userq_doorbell_xa, queue->doorbell_index); amdgpu_userq_fence_driver_free(queue); queue->fence_drv = NULL; - queue->userq_mgr = NULL; - list_del(&queue->userq_va_list); up_read(&adev->reset_domain->sem); } @@ -619,11 +617,6 @@ amdgpu_userq_destroy(struct amdgpu_userq_mgr *uq_mgr, struct amdgpu_usermode_que /* Cancel any pending hang detection work and cleanup */ cancel_delayed_work_sync(&queue->hang_detect_work); - - amdgpu_bo_reserve(vm->root.bo, true); - amdgpu_userq_buffer_vas_list_cleanup(adev, queue); - amdgpu_bo_unreserve(vm->root.bo); - mutex_lock(&uq_mgr->userq_mutex); amdgpu_userq_wait_for_last_fence(queue); @@ -635,6 +628,13 @@ amdgpu_userq_destroy(struct amdgpu_userq_mgr *uq_mgr, struct amdgpu_usermode_que amdgpu_userq_cleanup(queue); mutex_unlock(&uq_mgr->userq_mutex); + cancel_delayed_work_sync(&queue->hang_detect_work); + amdgpu_bo_reserve(vm->root.bo, true); + amdgpu_userq_buffer_vas_list_cleanup(adev, queue); + amdgpu_bo_unreserve(vm->root.bo); + list_del(&queue->userq_va_list); + queue->userq_mgr = NULL; + amdgpu_bo_reserve(queue->db_obj.obj, true); amdgpu_bo_unpin(queue->db_obj.obj); amdgpu_bo_unreserve(queue->db_obj.obj); From 0fb4a8e64a9db74eeda8da7d0b78985392ae483b Mon Sep 17 00:00:00 2001 From: "Stanley.Yang" Date: Mon, 11 May 2026 16:49:19 +0800 Subject: [PATCH 196/337] drm/amdgpu: fix potential overflow in fs_info.debugfs_name Use snprintf() with sizeof(fs_info.debugfs_name) so a long RAS block name plus the "_err_inject" suffix cannot overflow the 32-byte buffer. Signed-off-by: Stanley.Yang Reviewed-by: Tao Zhou Signed-off-by: Alex Deucher (cherry picked from commit 1a58070fda26857a8f6acc0ab05428e60d5c6844) --- drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c index 6c644cfe6695..fc9f3adf9912 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c @@ -2280,7 +2280,8 @@ void amdgpu_ras_debugfs_create_all(struct amdgpu_device *adev) list_for_each_entry(obj, &con->head, node) { if (amdgpu_ras_is_supported(adev, obj->head.block) && (obj->attr_inuse == 1)) { - sprintf(fs_info.debugfs_name, "%s_err_inject", + snprintf(fs_info.debugfs_name, sizeof(fs_info.debugfs_name), + "%s_err_inject", get_ras_block_str(&obj->head)); fs_info.head = obj->head; amdgpu_ras_debugfs_create(adev, &fs_info, dir); From 6842b6a4b72da9b2906ffc5ca9d846ace2c54c14 Mon Sep 17 00:00:00 2001 From: David Francis Date: Thu, 14 May 2026 10:31:20 -0400 Subject: [PATCH 197/337] drm/amdkfd: Check for pdd drm file first in CRIU restore path CRIU restore ioctls are meant to be called by CRIU with no existing drm file. There's an error path for if the drm file unexpectedly exists. It was positioned so it was missing a fput(drm_file). Do that check earlier, as soon as we have the pdd. Signed-off-by: David Francis Reviewed-by: Alex Deucher Signed-off-by: Alex Deucher (cherry picked from commit 2bab781dac78916c5cc8de76345a4102449267d7) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdkfd/kfd_chardev.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c index 03b266b26738..8785f7810157 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c @@ -2300,6 +2300,11 @@ static int criu_restore_devices(struct kfd_process *p, ret = -EINVAL; goto exit; } + + if (pdd->drm_file) { + ret = -EINVAL; + goto exit; + } pdd->user_gpu_id = device_buckets[i].user_gpu_id; drm_file = fget(device_buckets[i].drm_fd); @@ -2310,11 +2315,6 @@ static int criu_restore_devices(struct kfd_process *p, goto exit; } - if (pdd->drm_file) { - ret = -EINVAL; - goto exit; - } - /* create the vm using render nodes for kfd pdd */ if (kfd_process_device_init_vm(pdd, drm_file)) { pr_err("could not init vm for given pdd\n"); From dd4f3ee535b3b0ac027f75dbf9dc5fc88733c765 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timur=20Krist=C3=B3f?= Date: Tue, 19 May 2026 10:41:54 +0200 Subject: [PATCH 198/337] drm/amd/pm/si: Disregard vblank time when no displays are connected MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When no displays are connected, there is no vblank happening so the power management code shouldn't worry about it. This fixes a regression that caused the memory clock to be stuck at maximum when there were no displays connected to a SI GPU. Fixes: 9003a0746864 ("drm/amd/pm: Treat zero vblank time as too short in si_dpm (v3)") Fixes: 9d73b107a61b ("drm/amd/pm: Use pm_display_cfg in legacy DPM (v2)") Reviewed-by: Alex Deucher Tested-by: Jeremy Klarenbeek Signed-off-by: Timur Kristóf Signed-off-by: Alex Deucher (cherry picked from commit 6d87e0199f7b83735b56e422d59f170a201897a8) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.c b/drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.c index 36942467d4ad..c3aff5d0c53d 100644 --- a/drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.c +++ b/drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.c @@ -3076,6 +3076,10 @@ static bool si_dpm_vblank_too_short(void *handle) /* we never hit the non-gddr5 limit so disable it */ u32 switch_limit = adev->gmc.vram_type == AMDGPU_VRAM_TYPE_GDDR5 ? 450 : 0; + /* Disregard vblank time when there are no displays connected */ + if (!adev->pm.pm_display_cfg.num_display) + return false; + /* Consider zero vblank time too short and disable MCLK switching. * Note that the vblank time is set to maximum when no displays are attached, * so we'll still enable MCLK switching in that case. From ca8e7a119a2e4045324cffb8f9f58bedcc3dc928 Mon Sep 17 00:00:00 2001 From: Sunil Khatri Date: Wed, 20 May 2026 16:13:09 +0530 Subject: [PATCH 199/337] drm/amdgpu/userq: remove amdgpu_userq_create/destroy_object wrapper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove the amdgpu_userq_create/destroy_object wrappers and use directly the kernel bo allocation function which does all the things which are done in wrapper. Signed-off-by: Sunil Khatri Suggested-by: Christian König Reviewed-by: Christian König Signed-off-by: Alex Deucher (cherry picked from commit deb02080ca5d3f015cf71e56067a39ef2f141998) --- drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c | 67 ---------------------- drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h | 7 --- drivers/gpu/drm/amd/amdgpu/mes_userqueue.c | 29 +++++++--- 3 files changed, 21 insertions(+), 82 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c index 8d7dad1d30eb..a93f5c238e55 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c @@ -465,74 +465,7 @@ amdgpu_userq_ensure_ev_fence(struct amdgpu_userq_mgr *uq_mgr, dma_fence_put(ev_fence); } -int amdgpu_userq_create_object(struct amdgpu_userq_mgr *uq_mgr, - struct amdgpu_userq_obj *userq_obj, - int size) -{ - struct amdgpu_device *adev = uq_mgr->adev; - struct amdgpu_bo_param bp; - int r; - memset(&bp, 0, sizeof(bp)); - bp.byte_align = PAGE_SIZE; - bp.domain = AMDGPU_GEM_DOMAIN_GTT; - bp.flags = AMDGPU_GEM_CREATE_VRAM_CONTIGUOUS | - AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED; - bp.type = ttm_bo_type_kernel; - bp.size = size; - bp.resv = NULL; - bp.bo_ptr_size = sizeof(struct amdgpu_bo); - - r = amdgpu_bo_create(adev, &bp, &userq_obj->obj); - if (r) { - drm_file_err(uq_mgr->file, "Failed to allocate BO for userqueue (%d)", r); - return r; - } - - r = amdgpu_bo_reserve(userq_obj->obj, true); - if (r) { - drm_file_err(uq_mgr->file, "Failed to reserve BO to map (%d)", r); - goto free_obj; - } - - r = amdgpu_bo_pin(userq_obj->obj, AMDGPU_GEM_DOMAIN_GTT); - if (r) - goto unresv; - - r = amdgpu_ttm_alloc_gart(&(userq_obj->obj)->tbo); - if (r) { - drm_file_err(uq_mgr->file, "Failed to alloc GART for userqueue object (%d)", r); - goto unpin_bo; - } - - r = amdgpu_bo_kmap(userq_obj->obj, &userq_obj->cpu_ptr); - if (r) { - drm_file_err(uq_mgr->file, "Failed to map BO for userqueue (%d)", r); - goto unpin_bo; - } - - userq_obj->gpu_addr = amdgpu_bo_gpu_offset(userq_obj->obj); - amdgpu_bo_unreserve(userq_obj->obj); - memset(userq_obj->cpu_ptr, 0, size); - return 0; - -unpin_bo: - amdgpu_bo_unpin(userq_obj->obj); -unresv: - amdgpu_bo_unreserve(userq_obj->obj); -free_obj: - amdgpu_bo_unref(&userq_obj->obj); - - return r; -} - -void amdgpu_userq_destroy_object(struct amdgpu_userq_mgr *uq_mgr, - struct amdgpu_userq_obj *userq_obj) -{ - amdgpu_bo_kunmap(userq_obj->obj); - amdgpu_bo_unpin(userq_obj->obj); - amdgpu_bo_unref(&userq_obj->obj); -} static int amdgpu_userq_get_doorbell_index(struct amdgpu_userq_mgr *uq_mgr, diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h index 033b8a0de6b1..76ef5cfab52e 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h @@ -151,13 +151,6 @@ void amdgpu_userq_mgr_cancel_reset_work(struct amdgpu_device *adev); void amdgpu_userq_mgr_cancel_resume(struct amdgpu_userq_mgr *userq_mgr); void amdgpu_userq_mgr_fini(struct amdgpu_userq_mgr *userq_mgr); -int amdgpu_userq_create_object(struct amdgpu_userq_mgr *uq_mgr, - struct amdgpu_userq_obj *userq_obj, - int size); - -void amdgpu_userq_destroy_object(struct amdgpu_userq_mgr *uq_mgr, - struct amdgpu_userq_obj *userq_obj); - void amdgpu_userq_evict(struct amdgpu_userq_mgr *uq_mgr); void amdgpu_userq_ensure_ev_fence(struct amdgpu_userq_mgr *userq_mgr, diff --git a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c b/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c index 026940fad524..71251370c8b3 100644 --- a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c +++ b/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c @@ -192,12 +192,16 @@ static int mes_userq_create_ctx_space(struct amdgpu_userq_mgr *uq_mgr, * for the same. */ size = AMDGPU_USERQ_PROC_CTX_SZ + AMDGPU_USERQ_GANG_CTX_SZ; - r = amdgpu_userq_create_object(uq_mgr, ctx, size); + r = amdgpu_bo_create_kernel(uq_mgr->adev, size, 0, + AMDGPU_GEM_DOMAIN_GTT, + &ctx->obj, &ctx->gpu_addr, + &ctx->cpu_ptr); if (r) { DRM_ERROR("Failed to allocate ctx space bo for userqueue, err:%d\n", r); return r; } + memset(ctx->cpu_ptr, 0, size); return 0; } @@ -270,13 +274,19 @@ static int mes_userq_mqd_create(struct amdgpu_usermode_queue *queue, return -ENOMEM; } - r = amdgpu_userq_create_object(uq_mgr, &queue->mqd, - AMDGPU_MQD_SIZE_ALIGN(mqd_hw_default->mqd_size)); + r = amdgpu_bo_create_kernel(adev, + AMDGPU_MQD_SIZE_ALIGN(mqd_hw_default->mqd_size), + 0, AMDGPU_GEM_DOMAIN_GTT, + &queue->mqd.obj, &queue->mqd.gpu_addr, + &queue->mqd.cpu_ptr); if (r) { DRM_ERROR("Failed to create MQD object for userqueue\n"); goto free_props; } + memset(queue->mqd.cpu_ptr, 0, + AMDGPU_MQD_SIZE_ALIGN(mqd_hw_default->mqd_size)); + /* Initialize the MQD BO with user given values */ userq_props->wptr_gpu_addr = mqd_user->wptr_va; userq_props->rptr_gpu_addr = mqd_user->rptr_va; @@ -432,10 +442,12 @@ static int mes_userq_mqd_create(struct amdgpu_usermode_queue *queue, return 0; free_ctx: - amdgpu_userq_destroy_object(uq_mgr, &queue->fw_obj); + amdgpu_bo_free_kernel(&queue->fw_obj.obj, &queue->fw_obj.gpu_addr, + &queue->fw_obj.cpu_ptr); free_mqd: - amdgpu_userq_destroy_object(uq_mgr, &queue->mqd); + amdgpu_bo_free_kernel(&queue->mqd.obj, &queue->mqd.gpu_addr, + &queue->mqd.cpu_ptr); free_props: kfree(userq_props); @@ -445,11 +457,12 @@ static int mes_userq_mqd_create(struct amdgpu_usermode_queue *queue, static void mes_userq_mqd_destroy(struct amdgpu_usermode_queue *queue) { - struct amdgpu_userq_mgr *uq_mgr = queue->userq_mgr; - amdgpu_userq_destroy_object(uq_mgr, &queue->fw_obj); + amdgpu_bo_free_kernel(&queue->fw_obj.obj, &queue->fw_obj.gpu_addr, + &queue->fw_obj.cpu_ptr); kfree(queue->userq_prop); - amdgpu_userq_destroy_object(uq_mgr, &queue->mqd); + amdgpu_bo_free_kernel(&queue->mqd.obj, &queue->mqd.gpu_addr, + &queue->mqd.cpu_ptr); } static int mes_userq_preempt(struct amdgpu_usermode_queue *queue) From 93f5534b35a05ef8a0109c1eefa800062fee810a Mon Sep 17 00:00:00 2001 From: Eric Huang Date: Tue, 12 May 2026 10:19:52 -0400 Subject: [PATCH 200/337] drm/amdkfd: fix a vulnerability of integer overflow in kfd debugger get_queue_ids() computes array_size = num_queues * sizeof(uint32_t), which could overflow on 32-bit size_t build. using array_size() instead, it saturates to SIZE_MAX on overflow. Signed-off-by: Eric Huang Acked-by: Alex Deucher Signed-off-by: Alex Deucher (cherry picked from commit 2d57a0475f085c08b49312dfd8edcb461845f285) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c index e0a31e11f0ff..0d7296c739ed 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c @@ -3308,12 +3308,14 @@ static void copy_context_work_handler(struct work_struct *work) static uint32_t *get_queue_ids(uint32_t num_queues, uint32_t *usr_queue_id_array) { - size_t array_size = num_queues * sizeof(uint32_t); - if (!usr_queue_id_array) return NULL; - return memdup_user(usr_queue_id_array, array_size); + if (num_queues > KFD_MAX_NUM_OF_QUEUES_PER_PROCESS) + return ERR_PTR(-EINVAL); + + return memdup_user(usr_queue_id_array, + array_size(num_queues, sizeof(uint32_t))); } int resume_queues(struct kfd_process *p, From 0b8a1600ab50f331aeeba47c777a1b34cba606bf Mon Sep 17 00:00:00 2001 From: Sunil Khatri Date: Wed, 20 May 2026 16:25:50 +0530 Subject: [PATCH 201/337] drm/amdgpu/userq: move mqd_destroy to later stage to keep core obj valid MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit mqd_destroy cleans up queue core objects like mqd and fw_object which are needed for any pending fence to signal properly. Signed-off-by: Sunil Khatri Reviewed-by: Christian König Signed-off-by: Alex Deucher (cherry picked from commit 4ad65d610096498c8e265615aba42b3c47441bb5) --- drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c index a93f5c238e55..28a1849e7dcd 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c @@ -417,12 +417,10 @@ static void amdgpu_userq_cleanup(struct amdgpu_usermode_queue *queue) { struct amdgpu_userq_mgr *uq_mgr = queue->userq_mgr; struct amdgpu_device *adev = uq_mgr->adev; - const struct amdgpu_userq_funcs *uq_funcs = adev->userq_funcs[queue->queue_type]; /* Wait for mode-1 reset to complete */ down_read(&adev->reset_domain->sem); - uq_funcs->mqd_destroy(queue); /* Use interrupt-safe locking since IRQ handlers may access these XArrays */ xa_erase_irq(&adev->userq_doorbell_xa, queue->doorbell_index); amdgpu_userq_fence_driver_free(queue); @@ -541,15 +539,15 @@ static int amdgpu_userq_destroy(struct amdgpu_userq_mgr *uq_mgr, struct amdgpu_usermode_queue *queue) { struct amdgpu_device *adev = uq_mgr->adev; - struct amdgpu_fpriv *fpriv = uq_mgr_to_fpriv(uq_mgr); - struct amdgpu_vm *vm = &fpriv->vm; - + const struct amdgpu_userq_funcs *uq_funcs = adev->userq_funcs[queue->queue_type]; + struct amdgpu_vm *vm = queue->vm; int r = 0; cancel_delayed_work_sync(&uq_mgr->resume_work); /* Cancel any pending hang detection work and cleanup */ cancel_delayed_work_sync(&queue->hang_detect_work); + mutex_lock(&uq_mgr->userq_mutex); amdgpu_userq_wait_for_last_fence(queue); @@ -566,6 +564,7 @@ amdgpu_userq_destroy(struct amdgpu_userq_mgr *uq_mgr, struct amdgpu_usermode_que amdgpu_userq_buffer_vas_list_cleanup(adev, queue); amdgpu_bo_unreserve(vm->root.bo); list_del(&queue->userq_va_list); + uq_funcs->mqd_destroy(queue); queue->userq_mgr = NULL; amdgpu_bo_reserve(queue->db_obj.obj, true); From 181307acf8ea597ad63fd574b44d0f98a329a61b Mon Sep 17 00:00:00 2001 From: Sunil Khatri Date: Wed, 20 May 2026 16:39:49 +0530 Subject: [PATCH 202/337] drm/amdgpu/userq: use array instead of list for userq_vas MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use arrays instead of list for userq_vas since we have fixed no of bos. Also, we dont have to worry to free that memory later since this array would be free along with queue only. Signed-off-by: Sunil Khatri Reviewed-by: Christian König Signed-off-by: Alex Deucher (cherry picked from commit ef7dc711a664b0c548ecfdf13a00436b7446b8e7) --- drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c | 88 ++++++---------------- drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h | 20 +++-- drivers/gpu/drm/amd/amdgpu/mes_userqueue.c | 14 ++-- 3 files changed, 45 insertions(+), 77 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c index 28a1849e7dcd..cf192500800f 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c @@ -215,33 +215,15 @@ void amdgpu_userq_process_fence_irq(struct amdgpu_device *adev, u32 doorbell) xa_unlock_irqrestore(xa, flags); } -static int amdgpu_userq_buffer_va_list_add(struct amdgpu_usermode_queue *queue, - struct amdgpu_bo_va_mapping *va_map, u64 addr) -{ - struct amdgpu_userq_va_cursor *va_cursor; - struct userq_va_list; - - va_cursor = kzalloc_obj(*va_cursor); - if (!va_cursor) - return -ENOMEM; - - INIT_LIST_HEAD(&va_cursor->list); - va_cursor->gpu_addr = addr; - va_map->bo_va->userq_va_mapped = true; - list_add(&va_cursor->list, &queue->userq_va_list); - - return 0; -} - int amdgpu_userq_input_va_validate(struct amdgpu_device *adev, struct amdgpu_usermode_queue *queue, - u64 addr, u64 expected_size) + u64 addr, u64 expected_size, + u64 *va_out) { struct amdgpu_bo_va_mapping *va_map; struct amdgpu_vm *vm = queue->vm; u64 user_addr; u64 size; - int r = 0; /* Caller must hold vm->root.bo reservation */ dma_resv_assert_held(queue->vm->root.bo->tbo.base.resv); @@ -250,20 +232,18 @@ int amdgpu_userq_input_va_validate(struct amdgpu_device *adev, size = expected_size >> AMDGPU_GPU_PAGE_SHIFT; va_map = amdgpu_vm_bo_lookup_mapping(vm, user_addr); - if (!va_map) { - r = -EINVAL; - goto out_err; - } + if (!va_map) + return -EINVAL; + /* Only validate the userq whether resident in the VM mapping range */ if (user_addr >= va_map->start && va_map->last - user_addr + 1 >= size) { - amdgpu_userq_buffer_va_list_add(queue, va_map, user_addr); + va_map->bo_va->userq_va_mapped = true; + *va_out = user_addr; return 0; } - r = -EINVAL; -out_err: - return r; + return -EINVAL; } static bool amdgpu_userq_buffer_va_mapped(struct amdgpu_vm *vm, u64 addr) @@ -284,14 +264,16 @@ static bool amdgpu_userq_buffer_va_mapped(struct amdgpu_vm *vm, u64 addr) static bool amdgpu_userq_buffer_vas_mapped(struct amdgpu_usermode_queue *queue) { - struct amdgpu_userq_va_cursor *va_cursor, *tmp; - int r = 0; + int i, r = 0; - list_for_each_entry_safe(va_cursor, tmp, &queue->userq_va_list, list) { - r += amdgpu_userq_buffer_va_mapped(queue->vm, va_cursor->gpu_addr); + for (i = 0; i < ARRAY_SIZE(queue->userq_vas.va_array); i++) { + if (!queue->userq_vas.va_array[i]) + continue; + r += amdgpu_userq_buffer_va_mapped(queue->vm, + queue->userq_vas.va_array[i]); dev_dbg(queue->userq_mgr->adev->dev, "validate the userq mapping:%p va:%llx r:%d\n", - queue, va_cursor->gpu_addr, r); + queue, queue->userq_vas.va_array[i], r); } if (r != 0) @@ -300,24 +282,7 @@ static bool amdgpu_userq_buffer_vas_mapped(struct amdgpu_usermode_queue *queue) return false; } -static void amdgpu_userq_buffer_vas_list_cleanup(struct amdgpu_device *adev, - struct amdgpu_usermode_queue *queue) -{ - struct amdgpu_userq_va_cursor *va_cursor, *tmp; - struct amdgpu_bo_va_mapping *mapping; - /* Caller must hold vm->root.bo reservation */ - dma_resv_assert_held(queue->vm->root.bo->tbo.base.resv); - - list_for_each_entry_safe(va_cursor, tmp, &queue->userq_va_list, list) { - mapping = amdgpu_vm_bo_lookup_mapping(queue->vm, va_cursor->gpu_addr); - if (mapping) - dev_dbg(adev->dev, "delete the userq:%p va:%llx\n", - queue, va_cursor->gpu_addr); - list_del(&va_cursor->list); - kfree(va_cursor); - } -} static int amdgpu_userq_preempt_helper(struct amdgpu_usermode_queue *queue) { @@ -540,7 +505,6 @@ amdgpu_userq_destroy(struct amdgpu_userq_mgr *uq_mgr, struct amdgpu_usermode_que { struct amdgpu_device *adev = uq_mgr->adev; const struct amdgpu_userq_funcs *uq_funcs = adev->userq_funcs[queue->queue_type]; - struct amdgpu_vm *vm = queue->vm; int r = 0; cancel_delayed_work_sync(&uq_mgr->resume_work); @@ -560,10 +524,6 @@ amdgpu_userq_destroy(struct amdgpu_userq_mgr *uq_mgr, struct amdgpu_usermode_que mutex_unlock(&uq_mgr->userq_mutex); cancel_delayed_work_sync(&queue->hang_detect_work); - amdgpu_bo_reserve(vm->root.bo, true); - amdgpu_userq_buffer_vas_list_cleanup(adev, queue); - amdgpu_bo_unreserve(vm->root.bo); - list_del(&queue->userq_va_list); uq_funcs->mqd_destroy(queue); queue->userq_mgr = NULL; @@ -669,7 +629,6 @@ amdgpu_userq_create(struct drm_file *filp, union drm_amdgpu_userq *args) } kref_init(&queue->refcount); - INIT_LIST_HEAD(&queue->userq_va_list); queue->doorbell_handle = args->in.doorbell_handle; queue->queue_type = args->in.ip_type; queue->vm = &fpriv->vm; @@ -690,14 +649,17 @@ amdgpu_userq_create(struct drm_file *filp, union drm_amdgpu_userq *args) goto free_fence_drv; if (amdgpu_userq_input_va_validate(adev, queue, args->in.queue_va, - args->in.queue_size) || + args->in.queue_size, + &queue->userq_vas.va.queue_rb) || amdgpu_userq_input_va_validate(adev, queue, args->in.rptr_va, - AMDGPU_GPU_PAGE_SIZE) || + AMDGPU_GPU_PAGE_SIZE, + &queue->userq_vas.va.rptr) || amdgpu_userq_input_va_validate(adev, queue, args->in.wptr_va, - AMDGPU_GPU_PAGE_SIZE)) { + AMDGPU_GPU_PAGE_SIZE, + &queue->userq_vas.va.wptr)) { r = -EINVAL; amdgpu_bo_unreserve(fpriv->vm.root.bo); - goto clean_mapping; + goto free_fence_drv; } amdgpu_bo_unreserve(fpriv->vm.root.bo); @@ -709,7 +671,7 @@ amdgpu_userq_create(struct drm_file *filp, union drm_amdgpu_userq *args) r = amdgpu_userq_get_doorbell_index(uq_mgr, &db_info, filp, &index); if (r) { drm_file_err(uq_mgr->file, "Failed to get doorbell for queue\n"); - goto clean_mapping; + goto free_fence_drv; } queue->doorbell_index = index; @@ -769,10 +731,6 @@ amdgpu_userq_create(struct drm_file *filp, union drm_amdgpu_userq *args) amdgpu_bo_unpin(queue->db_obj.obj); amdgpu_bo_unreserve(queue->db_obj.obj); amdgpu_bo_unref(&queue->db_obj.obj); -clean_mapping: - amdgpu_bo_reserve(fpriv->vm.root.bo, true); - amdgpu_userq_buffer_vas_list_cleanup(adev, queue); - amdgpu_bo_unreserve(fpriv->vm.root.bo); free_fence_drv: amdgpu_userq_fence_driver_free(queue); free_queue: diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h index 76ef5cfab52e..28cfc6682333 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h @@ -48,11 +48,6 @@ struct amdgpu_userq_obj { struct amdgpu_bo *obj; }; -struct amdgpu_userq_va_cursor { - u64 gpu_addr; - struct list_head list; -}; - struct amdgpu_usermode_queue { int queue_type; enum amdgpu_userq_state state; @@ -93,7 +88,17 @@ struct amdgpu_usermode_queue { struct delayed_work hang_detect_work; struct kref refcount; - struct list_head userq_va_list; + union { + struct { + u64 queue_rb; + u64 wptr; + u64 rptr; + u64 eop; + u64 shadow; + u64 csa; + } va; + u64 va_array[6]; + } userq_vas; }; struct amdgpu_userq_funcs { @@ -174,7 +179,8 @@ void amdgpu_userq_process_fence_irq(struct amdgpu_device *adev, u32 doorbell); int amdgpu_userq_input_va_validate(struct amdgpu_device *adev, struct amdgpu_usermode_queue *queue, - u64 addr, u64 expected_size); + u64 addr, u64 expected_size, u64 *va_out); + void amdgpu_userq_gem_va_unmap_validate(struct amdgpu_device *adev, struct amdgpu_bo_va_mapping *mapping, uint64_t saddr); diff --git a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c b/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c index 71251370c8b3..98aa00eeb2f4 100644 --- a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c +++ b/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c @@ -318,8 +318,9 @@ static int mes_userq_mqd_create(struct amdgpu_usermode_queue *queue, kfree(compute_mqd); goto free_mqd; } - r = amdgpu_userq_input_va_validate(adev, queue, compute_mqd->eop_va, - 2048); + r = amdgpu_userq_input_va_validate(adev, queue, + compute_mqd->eop_va, 2048, + &queue->userq_vas.va.eop); amdgpu_bo_unreserve(queue->vm->root.bo); if (r) { kfree(compute_mqd); @@ -368,7 +369,8 @@ static int mes_userq_mqd_create(struct amdgpu_usermode_queue *queue, goto free_mqd; } r = amdgpu_userq_input_va_validate(adev, queue, mqd_gfx_v11->shadow_va, - shadow_info.shadow_size); + shadow_info.shadow_size, + &queue->userq_vas.va.shadow); if (r) { amdgpu_bo_unreserve(queue->vm->root.bo); kfree(mqd_gfx_v11); @@ -376,7 +378,8 @@ static int mes_userq_mqd_create(struct amdgpu_usermode_queue *queue, } r = amdgpu_userq_input_va_validate(adev, queue, mqd_gfx_v11->csa_va, - shadow_info.csa_size); + shadow_info.csa_size, + &queue->userq_vas.va.csa); amdgpu_bo_unreserve(queue->vm->root.bo); if (r) { kfree(mqd_gfx_v11); @@ -406,7 +409,8 @@ static int mes_userq_mqd_create(struct amdgpu_usermode_queue *queue, goto free_mqd; } r = amdgpu_userq_input_va_validate(adev, queue, mqd_sdma_v11->csa_va, - 32); + 32, + &queue->userq_vas.va.csa); amdgpu_bo_unreserve(queue->vm->root.bo); if (r) { kfree(mqd_sdma_v11); From 962d684b5dc0741dcd93485d41b450de402d5592 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20K=C3=B6nig?= Date: Wed, 18 Feb 2026 12:53:27 +0100 Subject: [PATCH 203/337] drm/amdgpu: fix amdgpu_hmm_range_get_pages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The notifier sequence must only be read once or otherwise we could work with invalid pages. While at it also fix the coding style, e.g. drop the pre-initialized return value and use the common define for 2G range. Signed-off-by: Christian König Reviewed-by: Vitaly Prosyak Tested-by: Vitaly Prosyak Reviewed-by: Alex Deucher Signed-off-by: Alex Deucher (cherry picked from commit c08972f555945cda57b0adb72272a37910153390) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.c index f72990ac046e..0a9582da3a33 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.c @@ -51,8 +51,6 @@ #include "amdgpu_amdkfd.h" #include "amdgpu_hmm.h" -#define MAX_WALK_BYTE (2UL << 30) - /** * amdgpu_hmm_invalidate_gfx - callback to notify about mm change * @@ -170,11 +168,13 @@ int amdgpu_hmm_range_get_pages(struct mmu_interval_notifier *notifier, void *owner, struct amdgpu_hmm_range *range) { - unsigned long end; + const u64 max_bytes = SZ_2G; + + struct hmm_range *hmm_range = &range->hmm_range; unsigned long timeout; unsigned long *pfns; - int r = 0; - struct hmm_range *hmm_range = &range->hmm_range; + unsigned long end; + int r; pfns = kvmalloc_array(npages, sizeof(*pfns), GFP_KERNEL); if (unlikely(!pfns)) { @@ -191,8 +191,9 @@ int amdgpu_hmm_range_get_pages(struct mmu_interval_notifier *notifier, end = start + npages * PAGE_SIZE; hmm_range->dev_private_owner = owner; + hmm_range->notifier_seq = mmu_interval_read_begin(notifier); do { - hmm_range->end = min(hmm_range->start + MAX_WALK_BYTE, end); + hmm_range->end = min(hmm_range->start + max_bytes, end); pr_debug("hmm range: start = 0x%lx, end = 0x%lx", hmm_range->start, hmm_range->end); @@ -200,7 +201,6 @@ int amdgpu_hmm_range_get_pages(struct mmu_interval_notifier *notifier, timeout = jiffies + msecs_to_jiffies(HMM_RANGE_DEFAULT_TIMEOUT); retry: - hmm_range->notifier_seq = mmu_interval_read_begin(notifier); r = hmm_range_fault(hmm_range); if (unlikely(r)) { if (r == -EBUSY && !time_after(jiffies, timeout)) @@ -210,7 +210,7 @@ int amdgpu_hmm_range_get_pages(struct mmu_interval_notifier *notifier, if (hmm_range->end == end) break; - hmm_range->hmm_pfns += MAX_WALK_BYTE >> PAGE_SHIFT; + hmm_range->hmm_pfns += max_bytes >> PAGE_SHIFT; hmm_range->start = hmm_range->end; } while (hmm_range->end < end); From 1c824497d8acd3187d585d6187cedc1897dcc871 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20K=C3=B6nig?= Date: Wed, 18 Feb 2026 12:31:29 +0100 Subject: [PATCH 204/337] drm/amdgpu: fix calling VM invalidation in amdgpu_hmm_invalidate_gfx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Otherwise we don't invalidate page tables on next CS. Signed-off-by: Christian König Reviewed-by: Vitaly Prosyak Tested-by: Vitaly Prosyak Reviewed-by: Alex Deucher Signed-off-by: Alex Deucher (cherry picked from commit b6444d1bcbc34f6f2a31a3aab3059be082f3683e) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.c | 1 + drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c | 7 +++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.c index 0a9582da3a33..5bfa5a84b09c 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.c @@ -76,6 +76,7 @@ static bool amdgpu_hmm_invalidate_gfx(struct mmu_interval_notifier *mni, mmu_interval_set_seq(mni, cur_seq); + amdgpu_vm_bo_invalidate(bo, false); r = dma_resv_wait_timeout(bo->tbo.base.resv, DMA_RESV_USAGE_BOOKKEEP, false, MAX_SCHEDULE_TIMEOUT); mutex_unlock(&adev->notifier_lock); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c index fccd758b6699..c9f88ecce1a7 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c @@ -1631,6 +1631,7 @@ int amdgpu_vm_handle_moved(struct amdgpu_device *adev, { struct amdgpu_bo_va *bo_va; struct dma_resv *resv; + struct amdgpu_bo *bo; bool clear, unlock; int r; @@ -1650,11 +1651,13 @@ int amdgpu_vm_handle_moved(struct amdgpu_device *adev, while (!list_empty(&vm->invalidated)) { bo_va = list_first_entry(&vm->invalidated, struct amdgpu_bo_va, base.vm_status); - resv = bo_va->base.bo->tbo.base.resv; + bo = bo_va->base.bo; + resv = bo->tbo.base.resv; spin_unlock(&vm->status_lock); /* Try to reserve the BO to avoid clearing its ptes */ - if (!adev->debug_vm && dma_resv_trylock(resv)) { + if (!adev->debug_vm && !amdgpu_ttm_tt_get_usermm(bo->tbo.ttm) && + dma_resv_trylock(resv)) { clear = false; unlock = true; /* The caller is already holding the reservation lock */ From 0a68853de27b522bca2b9934127277185374a24f Mon Sep 17 00:00:00 2001 From: Sun Shaojie Date: Wed, 27 May 2026 14:43:28 +0800 Subject: [PATCH 205/337] cgroup/cpuset: Use effective_xcpus in partcmd_update add/del mask calculation When sibling CPU exclusion occurs, a partition's user_xcpus may contain CPUs that were never actually granted to it. These CPUs are present in user_xcpus(cs) but not in cs->effective_xcpus. The partcmd_update path in update_parent_effective_cpumask() uses user_xcpus(cs) (via the local variable xcpus) to compute the addmask (CPUs to return to parent) and delmask (CPUs to request from parent). This is incorrect: 1) When newmask removes a CPU that was previously excluded by a sibling, addmask incorrectly includes that CPU and tries to return it to the parent even though the partition never actually owned it, causing CPU overlap with sibling partitions and triggering warnings in generate_sched_domains(). 2) When newmask adds a previously excluded CPU that is now available, delmask fails to request it from the parent because user_xcpus(cs) already includes it. Fix this by using cs->effective_xcpus instead of user_xcpus(cs) in all partcmd_update paths that calculate addmask or delmask, including the PERR_NOCPUS error handling paths. Reproducers: Example 1 - Removing a sibling-excluded CPU incorrectly returns it: # cd /sys/fs/cgroup # echo "0-1" > a1/cpuset.cpus # echo "root" > a1/cpuset.cpus.partition # echo "0-2" > b1/cpuset.cpus # echo "root" > b1/cpuset.cpus.partition # echo "2" > b1/cpuset.cpus # cat cpuset.cpus.effective # Actual: 0-1,3 Expected: 3 Example 2 - Expanding to a previously excluded CPU fails to request it: # cd /sys/fs/cgroup # echo "0-1" > a1/cpuset.cpus # echo "root" > a1/cpuset.cpus.partition # echo "0-2" > b1/cpuset.cpus # echo "root" > b1/cpuset.cpus.partition # echo "member" > a1/cpuset.cpus.partition # echo "1-2" > b1/cpuset.cpus # cat cpuset.cpus.effective # Actual: 0-1,3 Expected: 0,3 Fixes: 2a3602030d80 ("cgroup/cpuset: Don't invalidate sibling partitions on cpuset.cpus conflict") Cc: stable@vger.kernel.org # v7.0+ Suggested-by: Zhang Guopeng Signed-off-by: Sun Shaojie Reviewed-by: Waiman Long Signed-off-by: Tejun Heo --- kernel/cgroup/cpuset.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c index 5c33ab20cc20..c9e14fda3d6f 100644 --- a/kernel/cgroup/cpuset.c +++ b/kernel/cgroup/cpuset.c @@ -1811,9 +1811,9 @@ static int update_parent_effective_cpumask(struct cpuset *cs, int cmd, * Compute add/delete mask to/from effective_cpus * * For valid partition: - * addmask = exclusive_cpus & ~newmask + * addmask = effective_xcpus & ~newmask * & parent->effective_xcpus - * delmask = newmask & ~exclusive_cpus + * delmask = newmask & ~effective_xcpus * & parent->effective_xcpus * * For invalid partition: @@ -1825,11 +1825,11 @@ static int update_parent_effective_cpumask(struct cpuset *cs, int cmd, deleting = cpumask_and(tmp->delmask, newmask, parent->effective_xcpus); } else { - cpumask_andnot(tmp->addmask, xcpus, newmask); + cpumask_andnot(tmp->addmask, cs->effective_xcpus, newmask); adding = cpumask_and(tmp->addmask, tmp->addmask, parent->effective_xcpus); - cpumask_andnot(tmp->delmask, newmask, xcpus); + cpumask_andnot(tmp->delmask, newmask, cs->effective_xcpus); deleting = cpumask_and(tmp->delmask, tmp->delmask, parent->effective_xcpus); } @@ -1868,7 +1868,7 @@ static int update_parent_effective_cpumask(struct cpuset *cs, int cmd, part_error = PERR_NOCPUS; deleting = false; adding = cpumask_and(tmp->addmask, - xcpus, parent->effective_xcpus); + cs->effective_xcpus, parent->effective_xcpus); } } else { /* @@ -1890,7 +1890,8 @@ static int update_parent_effective_cpumask(struct cpuset *cs, int cmd, part_error = PERR_NOCPUS; if (is_partition_valid(cs)) adding = cpumask_and(tmp->addmask, - xcpus, parent->effective_xcpus); + cs->effective_xcpus, + parent->effective_xcpus); } else if (is_partition_invalid(cs) && !cpumask_empty(xcpus) && cpumask_subset(xcpus, parent->effective_xcpus)) { struct cgroup_subsys_state *css; From 645c3b7ef1a7eed9627664bd11d7a8eb4519ee15 Mon Sep 17 00:00:00 2001 From: Sun Shaojie Date: Wed, 27 May 2026 15:05:09 +0800 Subject: [PATCH 206/337] cgroup/cpuset: Add test cases for sibling CPU exclusion on partition update When sibling CPU exclusion occurs, a partition's effective_xcpus may be a subset of its user_xcpus. The partcmd_update path must use effective_xcpus instead of user_xcpus when calculating CPUs to return to or request from the parent. Add two test cases to verify this behavior: 1) Narrowing cpuset.cpus to only the sibling-excluded CPUs should not return CPUs to parent that the partition never actually owned. 2) Expanding cpuset.cpus after a sibling becomes a member should correctly request the additional CPUs from parent. Co-developed-by: Zhang Guopeng Signed-off-by: Zhang Guopeng Signed-off-by: Sun Shaojie Reviewed-by: Waiman Long Signed-off-by: Tejun Heo --- tools/testing/selftests/cgroup/test_cpuset_prs.sh | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tools/testing/selftests/cgroup/test_cpuset_prs.sh b/tools/testing/selftests/cgroup/test_cpuset_prs.sh index a56f4153c64d..683b05062810 100755 --- a/tools/testing/selftests/cgroup/test_cpuset_prs.sh +++ b/tools/testing/selftests/cgroup/test_cpuset_prs.sh @@ -492,6 +492,16 @@ REMOTE_TEST_MATRIX=( " C1-5:P1 . C1-4:P1 C2-3 . . \ . . . P1 . . p1:5|c11:1-4|c12:5 \ p1:P1|c11:P1|c12:P-1" + # Narrowing cpuset.cpus to previously sibling-excluded CPUs should + # not return CPUs that were never actually owned. + " C1-4:P1 . C1-2:P1 C1-3:P2 . . \ + . . . C3 . . p1:4|c11:1-2|c12:3 \ + p1:P1|c11:P1|c12:P2 3" + # Expanding cpuset.cpus to include a previously sibling-excluded CPU + # after the sibling has become a member should correctly request it. + " C1-4:P1 . C1-2:P1 C1-3:P2 . . \ + . . P0 C2-3 . . p1:1,4|c11:1|c12:2-3 \ + p1:P1|c11:P0|c12:P2 2-3" ) # From b565a73baec275a3f4b49c1300ab396daf4a748e Mon Sep 17 00:00:00 2001 From: Zicheng Qu Date: Wed, 27 May 2026 17:38:50 +0800 Subject: [PATCH 207/337] tools/sched_ext: Fix scx_show_state per-scheduler state reads scx_show_state.py still reads scx_aborting and scx_bypass_depth as global symbols. Those symbols no longer exist after the state was moved into struct scx_sched, so the drgn script fails when it reaches either field. Read aborting and bypass_depth from scx_root instead. This preserves the script's current root-scheduler view: with sub-scheduler support, the reported values are for the root scheduler and sub-schedulers are not enumerated. Fixes: 5c8d98a1b4de ("sched_ext: Move bypass state into scx_sched") Fixes: c1743da43cf5 ("sched_ext: Move aborting flag to per-scheduler field") Signed-off-by: Zicheng Qu Reviewed-by: Andrea Righi Signed-off-by: Tejun Heo --- tools/sched_ext/scx_show_state.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/tools/sched_ext/scx_show_state.py b/tools/sched_ext/scx_show_state.py index 02e43c184d43..446d82807f90 100644 --- a/tools/sched_ext/scx_show_state.py +++ b/tools/sched_ext/scx_show_state.py @@ -27,18 +27,25 @@ def read_static_key(name): def state_str(state): return prog['scx_enable_state_str'][state].string_().decode() +def read_root_ops_name(): + if root: + return root.ops.name.string_().decode() + return '' + +def read_root_field(name, default): + if root: + return getattr(root, name).value_() + return default + root = prog['scx_root'] enable_state = read_atomic("scx_enable_state_var") -if root: - print(f'ops : {root.ops.name.string_().decode()}') -else: - print('ops : ') +print(f'ops : {read_root_ops_name()}') print(f'enabled : {read_static_key("__scx_enabled")}') print(f'switching_all : {read_int("scx_switching_all")}') print(f'switched_all : {read_static_key("__scx_switched_all")}') print(f'enable_state : {state_str(enable_state)} ({enable_state})') -print(f'aborting : {prog["scx_aborting"].value_()}') -print(f'bypass_depth : {prog["scx_bypass_depth"].value_()}') +print(f'aborting : {read_root_field("aborting", False)}') +print(f'bypass_depth : {read_root_field("bypass_depth", 0)}') print(f'nr_rejected : {read_atomic("scx_nr_rejected")}') print(f'enable_seq : {read_atomic("scx_enable_seq")}') From 426a35d7530722ffa2d89d759c39f5157e0e500d Mon Sep 17 00:00:00 2001 From: Jeremy Erazo Date: Wed, 20 May 2026 18:23:31 +0000 Subject: [PATCH 208/337] smb: client: detect short folioq copy in cifs_copy_folioq_to_iter() cifs_copy_folioq_to_iter() copies a requested number of bytes from a folio queue into the destination iterator. Since the encrypted SMB2 READ path was changed to pass the server-declared payload length (data_len) instead of the larger folioq buffer length, the caller can ask for fewer bytes than the folio queue holds. In that case the helper continues walking the remaining folios after data_size has reached zero and calls copy_folio_to_iter() with len = 0, which is unnecessary work. The helper also returns 0 (success) when the folio queue is exhausted before data_size bytes have been copied. The caller has no way to distinguish that from a full copy and the reported transfer count ends up larger than the amount of data placed in the iterator. Add an early exit when data_size reaches zero, and return an error when the folio queue is exhausted before all requested bytes have been copied. Signed-off-by: Jeremy Erazo Reviewed-by: David Howells Signed-off-by: Steve French --- fs/smb/client/smb2ops.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c index 61b60114e4b8..d4875f9532b4 100644 --- a/fs/smb/client/smb2ops.c +++ b/fs/smb/client/smb2ops.c @@ -4706,9 +4706,15 @@ cifs_copy_folioq_to_iter(struct folio_queue *folioq, size_t data_size, { for (; folioq; folioq = folioq->next) { for (int s = 0; s < folioq_count(folioq); s++) { - struct folio *folio = folioq_folio(folioq, s); - size_t fsize = folio_size(folio); - size_t n, len = umin(fsize - skip, data_size); + struct folio *folio; + size_t fsize, n, len; + + if (data_size == 0) + return 0; + + folio = folioq_folio(folioq, s); + fsize = folio_size(folio); + len = umin(fsize - skip, data_size); n = copy_folio_to_iter(folio, skip, len, iter); if (n != len) { @@ -4721,6 +4727,12 @@ cifs_copy_folioq_to_iter(struct folio_queue *folioq, size_t data_size, } } + if (data_size != 0) { + cifs_dbg(VFS, "%s: short copy, %zu bytes missing\n", + __func__, data_size); + return smb_EIO2(smb_eio_trace_rx_copy_to_iter, 0, data_size); + } + return 0; } From 9d2491197a00acf8c423512078458c2855102b66 Mon Sep 17 00:00:00 2001 From: Steve French Date: Fri, 22 May 2026 18:28:49 -0500 Subject: [PATCH 209/337] smb: client: fix uninitialized variable in smb2_writev_callback compiling with W=2 pointed out that "written may be used uninitialized" Fixes: 20d72b00ca81 ("netfs: Fix the request's work item to not require a ref") Cc: stable@vger.kernel.org Reviewed-by: David Howells Signed-off-by: Steve French --- fs/smb/client/smb2pdu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c index 3bd300347f16..fbeb2156ddb6 100644 --- a/fs/smb/client/smb2pdu.c +++ b/fs/smb/client/smb2pdu.c @@ -4955,7 +4955,7 @@ smb2_writev_callback(struct TCP_Server_Info *server, struct mid_q_entry *mid) unsigned int rreq_debug_id = wdata->rreq->debug_id; unsigned int subreq_debug_index = wdata->subreq.debug_index; ssize_t result = 0; - size_t written; + size_t written = 0; WARN_ONCE(wdata->server != server, "wdata server %p != mid server %p", From a17dc12bfed8868e6a86f3b45c16065a70641acb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20Lothor=C3=A9=20=28eBPF=20Foundation=29?= Date: Wed, 27 May 2026 21:12:31 +0200 Subject: [PATCH 210/337] x86/ftrace: Relocate %rip-relative percpu refs in dynamic trampolines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With CONFIG_CALL_DEPTH_TRACKING enabled on an x86 retbleed-affected platform (eg: Skylake), with retbleed=stuff, registering a dynamic ftrace trampoline crashes on the first call into the traced function: BUG: unable to handle page fault for address: ffff88817ae18880 #PF: supervisor write access in kernel mode #PF: error_code(0x0002) - not-present page PGD 4b53067 P4D 4b53067 PUD 0 Oops: Oops: 0002 [#1] SMP PTI CPU: 3 UID: 0 PID: 187 Comm: usleep Not tainted 7.0.10 #243 PREEMPT(full) Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Arch Linux 1.17.0-2-2 04/01/2014 Code: 24 78 00 00 00 00 48 89 ea 48 89 54 24 20 48 8b b4 24 b8 00 00 00 48 8b bc 24 b0 00 00 00 48 89 bc 24 80 00 00 00 48 83 ef 05 <65> 48 c1 3d 1f a8 b6 02 05 48 8b 15 f6 00 00 00 4c 89 3c 24 4c 89 Call Trace: ? find_held_lock ? exc_page_fault ? lock_release ? __x64_sys_clock_nanosleep ? lockdep_hardirqs_on_prepare ? trace_hardirqs_on __x64_sys_clock_nanosleep do_syscall_64 ? exc_page_fault ? call_depth_return_thunk entry_SYSCALL_64_after_hwframe ... Kernel panic - not syncing: Fatal exception This small reproducer allows to easily trigger the crash: # echo 'p __x64_sys_clock_nanosleep' > /sys/kernel/tracing/kprobe_events # echo 1 > /sys/kernel/tracing/events/kprobes/p___x64_sys_clock_nanosleep_0/enable # usleep 1 Monitoring the crash under GDB points to the exact instruction in charge of incrementing the call depth: sarq $5, %gs:__x86_call_depth(%rip) This instruction matches the one inserted by the ftrace_regs_caller from ftrace_64.S. This emitted code was likely working fine until the introduction of 59bec00ace28 ("x86/percpu: Introduce %rip-relative addressing to PER_CPU_VAR()"): it has made the call depth accounting addressing relative to $rip, instead of being based on an absolute address. As this code exact location depends on where the trampoline lives in memory, the corresponding displacement needs to be adjusted at runtime to actually correctly find the per-cpu __x86_call_depth value, otherwise the targeted address is wrong, leading to the page fault seen above. Fix the %rip-relative displacement of the copied CALL_DEPTH_ACCOUNT instruction (from ftrace_regs_caller) by calling text_poke_apply_relocation(), as it is done for example by the x86 BPF JIT compiler through x86_call_depth_emit_accounting(). This corrects both CALL_DEPTH_ACCOUNT slots, in ftrace_caller and ftrace_regs_caller. [ bp: Massage. ] Fixes: 59bec00ace28 ("x86/percpu: Introduce %rip-relative addressing to PER_CPU_VAR()") Signed-off-by: Alexis Lothoré (eBPF Foundation) Signed-off-by: Borislav Petkov (AMD) Acked-by: Peter Zijlstra (Intel) Acked-by: Steven Rostedt Cc: Link: https://patch.msgid.link/20260527-fix_call_depth_in_trampoline-v1-1-1c1abc8ae310@bootlin.com --- arch/x86/kernel/ftrace.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/arch/x86/kernel/ftrace.c b/arch/x86/kernel/ftrace.c index 0543b57f54ee..17d6edfcb7e0 100644 --- a/arch/x86/kernel/ftrace.c +++ b/arch/x86/kernel/ftrace.c @@ -375,6 +375,13 @@ create_trampoline(struct ftrace_ops *ops, unsigned int *tramp_size) goto fail; } + /* + * Generated trampoline may contain rIP-relative addressing which + * displacement needs to be fixed. + */ + text_poke_apply_relocation(trampoline, trampoline, size, + (void *)start_offset, size); + /* * The address of the ftrace_ops that is used for this trampoline * is stored at the end of the trampoline. This will be used to From 8aeb879baf12fe64889f019da9a4f8347c604e91 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Tue, 26 May 2026 11:06:31 +0200 Subject: [PATCH 211/337] x86/kvm/vmx: Fix x86_64 CFI build It was missed that idt_do_interrupt_irqoff() gets compiled on x84_64; this is a problem for CFI builds because it includes an unadorned indirect call. It is however completely dead code. Rework things to not emit this function at all. Fixes: 0701c9e17bd9 ("x86/kvm/vmx: Move IRQ/NMI dispatch from KVM into x86 core") Reported-by: Nathan Chancellor Reported-by: Calvin Owens Signed-off-by: Peter Zijlstra (Intel) Tested-by: Nathan Chancellor Link: https://patch.msgid.link/20260526090631.GA4149641@noisy.programming.kicks-ass.net --- arch/x86/entry/common.c | 2 +- arch/x86/entry/entry.S | 2 ++ arch/x86/kernel/idt.c | 12 ++---------- 3 files changed, 5 insertions(+), 11 deletions(-) diff --git a/arch/x86/entry/common.c b/arch/x86/entry/common.c index 06c7c6ebd6f9..14cd43d4da6c 100644 --- a/arch/x86/entry/common.c +++ b/arch/x86/entry/common.c @@ -55,7 +55,7 @@ noinstr void x86_entry_from_kvm(unsigned int event_type, unsigned int vector) * The FRED NMI context is significantly different and will not work * right (specifically FRED fixed the NMI recursion issue). */ - idt_entry_from_kvm(vector); + idt_do_nmi_irqoff(); } EXPORT_SYMBOL_FOR_KVM(x86_entry_from_kvm); #endif diff --git a/arch/x86/entry/entry.S b/arch/x86/entry/entry.S index a56e043b266d..2bc217bb5475 100644 --- a/arch/x86/entry/entry.S +++ b/arch/x86/entry/entry.S @@ -109,11 +109,13 @@ EXPORT_SYMBOL(__ref_stack_chk_guard); RET .endm +#ifndef CONFIG_X86_64 .pushsection .text, "ax" SYM_FUNC_START(idt_do_interrupt_irqoff) IDT_DO_EVENT_IRQOFF CALL_NOSPEC _ASM_ARG1 SYM_FUNC_END(idt_do_interrupt_irqoff) .popsection +#endif .pushsection .noinstr.text, "ax" SYM_FUNC_START(idt_do_nmi_irqoff) diff --git a/arch/x86/kernel/idt.c b/arch/x86/kernel/idt.c index 7bcf1decc034..90a22e24a9eb 100644 --- a/arch/x86/kernel/idt.c +++ b/arch/x86/kernel/idt.c @@ -268,18 +268,10 @@ void __init idt_setup_early_pf(void) } #endif -#if IS_ENABLED(CONFIG_KVM_INTEL) -noinstr void idt_entry_from_kvm(unsigned int vector) +#if IS_ENABLED(CONFIG_KVM_INTEL) && !defined(CONFIG_X86_64) +void idt_entry_from_kvm(unsigned int vector) { - if (vector == NMI_VECTOR) - return idt_do_nmi_irqoff(); - - /* - * Only the NMI path requires noinstr. - */ - instrumentation_begin(); idt_do_interrupt_irqoff(gate_offset(idt_table + vector)); - instrumentation_end(); } #endif From 3daad7f60aa92d0307fa2b2edd38c886a09902f2 Mon Sep 17 00:00:00 2001 From: Jan Polensky Date: Thu, 21 May 2026 14:01:32 +0200 Subject: [PATCH 212/337] s390/bug: Always emit format word in __BUG_ENTRY When CONFIG_DEBUG_BUGVERBOSE is disabled, the s390 __BUG_ENTRY() macro omits the format string pointer, so the generated __bug_table entry no longer matches struct bug_entry. With HAVE_ARCH_BUG_FORMAT enabled, the generic BUG infrastructure reads bug_entry::format via bug_get_format(). If the format word is missing, subsequent fields are read from the wrong offset, which may: - Misinterpret flags (BUG vs WARN classification errors) - Fault when dereferencing a misread format pointer The root cause is that __BUG_ENTRY() delegates format word emission to __BUG_ENTRY_VERBOSE(), which is conditional on CONFIG_DEBUG_BUGVERBOSE. Fix this by moving the format field emission directly into __BUG_ENTRY() so it is always emitted unconditionally. Remove the format parameter from __BUG_ENTRY_VERBOSE() and keep only file/line emission conditional on CONFIG_DEBUG_BUGVERBOSE. Fixes: 2b71b8ab9718 ("s390/bug: Use BUG_FORMAT for DEBUG_BUGVERBOSE_DETAILED") Signed-off-by: Jan Polensky Reviewed-by: Heiko Carstens Signed-off-by: Alexander Gordeev --- arch/s390/include/asm/bug.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/arch/s390/include/asm/bug.h b/arch/s390/include/asm/bug.h index 59017fd3d935..50a270edb020 100644 --- a/arch/s390/include/asm/bug.h +++ b/arch/s390/include/asm/bug.h @@ -12,12 +12,11 @@ #if defined(CONFIG_BUG) && defined(CONFIG_CC_HAS_ASM_IMMEDIATE_STRINGS) #ifdef CONFIG_DEBUG_BUGVERBOSE -#define __BUG_ENTRY_VERBOSE(format, file, line) \ - " .long " format " - . # bug_entry::format\n" \ +#define __BUG_ENTRY_VERBOSE(file, line) \ " .long " file " - . # bug_entry::file\n" \ " .short " line " # bug_entry::line\n" #else -#define __BUG_ENTRY_VERBOSE(format, file, line) +#define __BUG_ENTRY_VERBOSE(file, line) #endif #ifdef CONFIG_DEBUG_BUGVERBOSE_DETAILED @@ -28,9 +27,10 @@ #define __BUG_ENTRY(format, file, line, flags, size) \ " .section __bug_table,\"aw\"\n" \ - "1: .long 0b - . # bug_entry::bug_addr\n" \ - __BUG_ENTRY_VERBOSE(format, file, line) \ - " .short "flags" # bug_entry::flags\n" \ + "1: .long 0b - . # bug_entry::bug_addr\n"\ + " .long " format " - . # bug_entry::format\n" \ + __BUG_ENTRY_VERBOSE(file, line) \ + " .short "flags" # bug_entry::flags\n" \ " .org 1b+"size"\n" \ " .previous" From 79378db6a86c7014cce40b65252e6c18f5b8bcc2 Mon Sep 17 00:00:00 2001 From: Santhosh Kumar K Date: Wed, 27 May 2026 23:07:36 +0530 Subject: [PATCH 213/337] spi: spi-mem: avoid mutating op template in spi_mem_supports_op() spi_mem_supports_op() accepts a const struct spi_mem_op pointer but casts away const internally to call spi_mem_adjust_op_freq(). This mutates the caller's op template, which causes stale max_freq values when callers reuse persistent templates - subsequent calls won't re-apply the device frequency cap since spi_mem_adjust_op_freq() skips non-zero values. Fix by operating on a stack-local copy instead. Fixes: a4f8e70d75dd ("spi: spi-mem: add spi_mem_adjust_op_freq() in spi_mem_supports_op()") Cc: Tianyu Xu Cc: stable@vger.kernel.org Signed-off-by: Santhosh Kumar K Reviewed-by: Miquel Raynal Link: https://patch.msgid.link/20260527173736.2243004-1-s-k6@ti.com Signed-off-by: Mark Brown --- drivers/spi/spi-mem.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/drivers/spi/spi-mem.c b/drivers/spi/spi-mem.c index a09371a075d2..93266848c6df 100644 --- a/drivers/spi/spi-mem.c +++ b/drivers/spi/spi-mem.c @@ -279,13 +279,20 @@ static bool spi_mem_internal_supports_op(struct spi_mem *mem, */ bool spi_mem_supports_op(struct spi_mem *mem, const struct spi_mem_op *op) { - /* Make sure the operation frequency is correct before going futher */ - spi_mem_adjust_op_freq(mem, (struct spi_mem_op *)op); + struct spi_mem_op eval_op = *op; - if (spi_mem_check_op(op)) + /* + * Work on a local copy; this is a pure capability check and must + * not modify the caller's op. Stored templates with max_freq == 0 + * must remain unset so their frequency is always re-capped to the + * current device maximum at execution time. + */ + spi_mem_adjust_op_freq(mem, &eval_op); + + if (spi_mem_check_op(&eval_op)) return false; - return spi_mem_internal_supports_op(mem, op); + return spi_mem_internal_supports_op(mem, &eval_op); } EXPORT_SYMBOL_GPL(spi_mem_supports_op); From 006c66d1d52f1905e6ccfb615cf27235e4e6e745 Mon Sep 17 00:00:00 2001 From: bui duc phuc Date: Thu, 28 May 2026 12:32:04 +0700 Subject: [PATCH 214/337] regmap: reject volatile update_bits() in cache-only mode Prevent _regmap_update_bits() from accessing hardware when the register map is in cache-only mode. Unlike regmap_raw_read() and _regmap_read(), the volatile _regmap_update_bits() fast path bypasses the cache_only check. This can result in unexpected hardware accesses while the device is suspended. Return -EBUSY to ensure behavior is consistent with other cache-only access paths. Signed-off-by: bui duc phuc Link: https://patch.msgid.link/20260528053204.46783-1-phucduc.bui@gmail.com Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index b2b26f07f4e3..e6e022b02637 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -3257,6 +3257,9 @@ static int _regmap_update_bits(struct regmap *map, unsigned int reg, *change = false; if (regmap_volatile(map, reg) && map->reg_update_bits) { + if (map->cache_only) + return -EBUSY; + reg = regmap_reg_addr(map, reg); ret = map->reg_update_bits(map->bus_context, reg, mask, val); if (ret == 0 && change) From ead6680f354f83966c796fc7f9463a3171789616 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sat, 23 May 2026 19:14:46 +0100 Subject: [PATCH 215/337] dma-buf: fix UAF in dma_buf_fd() tracepoint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Once FD_ADD() returns, the fd is live in the file descriptor table and a thread sharing that table can close() it before DMA_BUF_TRACE() runs. The close drops the last reference, __fput() frees the dma_buf, and the tracepoint then dereferences dmabuf to take dmabuf->name_lock -- slab-use-after-free. Split FD_ADD() back into get_unused_fd_flags() + fd_install() and emit the tracepoint between them. While the fdtable slot is reserved with a NULL file pointer, a racing close() returns -EBADF without entering __fput(), so the dma_buf stays alive across the trace. Same approach as commit 2d76319c4cbb ("dma-buf: fix UAF in dma_buf_put() tracepoint"). This undoes the FD_ADD() conversion done in commit 34dfce523c90 ("dma: convert dma_buf_fd() to FD_ADD()"); FD_ADD() has no place to hook the tracepoint safely. Reported-by: syzbot+7f4987d0afb97dd090cb@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=7f4987d0afb97dd090cb Fixes: 281a22631423 ("dma-buf: add some tracepoints to debug.") Cc: stable@vger.kernel.org # 7.0.x Signed-off-by: David Carlier Reviewed-by: Christian König Signed-off-by: Sumit Semwal Link: https://patch.msgid.link/20260523181446.69525-1-devnexen@gmail.com --- drivers/dma-buf/dma-buf.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c index 71f37544a5c6..d504c636dc29 100644 --- a/drivers/dma-buf/dma-buf.c +++ b/drivers/dma-buf/dma-buf.c @@ -792,9 +792,13 @@ int dma_buf_fd(struct dma_buf *dmabuf, int flags) if (!dmabuf || !dmabuf->file) return -EINVAL; - fd = FD_ADD(flags, dmabuf->file); + fd = get_unused_fd_flags(flags); + if (fd < 0) + return fd; + DMA_BUF_TRACE(trace_dma_buf_fd, dmabuf, fd); + fd_install(fd, dmabuf->file); return fd; } EXPORT_SYMBOL_NS_GPL(dma_buf_fd, "DMA_BUF"); From 66ac2df408ede627aaae588d4ce7e611dd25b4f9 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 28 May 2026 10:25:26 +0200 Subject: [PATCH 216/337] ARM: dts: gemini: Fix partition offsets These FIS partition offsets were never right: the comment clearly states the FIS index is at 0xfe0000 and 0x7f * 0x200000 is 0xfe0000. Tested on the iTian SQ201. Fixes: d88b11ef91b1 ("ARM: dts: Fix up SQ201 flash access") Fixes: b5a923f8c739 ("ARM: dts: gemini: Switch to redboot partition parsing") Signed-off-by: Linus Walleij Signed-off-by: Arnd Bergmann --- arch/arm/boot/dts/gemini/gemini-sl93512r.dts | 2 +- arch/arm/boot/dts/gemini/gemini-sq201.dts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/boot/dts/gemini/gemini-sl93512r.dts b/arch/arm/boot/dts/gemini/gemini-sl93512r.dts index 4992ec276de9..341dec9b636a 100644 --- a/arch/arm/boot/dts/gemini/gemini-sl93512r.dts +++ b/arch/arm/boot/dts/gemini/gemini-sl93512r.dts @@ -146,7 +146,7 @@ flash@30000000 { partitions { compatible = "redboot-fis"; /* Eraseblock at 0xfe0000 */ - fis-index-block = <0x1fc>; + fis-index-block = <0x7f>; }; }; diff --git a/arch/arm/boot/dts/gemini/gemini-sq201.dts b/arch/arm/boot/dts/gemini/gemini-sq201.dts index f8c6f6e5cdea..bfd1e8581ad6 100644 --- a/arch/arm/boot/dts/gemini/gemini-sq201.dts +++ b/arch/arm/boot/dts/gemini/gemini-sq201.dts @@ -134,7 +134,7 @@ flash@30000000 { partitions { compatible = "redboot-fis"; /* Eraseblock at 0xfe0000 */ - fis-index-block = <0x1fc>; + fis-index-block = <0x7f>; }; }; From b4aea43cd37afad714b5684fe9fdfcb0e78dba26 Mon Sep 17 00:00:00 2001 From: Lorenzo Stoakes Date: Wed, 13 May 2026 09:56:58 +0100 Subject: [PATCH 217/337] mm/hugetlb: avoid false positive lockdep assertion Commit 081056dc00a2 ("mm/hugetlb: unshare page tables during VMA split, not before") changed the locking model around hugetlbfs PMD unsharing on VMA split, but did not update the function which asserts the locks, hugetlb_vma_assert_locked(). This function asserts that either the hugetlb VMA lock is held (if a shared mapping) or that the reservation map lock is held (if private). If you get an unfortunate race between something which results in one of these locks being released and a hugetlb VMA split and you have CONFIG_LOCKDEP enabled, you can therefore see a false positive assertion arise when there is in fact no issue. Since this change introduced a new take_locks parameter to hugetlb_unshare_pmds(), which, when set to false, indicates that locking is sufficient, simply pass this to the unsharing logic and predicate the lock assertions on this. This is safe, as we already asserted the file rmap lock and the VMA write lock prior to this (implying exclusive mmap write lock), so we cannot be raced by either rmap or page fault page table walkers which the asserted locks are intended to protect against (we don't mind GUP-fast). Separate out huge_pmd_unshare() into __huge_pmd_unshare() to add a check_locks parameter, and update hugetlb_unshare_pmds() to pass this parameter to it. This leaves all other callers of huge_pmd_unshare() still correctly asserting the locks. The below reproducer will trigger the assert in a kernel with CONFIG_LOCKDEP enabled by racing process teardown (which will release the hugetlb lock) against a hugetlb split. void execute_one(void) { void *ptr; pid_t pid; /* * Create a hugetlb mapping spanning a PUD entry. * * We force the hugetlb page allocation with populate and * noreserve. * * |---------------------| * | | * |---------------------| * 0 PUD boundary */ ptr = mmap(0, PUD_SIZE, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_SHARED | MAP_ANON | MAP_NORESERVE | MAP_HUGETLB | MAP_POPULATE, -1, 0); if (ptr == MAP_FAILED) { perror("mmap"); exit(EXIT_FAILURE); } /* * Fork but with a bogus stack pointer so we try to execute code in * a non-VM_EXEC VMA, causing segfault + teardown via exit_mmap(). * * The clone will cause PMD page table sharing between the * processes first via: * copy_process() -> ... -> huge_pte_alloc() -> huge_pmd_share() * * Then tear down and release the hugetlb 'VMA' lock via: * exit_mmap() -> ... -> vma_close() -> hugetlb_vma_lock_free() */ pid = syscall(__NR_clone, 0, 2 * PMD_SIZE, 0, 0, 0); if (pid < 0) { perror("clone"); exit(EXIT_FAILURE); } if (pid == 0) { /* Pop stack... */ return; } /* * We are the parent process. * * Race the child process's teardown with a PMD unshare. * * We do this by triggering: * * __split_vma() -> hugetlb_split() -> hugetlb_unshare_pmds() * * Which, importantly, doesn't hold the hugetlb VMA lock (nor can * it), meaning we assert in hugetlb_vma_assert_locked(). * * . * |----------.----------| * | . | * |----------.----------| * 0 . PUD boundary */ mmap(0, PUD_SIZE / 2, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_ANON | MAP_PRIVATE, -1, 0); } int main(void) { int i; /* Kick off fork children. */ for (i = 0; i < NUM_FORKS; i++) { pid_t pid = fork(); if (pid < 0) { perror("fork"); exit(EXIT_FAILURE); } /* Fork children do their work and exit. */ if (!pid) { int j; for (j = 0; j < NUM_ITERS; j++) execute_one(); return EXIT_SUCCESS; } } /* If we succeeded, wait on children. */ for (i = 0; i < NUM_FORKS; i++) wait(NULL); return EXIT_SUCCESS; } [ljs@kernel.org: account for the !CONFIG_HUGETLB_PMD_PAGE_TABLE_SHARING case] Link: https://lore.kernel.org/agWZsPGYid08uU6O@lucifer Link: https://lore.kernel.org/20260513085658.45264-1-ljs@kernel.org Fixes: 081056dc00a2 ("mm/hugetlb: unshare page tables during VMA split, not before") Signed-off-by: Lorenzo Stoakes Acked-by: David Hildenbrand (Arm) Acked-by: Oscar Salvador Cc: Jann Horn Cc: Muchun Song Cc: Signed-off-by: Andrew Morton --- mm/hugetlb.c | 56 ++++++++++++++++++++++++++++++++++------------------ 1 file changed, 37 insertions(+), 19 deletions(-) diff --git a/mm/hugetlb.c b/mm/hugetlb.c index 4b80b167cc9c..ece86d9339ce 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -118,6 +118,9 @@ static int hugetlb_acct_memory(struct hstate *h, long delta); static void hugetlb_vma_lock_free(struct vm_area_struct *vma); static void hugetlb_vma_lock_alloc(struct vm_area_struct *vma); static void __hugetlb_vma_unlock_write_free(struct vm_area_struct *vma); +static int __huge_pmd_unshare(struct mmu_gather *tlb, + struct vm_area_struct *vma, unsigned long addr, pte_t *ptep, + bool check_locks); static void hugetlb_unshare_pmds(struct vm_area_struct *vma, unsigned long start, unsigned long end, bool take_locks); static struct resv_map *vma_resv_map(struct vm_area_struct *vma); @@ -6891,6 +6894,31 @@ pte_t *huge_pmd_share(struct mm_struct *mm, struct vm_area_struct *vma, return pte; } +static int __huge_pmd_unshare(struct mmu_gather *tlb, + struct vm_area_struct *vma, unsigned long addr, pte_t *ptep, + bool check_locks) +{ + unsigned long sz = huge_page_size(hstate_vma(vma)); + struct mm_struct *mm = vma->vm_mm; + pgd_t *pgd = pgd_offset(mm, addr); + p4d_t *p4d = p4d_offset(pgd, addr); + pud_t *pud = pud_offset(p4d, addr); + + if (sz != PMD_SIZE) + return 0; + if (!ptdesc_pmd_is_shared(virt_to_ptdesc(ptep))) + return 0; + i_mmap_assert_write_locked(vma->vm_file->f_mapping); + if (check_locks) + hugetlb_vma_assert_locked(vma); + pud_clear(pud); + + tlb_unshare_pmd_ptdesc(tlb, virt_to_ptdesc(ptep), addr); + + mm_dec_nr_pmds(mm); + return 1; +} + /** * huge_pmd_unshare - Unmap a pmd table if it is shared by multiple users * @tlb: the current mmu_gather. @@ -6910,24 +6938,7 @@ pte_t *huge_pmd_share(struct mm_struct *mm, struct vm_area_struct *vma, int huge_pmd_unshare(struct mmu_gather *tlb, struct vm_area_struct *vma, unsigned long addr, pte_t *ptep) { - unsigned long sz = huge_page_size(hstate_vma(vma)); - struct mm_struct *mm = vma->vm_mm; - pgd_t *pgd = pgd_offset(mm, addr); - p4d_t *p4d = p4d_offset(pgd, addr); - pud_t *pud = pud_offset(p4d, addr); - - if (sz != PMD_SIZE) - return 0; - if (!ptdesc_pmd_is_shared(virt_to_ptdesc(ptep))) - return 0; - i_mmap_assert_write_locked(vma->vm_file->f_mapping); - hugetlb_vma_assert_locked(vma); - pud_clear(pud); - - tlb_unshare_pmd_ptdesc(tlb, virt_to_ptdesc(ptep), addr); - - mm_dec_nr_pmds(mm); - return 1; + return __huge_pmd_unshare(tlb, vma, addr, ptep, /*check_locks=*/true); } /* @@ -6961,6 +6972,13 @@ pte_t *huge_pmd_share(struct mm_struct *mm, struct vm_area_struct *vma, return NULL; } +static int __huge_pmd_unshare(struct mmu_gather *tlb, + struct vm_area_struct *vma, unsigned long addr, pte_t *ptep, + bool check_locks) +{ + return 0; +} + int huge_pmd_unshare(struct mmu_gather *tlb, struct vm_area_struct *vma, unsigned long addr, pte_t *ptep) { @@ -7269,7 +7287,7 @@ static void hugetlb_unshare_pmds(struct vm_area_struct *vma, if (!ptep) continue; ptl = huge_pte_lock(h, mm, ptep); - huge_pmd_unshare(&tlb, vma, address, ptep); + __huge_pmd_unshare(&tlb, vma, address, ptep, take_locks); spin_unlock(ptl); } huge_pmd_unshare_flush(&tlb, vma); From c0cafe24d3f6534294c4b2bc2d47734ff7cbd313 Mon Sep 17 00:00:00 2001 From: Shakeel Butt Date: Thu, 21 May 2026 15:37:51 -0700 Subject: [PATCH 218/337] memcg: use round-robin victim selection in refill_stock Harry Yoo reported that get_random_u32_below() is not safe to call in the nmi context and memcg charge draining can happen in nmi context. More specifically get_random_u32_below() is neither reentrant- nor NMI-safe: it acquires a per-cpu local_lock via local_lock_irqsave() on the batched_entropy_u32 state. An NMI that lands on a CPU mid-update of the ChaCha batch state and recurses into the random subsystem would corrupt that state. The memcg_stock local_trylock prevents re-entry on the percpu stock itself, but cannot protect an unrelated subsystem's per-cpu lock. Replace the random pick with a per-cpu round-robin counter stored in memcg_stock_pcp and serialized by the same local_trylock that already guards cached[] and nr_pages[]. No atomics, no random calls, no extra locks needed. Link: https://lore.kernel.org/20260521223751.3794625-1-shakeel.butt@linux.dev Fixes: f735eebe55f8f ("memcg: multi-memcg percpu charge cache") Signed-off-by: Shakeel Butt Reported-by: Harry Yoo Closes: https://lore.kernel.org/4e20f643-6983-4b6e-b12d-c6c4eb20ae0c@kernel.org/ Acked-by: Harry Yoo (Oracle) Acked-by: Michal Hocko Cc: Johannes Weiner Cc: Muchun Song Cc: Roman Gushchin Cc: Signed-off-by: Andrew Morton --- mm/memcontrol.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 177732fef010..1a4fd2504bcd 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -2011,6 +2011,7 @@ struct memcg_stock_pcp { struct work_struct work; unsigned long flags; + uint8_t drain_idx; }; static DEFINE_PER_CPU_ALIGNED(struct memcg_stock_pcp, memcg_stock) = { @@ -2194,7 +2195,9 @@ static void refill_stock(struct mem_cgroup *memcg, unsigned int nr_pages) if (!success) { i = empty_slot; if (i == -1) { - i = get_random_u32_below(NR_MEMCG_STOCK); + i = stock->drain_idx++; + if (stock->drain_idx == NR_MEMCG_STOCK) + stock->drain_idx = 0; drain_stock(stock, i); } css_get(&memcg->css); From c0ca59beb5252ea2bd4fdaef009d003dedc2030e Mon Sep 17 00:00:00 2001 From: Muchun Song Date: Wed, 20 May 2026 14:10:25 +0800 Subject: [PATCH 219/337] mm/cma_debug: fix invalid accesses for inactive CMA areas cma_activate_area() can fail after allocating range bitmaps. Its cleanup path frees those bitmaps, but only clears cma->count and cma->available_count. It leaves cma->nranges and each range's count in place, so cma_debugfs_init() can still register debugfs files for an area that never activated successfully. That exposes two problems. Reading the bitmap file can make debugfs walk a freed range bitmap and trigger an invalid memory access. Reading maxchunk can also take cma->lock even though that lock is initialized only on the successful activation path. Fix this by creating debugfs entries only for CMA areas that reached CMA_ACTIVATED. c009da4258f9 introduced the invalid access to bitmap file. 2e32b947606d introduced the invalid access to cma->lock. This change applies to both issues. So I added two Fixes tags. Link: https://lore.kernel.org/20260520061025.3971821-1-songmuchun@bytedance.com Fixes: c009da4258f9 ("mm, cma: support multiple contiguous ranges, if requested") Fixes: 2e32b947606d ("mm: cma: add functions to get region pages counters") Signed-off-by: Muchun Song Acked-by: Mike Rapoport (Microsoft) Acked-by: Oscar Salvador (SUSE) Acked-by: David Hildenbrand (Arm) Cc: Dmitry Safonov <0x7f454c46@gmail.com> Cc: Frank van der Linden Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Michal Nazarewicz Cc: Stefan Strogin Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Signed-off-by: Andrew Morton --- mm/cma_debug.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mm/cma_debug.c b/mm/cma_debug.c index 5ae38f5abbcc..523ba4a0f9f7 100644 --- a/mm/cma_debug.c +++ b/mm/cma_debug.c @@ -205,7 +205,8 @@ static int __init cma_debugfs_init(void) cma_debugfs_root = debugfs_create_dir("cma", NULL); for (i = 0; i < cma_area_count; i++) - cma_debugfs_add_one(&cma_areas[i], cma_debugfs_root); + if (test_bit(CMA_ACTIVATED, &cma_areas[i].flags)) + cma_debugfs_add_one(&cma_areas[i], cma_debugfs_root); return 0; } From 40c81856e622a9dc59294a90d169ac07ea25b0b0 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Wed, 20 May 2026 05:49:12 +0100 Subject: [PATCH 220/337] mm/hugetlb: restore reservation on error in hugetlb folio copy paths Two sites in mm/hugetlb.c allocate a hugetlb folio via alloc_hugetlb_folio() (consuming a VMA reservation) and then call copy_user_large_folio(), which became int-returning in commit 1cb9dc4b475c ("mm: hwpoison: support recovery from HugePage copy-on-write faults") and can now fail (e.g. -EHWPOISON on a hwpoisoned source page). On the failure path, folio_put() restores the global hugetlb pool count through free_huge_folio(), but the per-VMA reservation map entry is left marked consumed: - hugetlb_mfill_atomic_pte() resubmission path (UFFDIO_COPY) - copy_hugetlb_page_range() fork-time CoW path when hugetlb_try_dup_anon_rmap() fails (rare: pinned hugetlb anon folio under fork) User-visible effect: on UFFDIO_COPY into a private hugetlb VMA where the resubmission copy fails, the reservation for that address is leaked from the VMA's reserve map. A subsequent fault at the same address takes the no-reservation path, and under hugetlb pool pressure the task is SIGBUSed at an address it had previously reserved. The fork-time CoW path leaks the same way in the child VMA's reserve map, though it requires the much rarer combination of pinned hugetlb anon page + hwpoisoned source. Add the missing restore_reserve_on_error() call before folio_put() on both error paths. Link: https://lore.kernel.org/20260520044912.6751-1-devnexen@gmail.com Fixes: 1cb9dc4b475c ("mm: hwpoison: support recovery from HugePage copy-on-write faults") Signed-off-by: David Carlier Reviewed-by: Muchun Song Cc: David Hildenbrand Cc: Mina Almasry Cc: Muchun Song Cc: Oscar Salvador Cc: yuehaibing Cc: Signed-off-by: Andrew Morton --- mm/hugetlb.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mm/hugetlb.c b/mm/hugetlb.c index ece86d9339ce..1b1d4f87a3a4 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -4977,6 +4977,7 @@ int copy_hugetlb_page_range(struct mm_struct *dst, struct mm_struct *src, addr, dst_vma); folio_put(pte_folio); if (ret) { + restore_reserve_on_error(h, dst_vma, addr, new_folio); folio_put(new_folio); break; } @@ -6273,6 +6274,7 @@ int hugetlb_mfill_atomic_pte(pte_t *dst_pte, folio_put(*foliop); *foliop = NULL; if (ret) { + restore_reserve_on_error(h, dst_vma, dst_addr, folio); folio_put(folio); goto out; } From 3c2d42b8ee345b17a4ba56b0f6492d1ff4c1178e Mon Sep 17 00:00:00 2001 From: Wupeng Ma Date: Fri, 22 May 2026 09:03:05 +0800 Subject: [PATCH 221/337] mm/memory-failure: fix hugetlb_lock AA deadlock in get_huge_page_for_hwpoison Two concurrent madvise(MADV_HWPOISON) calls on the same hugetlb page can trigger a recursive spinlock self-deadlock (AA deadlock) on hugetlb_lock when racing with a concurrent unmap: thread#0 thread#1 -------- -------- madvise(folio, MADV_HWPOISON) -> poisons the folio successfully madvise(folio, MADV_HWPOISON) unmap(folio) try_memory_failure_hugetlb get_huge_page_for_hwpoison spin_lock_irq(&hugetlb_lock) <- held __get_huge_page_for_hwpoison hugetlb_update_hwpoison() -> MF_HUGETLB_FOLIO_PRE_POISONED goto out: folio_put() refcount: 1 -> 0 free_huge_folio() spin_lock_irqsave(&hugetlb_lock) -> AA DEADLOCK! The out: path in __get_huge_page_for_hwpoison() calls folio_put() to drop the GUP reference while the hugetlb_lock is still held by the hugetlb.c wrapper get_huge_page_for_hwpoison(). If concurrent unmap has released the page table mapping reference, folio_put() drops the folio refcount to zero, triggering free_huge_folio() which attempts to re-acquire the non-recursive hugetlb_lock. Fix this by moving hugetlb_lock acquisition from the hugetlb.c wrapper into get_huge_page_for_hwpoison(). Place spin_unlock_irq() before the folio_put() at the out: label so the folio is always released outside the lock. [akpm@linux-foundation.org: fix race, rename label per Miaohe] Link: https://sashiko.dev/#/patchset/20260522010305.4099834-1-mawupeng1@huawei.com Link: https://lore.kernel.org/f39f405e-4b4b-8f79-70fe-a2b5b62114eb@huawei.com Link: https://lore.kernel.org/20260522010305.4099834-1-mawupeng1@huawei.com Fixes: 405ce051236c ("mm/hwpoison: fix race between hugetlb free/demotion and memory_failure_hugetlb()") Signed-off-by: Wupeng Ma Acked-by: Oscar Salvador (SUSE) Acked-by: Muchun Song Reviewed-by: Kefeng Wang Acked-by: Miaohe Lin Cc: David Hildenbrand Cc: Liam Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Naoya Horiguchi Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Signed-off-by: Andrew Morton --- include/linux/hugetlb.h | 8 -------- include/linux/mm.h | 8 -------- mm/hugetlb.c | 11 ----------- mm/memory-failure.c | 19 ++++++++++--------- 4 files changed, 10 insertions(+), 36 deletions(-) diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h index 5957bc25efa8..2abaf99321e9 100644 --- a/include/linux/hugetlb.h +++ b/include/linux/hugetlb.h @@ -153,8 +153,6 @@ long hugetlb_unreserve_pages(struct inode *inode, long start, long end, long freed); bool folio_isolate_hugetlb(struct folio *folio, struct list_head *list); int get_hwpoison_hugetlb_folio(struct folio *folio, bool *hugetlb, bool unpoison); -int get_huge_page_for_hwpoison(unsigned long pfn, int flags, - bool *migratable_cleared); void folio_putback_hugetlb(struct folio *folio); void move_hugetlb_state(struct folio *old_folio, struct folio *new_folio, int reason); void hugetlb_fix_reserve_counts(struct inode *inode); @@ -421,12 +419,6 @@ static inline int get_hwpoison_hugetlb_folio(struct folio *folio, bool *hugetlb, return 0; } -static inline int get_huge_page_for_hwpoison(unsigned long pfn, int flags, - bool *migratable_cleared) -{ - return 0; -} - static inline void folio_putback_hugetlb(struct folio *folio) { } diff --git a/include/linux/mm.h b/include/linux/mm.h index 06bbe9eba636..fc2acedf0b76 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -4975,8 +4975,6 @@ extern int soft_offline_page(unsigned long pfn, int flags); */ extern const struct attribute_group memory_failure_attr_group; extern void memory_failure_queue(unsigned long pfn, int flags); -extern int __get_huge_page_for_hwpoison(unsigned long pfn, int flags, - bool *migratable_cleared); void num_poisoned_pages_inc(unsigned long pfn); void num_poisoned_pages_sub(unsigned long pfn, long i); #else @@ -4984,12 +4982,6 @@ static inline void memory_failure_queue(unsigned long pfn, int flags) { } -static inline int __get_huge_page_for_hwpoison(unsigned long pfn, int flags, - bool *migratable_cleared) -{ - return 0; -} - static inline void num_poisoned_pages_inc(unsigned long pfn) { } diff --git a/mm/hugetlb.c b/mm/hugetlb.c index 1b1d4f87a3a4..c921287489de 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -7161,17 +7161,6 @@ int get_hwpoison_hugetlb_folio(struct folio *folio, bool *hugetlb, bool unpoison return ret; } -int get_huge_page_for_hwpoison(unsigned long pfn, int flags, - bool *migratable_cleared) -{ - int ret; - - spin_lock_irq(&hugetlb_lock); - ret = __get_huge_page_for_hwpoison(pfn, flags, migratable_cleared); - spin_unlock_irq(&hugetlb_lock); - return ret; -} - /** * folio_putback_hugetlb - unisolate a hugetlb folio * @folio: the isolated hugetlb folio diff --git a/mm/memory-failure.c b/mm/memory-failure.c index ee42d4361309..d47aef256a32 100644 --- a/mm/memory-failure.c +++ b/mm/memory-failure.c @@ -1966,20 +1966,19 @@ void folio_clear_hugetlb_hwpoison(struct folio *folio) folio_free_raw_hwp(folio, true); } -/* - * Called from hugetlb code with hugetlb_lock held. - */ -int __get_huge_page_for_hwpoison(unsigned long pfn, int flags, +static int get_huge_page_for_hwpoison(unsigned long pfn, int flags, bool *migratable_cleared) { struct page *page = pfn_to_page(pfn); - struct folio *folio = page_folio(page); + struct folio *folio; bool count_increased = false; int ret, rc; + spin_lock_irq(&hugetlb_lock); + folio = page_folio(page); if (!folio_test_hugetlb(folio)) { ret = MF_HUGETLB_NON_HUGEPAGE; - goto out; + goto out_unlock; } else if (flags & MF_COUNT_INCREASED) { ret = MF_HUGETLB_IN_USED; count_increased = true; @@ -1995,13 +1994,13 @@ int __get_huge_page_for_hwpoison(unsigned long pfn, int flags, } else { ret = MF_HUGETLB_RETRY; if (!(flags & MF_NO_RETRY)) - goto out; + goto out_unlock; } rc = hugetlb_update_hwpoison(folio, page); if (rc >= MF_HUGETLB_FOLIO_PRE_POISONED) { ret = rc; - goto out; + goto out_unlock; } /* @@ -2013,8 +2012,10 @@ int __get_huge_page_for_hwpoison(unsigned long pfn, int flags, *migratable_cleared = true; } + spin_unlock_irq(&hugetlb_lock); return ret; -out: +out_unlock: + spin_unlock_irq(&hugetlb_lock); if (count_increased) folio_put(folio); return ret; From 00739e4dd46dde2b39dd9dd19a27e3c8af4ca0d0 Mon Sep 17 00:00:00 2001 From: Muchun Song Date: Sat, 23 May 2026 14:01:23 +0800 Subject: [PATCH 222/337] mm/cma: fix reserved page leak on activation failure If cma_activate_area() fails after allocating only part of the range bitmaps, the cleanup path still has to release the reserved pages when CMA_RESERVE_PAGES_ON_ERROR is clear. That is still worth doing even in this __init path. A bitmap_zalloc() failure does not necessarily mean the system cannot make further progress: freeing the reserved CMA pages can return a substantial amount of memory to the buddy allocator and may relieve the temporary memory shortage that caused the allocation failure in the first place. However, the cleanup path currently uses the bitmap-freeing bound for page release as well. That is only correct for ranges whose bitmap allocation already succeeded. The failed range and all later ranges still keep their reserved pages, so a partial bitmap allocation failure can permanently leak them. Fix this by releasing reserved pages for all ranges. Use the saved early_pfn[] value for ranges whose bitmap allocation already succeeded and for the failed range, and use cmr->early_pfn for later ranges whose bitmap allocation was never attempted. Link: https://lore.kernel.org/20260523060123.2207992-1-songmuchun@bytedance.com Fixes: c009da4258f9 ("mm, cma: support multiple contiguous ranges, if requested") Signed-off-by: Muchun Song Reviewed-by: Oscar Salvador (SUSE) Acked-by: Usama Arif Cc: David Hildenbrand Cc: Frank van der Linden Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Signed-off-by: Andrew Morton --- mm/cma.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/mm/cma.c b/mm/cma.c index c7ca567f4c5c..a13ce4999b39 100644 --- a/mm/cma.c +++ b/mm/cma.c @@ -188,10 +188,13 @@ static void __init cma_activate_area(struct cma *cma) /* Expose all pages to the buddy, they are useless for CMA. */ if (!test_bit(CMA_RESERVE_PAGES_ON_ERROR, &cma->flags)) { - for (r = 0; r < allocrange; r++) { + for (r = 0; r < cma->nranges; r++) { + unsigned long start_pfn; + cmr = &cma->ranges[r]; + start_pfn = r <= allocrange ? early_pfn[r] : cmr->early_pfn; end_pfn = cmr->base_pfn + cmr->count; - for (pfn = early_pfn[r]; pfn < end_pfn; pfn++) + for (pfn = start_pfn; pfn < end_pfn; pfn++) free_reserved_page(pfn_to_page(pfn)); } } From 5ab62dd3687bcc2cc542b99385aabac5c996db6f Mon Sep 17 00:00:00 2001 From: Rajat Gupta Date: Wed, 20 May 2026 22:11:21 -0700 Subject: [PATCH 223/337] drm: prevent integer overflows in dumb buffer creation helpers Fix integer overflow issues in the dumb buffer creation path: 1. drm_mode_create_dumb() does not bound width, height, or bpp before passing them to driver callbacks. Downstream helpers (e.g. drm_gem_dma_dumb_create_internal) perform pitch/size alignment in u32 arithmetic that can overflow for extreme values. Add hard limits: width and height < 8192, bpp <= 32. No legitimate software rendering use case exceeds these. 2. drm_mode_align_dumb() uses roundup(pitch, hw_pitch_align) without checking for overflow. If pitch is near U32_MAX, roundup() wraps to a small value, making subsequent check_mul_overflow() pass with a much smaller pitch than intended. Add an overflow check after roundup. 3. drm_mode_align_dumb() uses ALIGN(size, hw_size_align) which only works correctly for power-of-two alignment values. Replace with roundup() which works for any alignment. Suggested-by: Thomas Zimmermann Signed-off-by: Rajat Gupta Signed-off-by: Thomas Zimmermann --- drivers/gpu/drm/drm_dumb_buffers.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/drm_dumb_buffers.c b/drivers/gpu/drm/drm_dumb_buffers.c index e2b62e5fb891..cc99681a9ed0 100644 --- a/drivers/gpu/drm/drm_dumb_buffers.c +++ b/drivers/gpu/drm/drm_dumb_buffers.c @@ -70,8 +70,11 @@ static int drm_mode_align_dumb(struct drm_mode_create_dumb *args, if (!pitch) return -EINVAL; - if (hw_pitch_align) + if (hw_pitch_align) { pitch = roundup(pitch, hw_pitch_align); + if (pitch < hw_pitch_align) + return -EINVAL; + } if (!hw_size_align) hw_size_align = PAGE_SIZE; @@ -80,7 +83,7 @@ static int drm_mode_align_dumb(struct drm_mode_create_dumb *args, if (check_mul_overflow(args->height, pitch, &size)) return -EINVAL; - size = ALIGN(size, hw_size_align); + size = roundup(size, hw_size_align); if (!size) return -EINVAL; @@ -199,6 +202,13 @@ int drm_mode_create_dumb(struct drm_device *dev, if (!args->width || !args->height || !args->bpp) return -EINVAL; + /* Reject unreasonable inputs early. Dumb buffers are for software + * rendering; nothing legitimate needs more than 8192x8192 at 32bpp. + * This prevents overflows in downstream alignment helpers. + */ + if (args->width >= 8192 || args->height >= 8192 || args->bpp > 32) + return -EINVAL; + /* overflow checks for 32bit size calculations */ if (args->bpp > U32_MAX - 8) return -EINVAL; From a0d8f7ac03e387634c5a7efe3dc162f7d605e2cd Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 7 May 2026 00:56:49 +0300 Subject: [PATCH 224/337] Revert "media: renesas: vsp1: Initialize format on all pads" This reverts commit 133ac42af0a1b389e8b7b3dc7c1cc8c30ff162b6. The change to format initialization, along with the change to format propagation in the BRx in commit 937f3e6b51f1 ("media: renesas: vsp1: brx: Fix format propagation"), broke configuration of the DRM pipeline. Revert it to fix the regression. The original commit was meant to fix a v4l2-compliance failure, with no known userspace applications being affected beside test tools. Reverting is the simplest option, a more comprehensive fix can be developed (and tested more thoroughly) later. Fixes: 133ac42af0a1 ("media: renesas: vsp1: Initialize format on all pads") Tested-by: Lad Prabhakar # On RZ/T2H Reviewed-by: Lad Prabhakar Link: https://patch.msgid.link/20260506215650.1897177-2-laurent.pinchart+renesas@ideasonboard.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- drivers/media/platform/renesas/vsp1/vsp1_entity.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/renesas/vsp1/vsp1_entity.c b/drivers/media/platform/renesas/vsp1/vsp1_entity.c index 1dad9589768c..839b75b62ceb 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_entity.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_entity.c @@ -380,7 +380,7 @@ static int vsp1_entity_init_state(struct v4l2_subdev *subdev, unsigned int pad; /* Initialize all pad formats with default values. */ - for (pad = 0; pad < subdev->entity.num_pads; ++pad) { + for (pad = 0; pad < subdev->entity.num_pads - 1; ++pad) { struct v4l2_subdev_format format = { .pad = pad, .which = sd_state ? V4L2_SUBDEV_FORMAT_TRY From f78073e84c800ae146ce62447e7a685a5ceeb92d Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 7 May 2026 00:56:50 +0300 Subject: [PATCH 225/337] Revert "media: renesas: vsp1: brx: Fix format propagation" This reverts commit 937f3e6b51f1cea079be9ba642665f2bf8bcc31f. The change to format propagation in the BRx broke configuration of the DRM pipeline. Revert it to fix the regression. The original commit was meant to fix a v4l2-compliance failure, with no known userspace applications being affected beside test tools. Reverting is the simplest option, a more comprehensive fix can be developed (and tested more thoroughly) later. Reported-by: Lad Prabhakar Closes: https://lore.kernel.org/linux-media/CA+V-a8t481xuwava0nb7uY9CUPqFWZ_8EP0xrK3BgumP7HDcLg@mail.gmail.com Fixes: 937f3e6b51f1 ("media: renesas: vsp1: brx: Fix format propagation") Tested-by: Lad Prabhakar # On RZ/T2H Reviewed-by: Lad Prabhakar Link: https://patch.msgid.link/20260506215650.1897177-3-laurent.pinchart+renesas@ideasonboard.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- drivers/media/platform/renesas/vsp1/vsp1_brx.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/drivers/media/platform/renesas/vsp1/vsp1_brx.c b/drivers/media/platform/renesas/vsp1/vsp1_brx.c index b1a2c68e9944..9d93cb8b8e82 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_brx.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_brx.c @@ -156,20 +156,14 @@ static int brx_set_format(struct v4l2_subdev *subdev, compose->height = format->height; } - /* - * Propagate the format code to all pads, and the whole format to the - * source pad. - */ + /* Propagate the format code to all pads. */ if (fmt->pad == BRX_PAD_SINK(0)) { unsigned int i; - for (i = 0; i < brx->entity.source_pad; ++i) { + for (i = 0; i <= brx->entity.source_pad; ++i) { format = v4l2_subdev_state_get_format(state, i); format->code = fmt->format.code; } - - format = v4l2_subdev_state_get_format(state, i); - *format = fmt->format; } done: From c889b146478885344a220dd468e5a08de088cbc5 Mon Sep 17 00:00:00 2001 From: Tudor Ambarus Date: Tue, 5 May 2026 13:13:02 +0000 Subject: [PATCH 226/337] firmware: samsung: acpm: Fix false timeouts and Use-After-Free in polling Sashiko identified severe races in the polling state machine [1]. In the ACPM driver's polling mode, threads waited for responses by monitoring the globally shared 'bitmap_seqnum'. This caused false timeouts because if a thread processed its response and freed the sequence number, a concurrent TX thread could immediately reallocate it before the polling thread woke up. Additionally, the driver suffered from a cross-thread Use-After-Free (UAF) preemption race. Previously, acpm_get_rx() cleared the sequence number of whichever RX message it drained from the hardware queue. This meant Thread A could globally free Thread B's sequence slot while Thread B was asleep. A new Thread C could then steal the slot, overwrite the buffer, and leave Thread B to wake up to corrupted state or a timeout. Fix this by rewriting the polling state machine: 1. Decouple polling from the global allocator by introducing a per-slot 'completed' flag, synchronized via smp_store_release() and smp_load_acquire(). 2. Strip acpm_get_saved_rx() out of acpm_get_rx() to make it a pure queue-draining function. Introduce a 'native_match' boolean argument which evaluates to true only if the thread natively processed its own sequence number during the call. This explicitly informs the polling loop whether it must retrieve its payload from the cross-thread cache. 3. Centralize the cache fallback and sequence number free (clear_bit) inside the polling loop. Crucially, the free operation now strictly targets the thread's own TX sequence number (xfer->txd[0]), rather than the drained RX sequence number. This enforces strict ownership: a thread only ever frees its own allocated sequence slot, and only at the exact moment it completes its poll, eliminating the UAF window. Furthermore, explicitly guard the 'native_match' assignment with an if (rx_seqnum == tx_seqnum) check, even for zero-length (no payload) responses. While an unguarded assignment wouldn't crash (because the cache fallback acpm_get_saved_rx() safely returns early on zero-length transfers) doing so would "lie" to the state machine. If a thread drained the queue and found another thread's zero-length message, setting native_match = true would falsely convince the polling loop that it natively handled its own response. Maintaining a rigorous state machine requires that native_match is only set when a thread explicitly processes its own sequence number. Cc: stable@vger.kernel.org Fixes: a88927b534ba ("firmware: add Exynos ACPM protocol driver") Closes: https://sashiko.dev/#/patchset/20260429-acpm-fixes-sashiko-reports-v3-0-47cf74ab09ad%40linaro.org [1] Signed-off-by: Tudor Ambarus Link: https://patch.msgid.link/20260505-acpm-fixes-sashiko-reports-v5-5-43b5ee7f1674@linaro.org Signed-off-by: Krzysztof Kozlowski --- drivers/firmware/samsung/exynos-acpm.c | 68 ++++++++++++++++++-------- 1 file changed, 48 insertions(+), 20 deletions(-) diff --git a/drivers/firmware/samsung/exynos-acpm.c b/drivers/firmware/samsung/exynos-acpm.c index 9766425a44ab..620880d8820a 100644 --- a/drivers/firmware/samsung/exynos-acpm.c +++ b/drivers/firmware/samsung/exynos-acpm.c @@ -105,11 +105,14 @@ struct acpm_queue { * @cmd: pointer to where the data shall be saved. * @n_cmd: number of 32-bit commands. * @rxcnt: expected length of the response in 32-bit words. + * @completed: flag indicating if the firmware response has been fully + * processed. */ struct acpm_rx_data { u32 *cmd; size_t n_cmd; size_t rxcnt; + bool completed; }; #define ACPM_SEQNUM_MAX 64 @@ -204,26 +207,28 @@ static void acpm_get_saved_rx(struct acpm_chan *achan, rx_seqnum = FIELD_GET(ACPM_PROTOCOL_SEQNUM, rx_data->cmd[0]); - if (rx_seqnum == tx_seqnum) { + if (rx_seqnum == tx_seqnum) memcpy(xfer->rxd, rx_data->cmd, xfer->rxcnt * sizeof(*xfer->rxd)); - clear_bit(rx_seqnum - 1, achan->bitmap_seqnum); - } } /** * acpm_get_rx() - get response from RX queue. * @achan: ACPM channel info. * @xfer: reference to the transfer to get response for. + * @native_match: pointer to a boolean set to true if the thread natively + * processed its own sequence number during this call. * * Return: 0 on success, -errno otherwise. */ -static int acpm_get_rx(struct acpm_chan *achan, const struct acpm_xfer *xfer) +static int acpm_get_rx(struct acpm_chan *achan, const struct acpm_xfer *xfer, + bool *native_match) { u32 rx_front, rx_seqnum, tx_seqnum, seqnum; const void __iomem *base, *addr; struct acpm_rx_data *rx_data; u32 i, val, mlen; - bool rx_set = false; + + *native_match = false; guard(mutex)(&achan->rx_lock); @@ -232,10 +237,8 @@ static int acpm_get_rx(struct acpm_chan *achan, const struct acpm_xfer *xfer) tx_seqnum = FIELD_GET(ACPM_PROTOCOL_SEQNUM, xfer->txd[0]); - if (i == rx_front) { - acpm_get_saved_rx(achan, xfer, tx_seqnum); + if (i == rx_front) return 0; - } base = achan->rx.base; mlen = achan->mlen; @@ -259,8 +262,13 @@ static int acpm_get_rx(struct acpm_chan *achan, const struct acpm_xfer *xfer) if (rx_data->rxcnt) { if (rx_seqnum == tx_seqnum) { __ioread32_copy(xfer->rxd, addr, xfer->rxcnt); - rx_set = true; - clear_bit(seqnum, achan->bitmap_seqnum); + /* + * Signal completion to the polling thread. + * Pairs with smp_load_acquire() in polling + * loop. + */ + smp_store_release(&rx_data->completed, true); + *native_match = true; } else { /* * The RX data corresponds to another request. @@ -270,9 +278,21 @@ static int acpm_get_rx(struct acpm_chan *achan, const struct acpm_xfer *xfer) */ __ioread32_copy(rx_data->cmd, addr, rx_data->rxcnt); + /* + * Signal completion to the polling thread. + * Pairs with smp_load_acquire() in polling + * loop. + */ + smp_store_release(&rx_data->completed, true); } } else { - clear_bit(seqnum, achan->bitmap_seqnum); + /* + * Signal completion to the polling thread. + * Pairs with smp_load_acquire() in polling loop. + */ + smp_store_release(&rx_data->completed, true); + if (rx_seqnum == tx_seqnum) + *native_match = true; } i = (i + 1) % achan->qlen; @@ -281,13 +301,6 @@ static int acpm_get_rx(struct acpm_chan *achan, const struct acpm_xfer *xfer) /* We saved all responses, mark RX empty. */ writel(rx_front, achan->rx.rear); - /* - * If the response was not in this iteration of the queue, check if the - * RX data was previously saved. - */ - if (!rx_set) - acpm_get_saved_rx(achan, xfer, tx_seqnum); - return 0; } @@ -302,6 +315,7 @@ static int acpm_dequeue_by_polling(struct acpm_chan *achan, const struct acpm_xfer *xfer) { struct device *dev = achan->acpm->dev; + bool native_match; ktime_t timeout; u32 seqnum; int ret; @@ -310,12 +324,25 @@ static int acpm_dequeue_by_polling(struct acpm_chan *achan, timeout = ktime_add_us(ktime_get(), ACPM_POLL_TIMEOUT_US); do { - ret = acpm_get_rx(achan, xfer); + ret = acpm_get_rx(achan, xfer, &native_match); if (ret) return ret; - if (!test_bit(seqnum - 1, achan->bitmap_seqnum)) + /* + * Safely check if our specific transaction has been processed. + * smp_load_acquire prevents the CPU from speculatively + * executing subsequent instructions before the transaction is + * synchronized. + */ + if (smp_load_acquire(&achan->rx_data[seqnum - 1].completed)) { + /* Retrieve payload if another thread cached it for us */ + if (!native_match) + acpm_get_saved_rx(achan, xfer, seqnum); + + /* Relinquish ownership of the sequence slot */ + clear_bit(seqnum - 1, achan->bitmap_seqnum); return 0; + } /* Determined experimentally. */ udelay(20); @@ -380,6 +407,7 @@ static void acpm_prepare_xfer(struct acpm_chan *achan, /* Clear data for upcoming responses */ rx_data = &achan->rx_data[achan->seqnum - 1]; + rx_data->completed = false; memset(rx_data->cmd, 0, sizeof(*rx_data->cmd) * rx_data->n_cmd); /* zero means no response expected */ rx_data->rxcnt = xfer->rxcnt; From bf296f83a3ddab1ab875edc4e8862cb10553064f Mon Sep 17 00:00:00 2001 From: Tudor Ambarus Date: Tue, 5 May 2026 13:13:03 +0000 Subject: [PATCH 227/337] firmware: samsung: acpm: Fix missing LKMM barriers in sequence allocator Sashiko identified memory ordering races in [1]. The ACPM driver uses a globally shared 'bitmap_seqnum' to track available sequence numbers. Even though threads now strictly free their own sequence numbers, the allocation and freeing of these bits across concurrent threads are effectively lockless operations and require explicit LKMM memory barriers. Previously, the driver used plain bitwise operators (test_bit, set_bit, clear_bit), which lack ordering guarantees. This creates two race conditions on weakly ordered architectures like ARM64: 1. Polling Release Violation: The polling thread copies its payload and calls clear_bit(). Without a release barrier, the CPU can reorder the memory operations, making the cleared bit globally visible before the payload reads have fully completed. 2. TX Acquire Violation: The TX thread loops on test_bit(), calls set_bit(), and then wipes the payload buffer via memset(). Without an acquire barrier, the CPU can speculatively execute the memset() before the bit is safely and formally claimed. If these reorderings overlap, a new TX thread can claim the sequence number and overwrite the buffer while the original polling thread is still actively reading from it. Fix this by upgrading the bitwise operators. Wrap the TX allocation in test_and_set_bit_lock() to establish formal LKMM Acquire semantics, and pair it with clear_bit_unlock() in the polling path to enforce Release semantics. Cc: stable@vger.kernel.org Fixes: a88927b534ba ("firmware: add Exynos ACPM protocol driver") Closes: https://sashiko.dev/#/patchset/20260423-acpm-fixes-sashiko-reports-v1-0-2217b790925e%40linaro.org [1] Signed-off-by: Tudor Ambarus Link: https://patch.msgid.link/20260505-acpm-fixes-sashiko-reports-v5-6-43b5ee7f1674@linaro.org Signed-off-by: Krzysztof Kozlowski --- drivers/firmware/samsung/exynos-acpm.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/drivers/firmware/samsung/exynos-acpm.c b/drivers/firmware/samsung/exynos-acpm.c index 620880d8820a..3fa9fe283be4 100644 --- a/drivers/firmware/samsung/exynos-acpm.c +++ b/drivers/firmware/samsung/exynos-acpm.c @@ -7,7 +7,7 @@ #include #include -#include +#include #include #include #include @@ -340,7 +340,7 @@ static int acpm_dequeue_by_polling(struct acpm_chan *achan, acpm_get_saved_rx(achan, xfer, seqnum); /* Relinquish ownership of the sequence slot */ - clear_bit(seqnum - 1, achan->bitmap_seqnum); + clear_bit_unlock(seqnum - 1, achan->bitmap_seqnum); return 0; } @@ -397,11 +397,18 @@ static void acpm_prepare_xfer(struct acpm_chan *achan, struct acpm_rx_data *rx_data; u32 *txd = (u32 *)xfer->txd; - /* Prevent chan->seqnum from being re-used */ + /* + * Prevent chan->seqnum from being re-used. + * test_and_set_bit_lock() provides formal LKMM Acquire semantics. + * It pairs with the RX thread's clear_bit_unlock() to ensure the CPU + * does not speculatively execute the rx_data buffer wipe (memset) + * before the sequence number is safely claimed. + */ do { if (++achan->seqnum == ACPM_SEQNUM_MAX) achan->seqnum = 1; - } while (test_bit(achan->seqnum - 1, achan->bitmap_seqnum)); + /* Flag the index based on seqnum. (seqnum: 1~63, bitmap: 0~62) */ + } while (test_and_set_bit_lock(achan->seqnum - 1, achan->bitmap_seqnum)); txd[0] |= FIELD_PREP(ACPM_PROTOCOL_SEQNUM, achan->seqnum); @@ -411,9 +418,6 @@ static void acpm_prepare_xfer(struct acpm_chan *achan, memset(rx_data->cmd, 0, sizeof(*rx_data->cmd) * rx_data->n_cmd); /* zero means no response expected */ rx_data->rxcnt = xfer->rxcnt; - - /* Flag the index based on seqnum. (seqnum: 1~63, bitmap: 0~62) */ - set_bit(achan->seqnum - 1, achan->bitmap_seqnum); } /** From 7fe40c32a33905302341797b5d12c541729dd08d Mon Sep 17 00:00:00 2001 From: Tudor Ambarus Date: Tue, 5 May 2026 13:13:04 +0000 Subject: [PATCH 228/337] firmware: samsung: acpm: Fix infinite loop on sequence number exhaustion Sashiko identified a possible infinite loop [1]. ACPM IPC sequence numbers are tracked via a 64-bit bitmap. Previously, acpm_prepare_xfer() used a do...while loop to search for a free sequence number. If all 63 available sequence numbers are leaked due to transient hardware timeouts or mailbox failures, the bitmap becomes full. The next call to acpm_prepare_xfer() would enter an infinite loop. Fix this by utilizing the kernel's optimized bitmap search functions (find_next_zero_bit / find_first_zero_bit). If the pool is completely exhausted, log the failure and return -EBUSY to allow the kernel to fail gracefully instead of hanging. Furthermore, drop the allocation loop entirely. Because acpm_prepare_xfer() is strictly called under the 'tx_lock' mutex, sequence number allocations are perfectly serialized. If find_next_zero_bit() locates a free bit, a single test_and_set_bit_lock() is mathematically guaranteed to succeed. To enforce this locking invariant, wrap the allocation in a WARN_ON_ONCE. If the atomic set fails, it indicates the driver's mutex serialization is fundamentally broken. The warning generates a stack trace for debugging, while returning -EIO immediately aborts the transfer to prevent silent payload corruption. Cc: stable@vger.kernel.org Fixes: a88927b534ba ("firmware: add Exynos ACPM protocol driver") Closes: https://sashiko.dev/#/patchset/20260420-acpm-tmu-v3-0-3dc8e93f0b26%40linaro.org [1] Signed-off-by: Tudor Ambarus Link: https://patch.msgid.link/20260505-acpm-fixes-sashiko-reports-v5-7-43b5ee7f1674@linaro.org Signed-off-by: Krzysztof Kozlowski --- drivers/firmware/samsung/exynos-acpm.c | 45 ++++++++++++++++++-------- 1 file changed, 31 insertions(+), 14 deletions(-) diff --git a/drivers/firmware/samsung/exynos-acpm.c b/drivers/firmware/samsung/exynos-acpm.c index 3fa9fe283be4..19db3674a28f 100644 --- a/drivers/firmware/samsung/exynos-acpm.c +++ b/drivers/firmware/samsung/exynos-acpm.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -390,34 +391,48 @@ static int acpm_wait_for_queue_slots(struct acpm_chan *achan, u32 next_tx_front) * TX queue. * @achan: ACPM channel info. * @xfer: reference to the transfer being prepared. + * + * Return: 0 on success, -errno otherwise. */ -static void acpm_prepare_xfer(struct acpm_chan *achan, - const struct acpm_xfer *xfer) +static int acpm_prepare_xfer(struct acpm_chan *achan, + const struct acpm_xfer *xfer) { struct acpm_rx_data *rx_data; u32 *txd = (u32 *)xfer->txd; + unsigned long size = ACPM_SEQNUM_MAX - 1; + unsigned long bit = achan->seqnum; + + bit = find_next_zero_bit(achan->bitmap_seqnum, size, bit); + if (bit >= size) { + bit = find_first_zero_bit(achan->bitmap_seqnum, size); + if (bit >= size) { + dev_err_ratelimited(achan->acpm->dev, + "ACPM sequence number pool exhausted\n"); + return -EBUSY; + } + } /* - * Prevent chan->seqnum from being re-used. - * test_and_set_bit_lock() provides formal LKMM Acquire semantics. - * It pairs with the RX thread's clear_bit_unlock() to ensure the CPU - * does not speculatively execute the rx_data buffer wipe (memset) - * before the sequence number is safely claimed. + * Execute the atomic set to formally claim the bit and establish + * LKMM Acquire semantics against the RX thread's clear_bit_unlock(). + * A loop is unnecessary because allocations are strictly serialized + * by tx_lock. */ - do { - if (++achan->seqnum == ACPM_SEQNUM_MAX) - achan->seqnum = 1; - /* Flag the index based on seqnum. (seqnum: 1~63, bitmap: 0~62) */ - } while (test_and_set_bit_lock(achan->seqnum - 1, achan->bitmap_seqnum)); + if (WARN_ON_ONCE(test_and_set_bit_lock(bit, achan->bitmap_seqnum))) + return -EIO; + /* Flag the index based on seqnum. (seqnum: 1~63, bitmap: 0~62) */ + achan->seqnum = bit + 1; txd[0] |= FIELD_PREP(ACPM_PROTOCOL_SEQNUM, achan->seqnum); /* Clear data for upcoming responses */ - rx_data = &achan->rx_data[achan->seqnum - 1]; + rx_data = &achan->rx_data[bit]; rx_data->completed = false; memset(rx_data->cmd, 0, sizeof(*rx_data->cmd) * rx_data->n_cmd); /* zero means no response expected */ rx_data->rxcnt = xfer->rxcnt; + + return 0; } /** @@ -477,7 +492,9 @@ int acpm_do_xfer(struct acpm_handle *handle, const struct acpm_xfer *xfer) if (ret) return ret; - acpm_prepare_xfer(achan, xfer); + ret = acpm_prepare_xfer(achan, xfer); + if (ret) + return ret; /* Write TX command. */ __iowrite32_copy(achan->tx.base + achan->mlen * tx_front, From 8d4ae34e997062076a9098602eaca43353665bd9 Mon Sep 17 00:00:00 2001 From: Huan He Date: Sat, 9 May 2026 16:49:07 +0800 Subject: [PATCH 229/337] mmc: sdhci-of-dwcmshc: Fix reset, clk, and SDIO support for Eswin EIC7700 The EIC7700 code in sdhci-of-dwcmshc uses host->mmc->caps2 to select different configuration paths for different card types. The current logic distinguishes eMMC and SD, but does not handle SDIO separately. Update the EIC7700 card-type checks so that eMMC, SD and SDIO are distinguished explicitly. Switch the reset path to dwcmshc_reset() so that pending interrupt state is cleared consistently, and use sdhci_enable_clk() so the clock enable sequence follows the standard SDHCI flow. Fixes: 32b2633219d3 ("mmc: sdhci-of-dwcmshc: Add support for Eswin EIC7700") Signed-off-by: Huan He Acked-by: Adrian Hunter Cc: stable@vger.kernel.org Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-of-dwcmshc.c | 44 ++++++++++++++--------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/drivers/mmc/host/sdhci-of-dwcmshc.c b/drivers/mmc/host/sdhci-of-dwcmshc.c index 0b2158a7e409..b9ecd91f44ad 100644 --- a/drivers/mmc/host/sdhci-of-dwcmshc.c +++ b/drivers/mmc/host/sdhci-of-dwcmshc.c @@ -277,6 +277,7 @@ #define PHY_DELAY_CODE_MAX 0x7f #define PHY_DELAY_CODE_EMMC 0x17 #define PHY_DELAY_CODE_SD 0x55 +#define PHY_DELAY_CODE_SDIO 0x29 struct rk35xx_priv { struct reset_control *reset; @@ -1433,10 +1434,7 @@ static void sdhci_eic7700_set_clock(struct sdhci_host *host, unsigned int clock) clk_set_rate(pltfm_host->clk, clock); clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL); - clk |= SDHCI_CLOCK_INT_EN; - sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); - - dwcmshc_enable_card_clk(host); + sdhci_enable_clk(host, clk); } static void sdhci_eic7700_config_phy_delay(struct sdhci_host *host, int delay) @@ -1497,7 +1495,7 @@ static void sdhci_eic7700_config_phy(struct sdhci_host *host) static void sdhci_eic7700_reset(struct sdhci_host *host, u8 mask) { - sdhci_reset(host, mask); + dwcmshc_reset(host, mask); /* after reset all, the phy's config will be clear */ if (mask == SDHCI_RESET_ALL) @@ -1594,18 +1592,17 @@ static int sdhci_eic7700_phase_code_tuning(struct sdhci_host *host, u32 opcode) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host); - u32 sd_caps = MMC_CAP2_NO_MMC | MMC_CAP2_NO_SDIO; + u32 emmc_caps = MMC_CAP2_NO_SD | MMC_CAP2_NO_SDIO; int phase_code = -1; int code_range = -1; - bool is_sd = false; int code_min = -1; int code_max = -1; int cmd_error = 0; + bool is_emmc; int ret = 0; int i = 0; - if ((host->mmc->caps2 & sd_caps) == sd_caps) - is_sd = true; + is_emmc = (host->mmc->caps2 & emmc_caps) == emmc_caps; for (i = 0; i <= MAX_PHASE_CODE; i++) { /* Centered Phase code */ @@ -1614,8 +1611,8 @@ static int sdhci_eic7700_phase_code_tuning(struct sdhci_host *host, u32 opcode) host->ops->reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA); if (ret) { - /* SD specific range tracking */ - if (is_sd && code_min != -1 && code_max != -1) { + /* SD/SDIO specific range tracking */ + if (!is_emmc && code_min != -1 && code_max != -1) { if (code_max - code_min > code_range) { code_range = code_max - code_min; phase_code = (code_min + code_max) / 2; @@ -1626,17 +1623,17 @@ static int sdhci_eic7700_phase_code_tuning(struct sdhci_host *host, u32 opcode) code_max = -1; } /* EMMC breaks after first valid range */ - if (!is_sd && code_min != -1 && code_max != -1) + if (is_emmc && code_min != -1 && code_max != -1) break; } else { /* Track valid phase code range */ if (code_min == -1) { code_min = i; - if (!is_sd) + if (is_emmc) continue; } code_max = i; - if (is_sd && i == MAX_PHASE_CODE) { + if (!is_emmc && i == MAX_PHASE_CODE) { if (code_max - code_min > code_range) { code_range = code_max - code_min; phase_code = (code_min + code_max) / 2; @@ -1646,19 +1643,19 @@ static int sdhci_eic7700_phase_code_tuning(struct sdhci_host *host, u32 opcode) } /* Handle tuning failure case */ - if ((is_sd && phase_code == -1) || - (!is_sd && code_min == -1 && code_max == -1)) { + if ((!is_emmc && phase_code == -1) || + (is_emmc && code_min == -1 && code_max == -1)) { pr_err("%s: phase code tuning failed!\n", mmc_hostname(host->mmc)); sdhci_writew(host, 0, priv->vendor_specific_area1 + DWCMSHC_AT_STAT); return -EIO; } - if (!is_sd) + if (is_emmc) phase_code = (code_min + code_max) / 2; sdhci_writew(host, phase_code, priv->vendor_specific_area1 + DWCMSHC_AT_STAT); - /* SD specific final verification */ - if (is_sd) { + /* SD/SDIO specific final verification */ + if (!is_emmc) { ret = mmc_send_tuning(host->mmc, opcode, &cmd_error); host->ops->reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA); if (ret) { @@ -1756,9 +1753,9 @@ static void sdhci_eic7700_set_uhs_signaling(struct sdhci_host *host, unsigned in static void sdhci_eic7700_set_uhs_wrapper(struct sdhci_host *host, unsigned int timing) { - u32 sd_caps = MMC_CAP2_NO_MMC | MMC_CAP2_NO_SDIO; + u32 emmc_caps = MMC_CAP2_NO_SD | MMC_CAP2_NO_SDIO; - if ((host->mmc->caps2 & sd_caps) == sd_caps) + if ((host->mmc->caps2 & emmc_caps) != emmc_caps) sdhci_set_uhs_signaling(host, timing); else sdhci_eic7700_set_uhs_signaling(host, timing); @@ -1767,6 +1764,7 @@ static void sdhci_eic7700_set_uhs_wrapper(struct sdhci_host *host, unsigned int static int eic7700_init(struct device *dev, struct sdhci_host *host, struct dwcmshc_priv *dwc_priv) { u32 emmc_caps = MMC_CAP2_NO_SD | MMC_CAP2_NO_SDIO; + u32 sd_caps = MMC_CAP2_NO_MMC | MMC_CAP2_NO_SDIO; unsigned int val, hsp_int_status, hsp_pwr_ctrl; static const char * const clk_ids[] = {"axi"}; struct of_phandle_args args; @@ -1821,8 +1819,10 @@ static int eic7700_init(struct device *dev, struct sdhci_host *host, struct dwcm if ((host->mmc->caps2 & emmc_caps) == emmc_caps) dwc_priv->delay_line = PHY_DELAY_CODE_EMMC; - else + else if ((host->mmc->caps2 & sd_caps) == sd_caps) dwc_priv->delay_line = PHY_DELAY_CODE_SD; + else + dwc_priv->delay_line = PHY_DELAY_CODE_SDIO; if (!of_property_read_u32(dev->of_node, "eswin,drive-impedance-ohms", &val)) priv->drive_impedance = eic7700_convert_drive_impedance_ohm(dev, val); From f48ee49726ee4ab545fd2dc644f169c0809b19b3 Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Tue, 19 May 2026 14:53:40 +0100 Subject: [PATCH 230/337] mmc: renesas_sdhi: Add OF entry for RZ/G2H SoC The RZ/G2H (R8A774E1) SoC was previously handled via the generic "renesas,rcar-gen3-sdhi" fallback compatible string. However, because the SDHI IP on RZ/G2H is identical with the R-Car H3-N (R8A77951), it requires the specific quirks and configuration defined in `of_r8a7795_compatible` rather than the generic Gen3 data. Add the explicit "renesas,sdhi-r8a774e1" match entry to map it correctly. Note that the DT binding file renesas,sdhi.yaml does not need an update as the entry for this SoC is already present. Fixes: 31941342888d ("arm64: dts: renesas: r8a774e1: Add SDHI nodes") Cc: stable@vger.kernel.org Signed-off-by: Lad Prabhakar Reviewed-by: Wolfram Sang Reviewed-by: Geert Uytterhoeven Signed-off-by: Ulf Hansson --- drivers/mmc/host/renesas_sdhi_internal_dmac.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mmc/host/renesas_sdhi_internal_dmac.c b/drivers/mmc/host/renesas_sdhi_internal_dmac.c index f6ebb7bc7ede..838248bf8dd6 100644 --- a/drivers/mmc/host/renesas_sdhi_internal_dmac.c +++ b/drivers/mmc/host/renesas_sdhi_internal_dmac.c @@ -279,6 +279,7 @@ static const struct renesas_sdhi_of_data_with_quirks of_rza2_compatible = { static const struct of_device_id renesas_sdhi_internal_dmac_of_match[] = { { .compatible = "renesas,sdhi-r7s9210", .data = &of_rza2_compatible, }, { .compatible = "renesas,sdhi-mmc-r8a77470", .data = &of_rcar_gen3_compatible, }, + { .compatible = "renesas,sdhi-r8a774e1", .data = &of_r8a7795_compatible, }, { .compatible = "renesas,sdhi-r8a7795", .data = &of_r8a7795_compatible, }, { .compatible = "renesas,sdhi-r8a77961", .data = &of_r8a77961_compatible, }, { .compatible = "renesas,sdhi-r8a77965", .data = &of_r8a77965_compatible, }, From b837e38c255dd9f8b53511d52e87f1fda32b3dfe Mon Sep 17 00:00:00 2001 From: Inochi Amaoto Date: Thu, 21 May 2026 15:21:20 +0800 Subject: [PATCH 231/337] mmc: litex_mmc: Use DIV_ROUND_UP for more accurate clock calculation The previous clock uses roundup_pow_of_two() to calculate the core clock frequency. It does not meet the actual hardware meaning. The actual frequency is calculated by "ref_clk / ((div >> 1) << 1)". Fix the clock divider calculation. Fixes: 92e099104729 ("mmc: Add driver for LiteX's LiteSDCard interface") Signed-off-by: Inochi Amaoto Reviewed-by: Gabriel Somlo Cc: stable@vger.kernel.org Signed-off-by: Ulf Hansson --- drivers/mmc/host/litex_mmc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/host/litex_mmc.c b/drivers/mmc/host/litex_mmc.c index d2f19c2dc673..52571bb17c61 100644 --- a/drivers/mmc/host/litex_mmc.c +++ b/drivers/mmc/host/litex_mmc.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -436,11 +437,10 @@ static void litex_mmc_setclk(struct litex_mmc_host *host, unsigned int freq) struct device *dev = mmc_dev(host->mmc); u32 div; - div = freq ? host->ref_clk / freq : 256U; - div = roundup_pow_of_two(div); + div = freq ? DIV_ROUND_UP(host->ref_clk, freq) : 256U; div = clamp(div, 2U, 256U); dev_dbg(dev, "sd_clk_freq=%d: set to %d via div=%d\n", - freq, host->ref_clk / div, div); + freq, host->ref_clk / ((div + 1) & ~1U), div); litex_write16(host->sdphy + LITEX_PHY_CLOCKERDIV, div); host->sd_clk = freq; } From 99982b743e5ba72bd1f5de0e03e3b96ae70b1e51 Mon Sep 17 00:00:00 2001 From: Inochi Amaoto Date: Thu, 21 May 2026 15:21:21 +0800 Subject: [PATCH 232/337] mmc: litex_mmc: Set mandatory idle clocks before CMD0 The litex_mmc driver assumes the card is already probed in the BIOS and skip the phy initialization. This will cause the command fail like the following when the old card is unplugged and then insert a new card: [ 62.923593] litex-mmc f0004000.mmc: Command (cmd 8) error, status -110 [ 62.949717] litex-mmc f0004000.mmc: Command (cmd 55) error, status -110 [ 62.976606] litex-mmc f0004000.mmc: Command (cmd 55) error, status -110 [ 63.002516] litex-mmc f0004000.mmc: Command (cmd 55) error, status -110 [ 63.028442] litex-mmc f0004000.mmc: Command (cmd 55) error, status -110 Add required clock settings and initialization for the CMD 0, so it can probe the new card. Fixes: 92e099104729 ("mmc: Add driver for LiteX's LiteSDCard interface") Signed-off-by: Inochi Amaoto Reviewed-by: Gabriel Somlo Cc: stable@vger.kernel.org Signed-off-by: Ulf Hansson --- drivers/mmc/host/litex_mmc.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/mmc/host/litex_mmc.c b/drivers/mmc/host/litex_mmc.c index 52571bb17c61..3655542ca998 100644 --- a/drivers/mmc/host/litex_mmc.c +++ b/drivers/mmc/host/litex_mmc.c @@ -69,6 +69,9 @@ #define SD_SLEEP_US 5 #define SD_TIMEOUT_US 20000 +#define SD_INIT_DELAY_US 1000 +#define SD_INIT_CLK_HZ 400000 + #define SDIRQ_CARD_DETECT 1 #define SDIRQ_SD_TO_MEM_DONE 2 #define SDIRQ_MEM_TO_SD_DONE 4 @@ -449,6 +452,17 @@ static void litex_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) { struct litex_mmc_host *host = mmc_priv(mmc); + /* + * The SD specification requires at least 74 idle clocks before CMD0. + * These dummy cycles is generated by writing LITEX_PHY_INITIALIZE. + */ + if (ios->chip_select == MMC_CS_HIGH) { + litex_mmc_setclk(host, SD_INIT_CLK_HZ); + litex_write8(host->sdphy + LITEX_PHY_INITIALIZE, 1); + fsleep(SD_INIT_DELAY_US); + return; + } + /* * NOTE: Ignore any ios->bus_width updates; they occur right after * the mmc core sends its own acmd6 bus-width change notification, From 1e9a4850afa0ceb63984fb1a9f3e86d0fc4fd18f Mon Sep 17 00:00:00 2001 From: Heiko Stuebner Date: Fri, 22 May 2026 20:43:07 +0200 Subject: [PATCH 233/337] mmc: dw_mmc-rockchip: Add missing private data for very old controllers The really old controllers (rk2928, rk3066, rk3188) do not support UHS speeds at all, and thus never handled phase data. For that reason it never had a parse_dt callback and no driver private data at all. Commit ff6f0286c896 ("mmc: dw_mmc-rockchip: Add memory clock auto-gating support") makes the private data sort of mandatory, because the init function checks whether phases are configured internally or through the clock controller. This results in the old SoCs then experiencing NULL-pointer dereferences when they try to access that private-data struct. While we could have if (priv) conditionals in all places, it's way less cluttery to just give the old types their private-data struct. Fixes: ff6f0286c896 ("mmc: dw_mmc-rockchip: Add memory clock auto-gating support") Cc: stable@vger.kernel.org Signed-off-by: Heiko Stuebner Acked-by: Shawn Lin Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc-rockchip.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/drivers/mmc/host/dw_mmc-rockchip.c b/drivers/mmc/host/dw_mmc-rockchip.c index c6eece4ec3fd..75c82ff20f17 100644 --- a/drivers/mmc/host/dw_mmc-rockchip.c +++ b/drivers/mmc/host/dw_mmc-rockchip.c @@ -441,6 +441,22 @@ static int dw_mci_common_parse_dt(struct dw_mci *host) return 0; } +static int dw_mci_rk2928_parse_dt(struct dw_mci *host) +{ + struct dw_mci_rockchip_priv_data *priv; + int err; + + err = dw_mci_common_parse_dt(host); + if (err) + return err; + + priv = host->priv; + + priv->internal_phase = false; + + return 0; +} + static int dw_mci_rk3288_parse_dt(struct dw_mci *host) { struct dw_mci_rockchip_priv_data *priv; @@ -514,6 +530,7 @@ static int dw_mci_rockchip_init(struct dw_mci *host) static const struct dw_mci_drv_data rk2928_drv_data = { .init = dw_mci_rockchip_init, + .parse_dt = dw_mci_rk2928_parse_dt, }; static const struct dw_mci_drv_data rk3288_drv_data = { From f595e8e77a51eee35e331f69321766593a845ef2 Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Sun, 24 May 2026 10:34:55 +0800 Subject: [PATCH 234/337] mmc: sdhci: add signal voltage switch in sdhci_resume_host I met one suspend/resume issue with sdr104 capable sdio wifi card (with "keep-power-in-suspend" set in DT property): After resuming from suspend to ram, the sdio wifi card stops working. Further debug shows that although ios shows the sdio card is at sdr104 mode, the voltage is still at 3V3. This is due to missing the calling of ->start_signal_voltage_switch() in sdhci_resume_host(). Fix this issue by adding ->start_signal_voltage_switch() in sdhci_resume_host(). This also matches what we do for sdhci_runtime_resume_host(). Then the question is: why this issue hasn't reported and fixed for so long time. IMHO, several reasons: Some host controllers just kick off the runtime resume for system resume, so they benefit from the well supported runtime pm code; Some platforms just use the old sdio wifi card which doesn't need signal voltage switch at all, the default voltage is 3v3 after resuming. Fixes: 6308d2905bd3 ("mmc: sdhci: add quirk for keeping card power during suspend") Signed-off-by: Jisheng Zhang Acked-by: Adrian Hunter Cc: stable@vger.kernel.org Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 605be55f8d2d..e3bf901b10aa 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -3836,6 +3836,7 @@ int sdhci_resume_host(struct sdhci_host *host) host->pwr = 0; host->clock = 0; host->reinit_uhs = true; + mmc->ops->start_signal_voltage_switch(mmc, &mmc->ios); mmc->ops->set_ios(mmc, &mmc->ios); } else { sdhci_init(host, (mmc->pm_flags & MMC_PM_KEEP_POWER)); From 7164d78559b0ff29931a366a840a9e5dd53d4b7c Mon Sep 17 00:00:00 2001 From: Zhenghang Xiao Date: Tue, 26 May 2026 16:53:13 +0800 Subject: [PATCH 235/337] drm/gem: fix race between change_handle and handle_delete drm_gem_change_handle_ioctl leaves the old handle live in the IDR during the window between spin_unlock(table_lock) and the final spin_lock(table_lock). A concurrent drm_gem_handle_delete on the old handle succeeds in this window, decrements handle_count to 0, and frees the GEM object while the new handle's IDR entry still references it. NULL the old handle's IDR entry before dropping table_lock so that any concurrent GEM_CLOSE on the old handle sees NULL and returns -EINVAL. Restore the old entry on the prime-bookkeeping error path. Fixes: 5e28b7b94408 ("drm: Set old handle to NULL before prime swap in change_handle") Signed-off-by: Zhenghang Xiao Cc: stable@vger.kernel.org Signed-off-by: Dave Airlie Link: https://patch.msgid.link/20260526085313.26791-1-kipreyyy@gmail.com --- drivers/gpu/drm/drm_gem.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index e3b8a1f353cb..e12cdf91f4dc 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -1065,6 +1065,7 @@ int drm_gem_change_handle_ioctl(struct drm_device *dev, void *data, goto out_unlock; } + idr_replace(&file_priv->object_idr, NULL, args->handle); spin_unlock(&file_priv->table_lock); if (obj->dma_buf) { @@ -1073,6 +1074,7 @@ int drm_gem_change_handle_ioctl(struct drm_device *dev, void *data, if (ret < 0) { spin_lock(&file_priv->table_lock); idr_remove(&file_priv->object_idr, handle); + idr_replace(&file_priv->object_idr, obj, args->handle); spin_unlock(&file_priv->table_lock); goto out_unlock; } From 44eeff9bc467bc7d1fec34fc3f6001f385fe462c Mon Sep 17 00:00:00 2001 From: Andrei Vagin Date: Tue, 26 May 2026 20:50:43 +0000 Subject: [PATCH 236/337] Revert "x86/fpu: Refine and simplify the magic number check during signal return" This reverts dc8aa31a7ac2 ("x86/fpu: Refine and simplify the magic number check during signal return"). The aforementioned commit broke applications that construct signal frames in userspace (such as CRIU and gVisor) if the frame's xstate size is smaller than the kernel's fpstate->user_size. Furthermore, this introduces a critical issue for checkpoint/restore tools like CRIU. If a process is checkpointed while inside a signal handler, its stack contains a signal frame formatted according to the source host's xstate capabilities. If that process is later restored on a destination host with larger xstate capabilities (e.g., a newer CPU with more features enabled, resulting in a larger fpstate->user_size), the kernel will look for FP_XSTATE_MAGIC2 at the destination host's larger user_size offset instead of the offset encoded in the frame's fx_sw->xstate_size. This causes the magic2 check to fail, forcing sigreturn to silently fall back to "FX-only" mode. Upon return from the signal handler, the process's extended state is reset to initial values instead of being restored, leading to silent data corruption. The aforementioned commit cited d877550eaf2d ("x86/fpu: Stop relying on userspace for info to fault in xsave buffer") as justification to stop relying on userspace for the magic number check. However, these two changes are fundamentally different. The last one only changed how much memory the kernel ensures is paged-in before running XRSTOR to prevent an infinite loop. It did not change the signal frame format or how the layout is validated. Reverting this change restores the use of fx_sw->xstate_size for locating magic2 and restores the necessary sanity checks, ensuring that the signal frame remains self-describing and portable. [ bp: Massage commit message. ] Fixes: dc8aa31a7ac2 ("x86/fpu: Refine and simplify the magic number check during signal return") Signed-off-by: Andrei Vagin Signed-off-by: Borislav Petkov (AMD) Acked-by: Chang S. Bae Cc: stable@vger.kernel.org Link: https://lore.kernel.org/all/20260429000623.3356606-1-avagin@google.com --- arch/x86/kernel/fpu/signal.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/arch/x86/kernel/fpu/signal.c b/arch/x86/kernel/fpu/signal.c index c3ec2512f2bb..20b638c507ca 100644 --- a/arch/x86/kernel/fpu/signal.c +++ b/arch/x86/kernel/fpu/signal.c @@ -27,14 +27,19 @@ static inline bool check_xstate_in_sigframe(struct fxregs_state __user *fxbuf, struct _fpx_sw_bytes *fx_sw) { + int min_xstate_size = sizeof(struct fxregs_state) + + sizeof(struct xstate_header); void __user *fpstate = fxbuf; unsigned int magic2; if (__copy_from_user(fx_sw, &fxbuf->sw_reserved[0], sizeof(*fx_sw))) return false; - /* Check for the first magic field */ - if (fx_sw->magic1 != FP_XSTATE_MAGIC1) + /* Check for the first magic field and other error scenarios. */ + if (fx_sw->magic1 != FP_XSTATE_MAGIC1 || + fx_sw->xstate_size < min_xstate_size || + fx_sw->xstate_size > x86_task_fpu(current)->fpstate->user_size || + fx_sw->xstate_size > fx_sw->extended_size) goto setfx; /* @@ -43,7 +48,7 @@ static inline bool check_xstate_in_sigframe(struct fxregs_state __user *fxbuf, * fpstate layout with out copying the extended state information * in the memory layout. */ - if (__get_user(magic2, (__u32 __user *)(fpstate + x86_task_fpu(current)->fpstate->user_size))) + if (__get_user(magic2, (__u32 __user *)(fpstate + fx_sw->xstate_size))) return false; if (likely(magic2 == FP_XSTATE_MAGIC2)) From 7c6535c37dbc03c1c35926b7420d66fb122b513a Mon Sep 17 00:00:00 2001 From: Marco Elver Date: Thu, 21 May 2026 14:27:23 +0200 Subject: [PATCH 237/337] s390: Implement _THIS_IP_ using inline asm Both GCC [1] and Clang [2] consider the generic version of _THIS_IP_ to be broken: #define _THIS_IP_ ({ __label__ __here; __here: (unsigned long)&&__here; }) In particular, the address of a label is only expected to be used with a computed goto. While the generic version more or less works today, it is known to be brittle and may break with current and future optimizations. For example, Clang -O2 always returns 1 when this function is inlined: static inline unsigned long get_ip(void) { return ({ __label__ __here; __here: (unsigned long)&&__here; }); } Fix it by overriding _THIS_IP_ in (which is included by ) using an architecture-specific inline asm version. Additionally, avoiding taking the address of a label prevents compilers from emitting spurious indirect branch targets (e.g. ENDBR or BTI) under control-flow integrity schemes. Link: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=120071 [1] Link: https://github.com/llvm/llvm-project/issues/138272 [2] Signed-off-by: Marco Elver Reviewed-by: Heiko Carstens Signed-off-by: Alexander Gordeev --- arch/s390/include/asm/linkage.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/s390/include/asm/linkage.h b/arch/s390/include/asm/linkage.h index df3fb7d8227b..1b3ac553a642 100644 --- a/arch/s390/include/asm/linkage.h +++ b/arch/s390/include/asm/linkage.h @@ -7,4 +7,6 @@ #define __ALIGN .balign CONFIG_FUNCTION_ALIGNMENT, 0x07 #define __ALIGN_STR __stringify(__ALIGN) +#define _THIS_IP_ ({ unsigned long __ip; asm volatile("larl %0, ." : "=d" (__ip)); __ip; }) + #endif From 05d5d79440c2cc0784f91b61f2012753e66be472 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sat, 30 May 2026 12:25:36 +0200 Subject: [PATCH 238/337] Revert "gpib: cb7210: Fix region leak when request_irq fails" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 2eae90a457baa0048a96ed38ad93090ee38c8b2f. Turns out not to be correct. Link: https://lore.kernel.org/r/PpNUbGhrvT8I_KayoDvQYI2PYjmMw1QEkuVBDZz2PwBsVVgPkBXJarc2mBM0IhiH3AQG0GtgqEsDRXNj3yUKEDBaZa25u73pAjvcE6vfRsg=@protonmail.com Reported-by: Dominik Karol Piątkowski Cc: Mark Brown Cc: Hongling Zeng Cc: Hongling Zeng Signed-off-by: Greg Kroah-Hartman --- drivers/gpib/cb7210/cb7210.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/drivers/gpib/cb7210/cb7210.c b/drivers/gpib/cb7210/cb7210.c index 673b5bfe2e7d..6dd8637c5964 100644 --- a/drivers/gpib/cb7210/cb7210.c +++ b/drivers/gpib/cb7210/cb7210.c @@ -1049,8 +1049,7 @@ static int cb_isa_attach(struct gpib_board *board, const struct gpib_board_confi if (!request_region(config->ibbase, cb7210_iosize, DRV_NAME)) { dev_err(board->gpib_dev, "ioports starting at 0x%x are already in use\n", config->ibbase); - retval = -EBUSY; - goto err_release_region; + return -EBUSY; } nec_priv->iobase = config->ibbase; cb_priv->fifo_iobase = nec7210_iobase(cb_priv); @@ -1063,16 +1062,11 @@ static int cb_isa_attach(struct gpib_board *board, const struct gpib_board_confi // install interrupt handler if (request_irq(config->ibirq, cb7210_interrupt, isr_flags, DRV_NAME, board)) { dev_err(board->gpib_dev, "failed to obtain IRQ %d\n", config->ibirq); - retval = -EBUSY; - goto err_release_region; + return -EBUSY; } cb_priv->irq = config->ibirq; return cb7210_init(cb_priv, board); - -err_release_region: - release_region(nec7210_iobase(cb_priv), cb7210_iosize); - return retval; } static void cb_isa_detach(struct gpib_board *board) From 1d774589f924056b8403e271fdecaf9a803a50fc Mon Sep 17 00:00:00 2001 From: Alexis Bouzigues Date: Fri, 29 May 2026 09:28:14 -0500 Subject: [PATCH 239/337] i2c: virtio: mark device ready before registering the adapter virtio_i2c_probe() synchronously probes child i2c drivers on the bus, but peripherals may use the bus at probe for tasks like reading a chip id. The vhost-user-i2c backend stalls at such probes unless DRIVER_OK is already set before the virtqueue is first kicked. Set DRIVER_OK explicitly before i2c_add_adapter(), as done for the same reason in commit f5866db64f34 ("virtio_console: enable VQs early") and commit 71e4b8bf0482 ("virtio_rpmsg: set DRIVER_OK before using device"). Signed-off-by: Alexis Bouzigues Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-virtio.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/i2c/busses/i2c-virtio.c b/drivers/i2c/busses/i2c-virtio.c index 7b0b0bff8000..5da6fef92bec 100644 --- a/drivers/i2c/busses/i2c-virtio.c +++ b/drivers/i2c/busses/i2c-virtio.c @@ -222,6 +222,8 @@ static int virtio_i2c_probe(struct virtio_device *vdev) */ ACPI_COMPANION_SET(&vi->adap.dev, ACPI_COMPANION(vdev->dev.parent)); + virtio_device_ready(vdev); + ret = i2c_add_adapter(&vi->adap); if (ret) virtio_i2c_del_vqs(vdev); From 171022c7d594c133a45f92357a2a91475edabe20 Mon Sep 17 00:00:00 2001 From: Henri A Date: Wed, 20 May 2026 10:25:44 -0400 Subject: [PATCH 240/337] media: rc: igorplugusb: fix control request setup packet Commit eac69475b01f ("media: rc: igorplugusb: heed coherency rules") changed the control request storage from an embedded struct to an allocated pointer so it can obey DMA coherency rules. However, the driver still passes &ir->request to usb_fill_control_urb(). That points the URB setup packet at the pointer field itself rather than at the allocated struct usb_ctrlrequest. USB core then interprets pointer bytes as the setup packet. This can produce an invalid bRequestType and trigger the control direction warning reported by syzbot: usb 2-1: BOGUS control dir, pipe 80003580 doesn't match bRequestType 0 Pass ir->request itself as the setup packet. Fixes: eac69475b01f ("media: rc: igorplugusb: heed coherency rules") Reported-by: syzbot+11f0e4f957c7c3bf3d51@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=11f0e4f957c7c3bf3d51 Tested-by: syzbot+11f0e4f957c7c3bf3d51@syzkaller.appspotmail.com Cc: stable@vger.kernel.org Assisted-by: Codex:GPT-5.5 Signed-off-by: Henri A Signed-off-by: Sean Young Signed-off-by: Hans Verkuil --- drivers/media/rc/igorplugusb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/rc/igorplugusb.c b/drivers/media/rc/igorplugusb.c index 3e10f6fe89f8..b5117ee9f5fa 100644 --- a/drivers/media/rc/igorplugusb.c +++ b/drivers/media/rc/igorplugusb.c @@ -184,7 +184,7 @@ static int igorplugusb_probe(struct usb_interface *intf, if (!ir->buf_in) goto fail; usb_fill_control_urb(ir->urb, udev, - usb_rcvctrlpipe(udev, 0), (uint8_t *)&ir->request, + usb_rcvctrlpipe(udev, 0), (uint8_t *)ir->request, ir->buf_in, MAX_PACKET, igorplugusb_callback, ir); usb_make_path(udev, ir->phys, sizeof(ir->phys)); From 0eaa1f245ac03ed0c6394159360532726f666811 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Fri, 29 May 2026 08:57:05 +0300 Subject: [PATCH 241/337] wifi: iwlwifi: mvm: don't support the reset handshake for old firmwares -77.ucode doesn't contain the fixes for this flow it seems. Don't use the firmware reset handshake even if the firmware claims support for it. Fixes: 906d4eb84408 ("iwlwifi: support firmware reset handshake") Closes: https://bugzilla.kernel.org/show_bug.cgi?id=220600 Signed-off-by: Emmanuel Grumbach Reviewed-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20260529085453.9307b81d9b02.I21bba9e649f4cd0e35d3ea6cd97a03258be5832f@changeid --- drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index ae177477b201..384bed95835d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -1416,6 +1416,12 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_rf_cfg *cfg, fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_FW_RESET_HANDSHAKE); + /* Those firmware versions claim to support the fw_reset_handshake + * but they are buggy. + */ + if (IWL_UCODE_MAJOR(mvm->fw->ucode_ver) <= 77) + trans->conf.fw_reset_handshake = false; + trans->conf.queue_alloc_cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, WIDE_ID(DATA_PATH_GROUP, From 9bf1b409afc7c4a1f0340f3975846c4f3278643a Mon Sep 17 00:00:00 2001 From: Pagadala Yesu Anjaneyulu Date: Fri, 29 May 2026 08:57:06 +0300 Subject: [PATCH 242/337] wifi: iwlwifi: mld: send tx power constraints before link activation TX power constraints must be sent to the firmware before link activation. If not, the firmware will use default power values. Fix this by moving the iwl_mld_send_ap_tx_power_constraint_cmd() call from iwl_mld_start_ap_ibss() to iwl_mld_assign_vif_chanctx(), before iwl_mld_activate_link() for AP interfaces. Also update the guard in the function to allow it to run before link activation for AP interfaces. Signed-off-by: Pagadala Yesu Anjaneyulu Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20260529085453.06c94b01efd2.Id43bdfe5eb030061c23348779687ba71b5f58182@changeid --- drivers/net/wireless/intel/iwlwifi/mld/ap.c | 4 ---- drivers/net/wireless/intel/iwlwifi/mld/mac80211.c | 7 +++++++ drivers/net/wireless/intel/iwlwifi/mld/power.c | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/ap.c b/drivers/net/wireless/intel/iwlwifi/mld/ap.c index 5c59acc8c4c5..6598d9333333 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/ap.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/ap.c @@ -9,7 +9,6 @@ #include "ap.h" #include "hcmd.h" #include "tx.h" -#include "power.h" #include "key.h" #include "phy.h" #include "iwl-utils.h" @@ -273,9 +272,6 @@ int iwl_mld_start_ap_ibss(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *ctx; int ret; - if (vif->type == NL80211_IFTYPE_AP) - iwl_mld_send_ap_tx_power_constraint_cmd(mld, vif, link); - ret = iwl_mld_update_beacon_template(mld, vif, link); if (ret) return ret; diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c index da6fd7471568..3c8daddc0bcb 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c @@ -1150,6 +1150,13 @@ int iwl_mld_assign_vif_chanctx(struct ieee80211_hw *hw, if (iwl_mld_can_activate_link(mld, vif, link)) { iwl_mld_tlc_update_phy(mld, vif, link); + /* FW requires AP_TX_POWER_CONSTRAINTS_CMD before link + * activation for AP and after link activation for STA, + * for an unknown reason. + */ + if (vif->type == NL80211_IFTYPE_AP) + iwl_mld_send_ap_tx_power_constraint_cmd(mld, vif, link); + ret = iwl_mld_activate_link(mld, link); if (ret) goto err; diff --git a/drivers/net/wireless/intel/iwlwifi/mld/power.c b/drivers/net/wireless/intel/iwlwifi/mld/power.c index 49b0d9f8f865..266fe16bb95d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/power.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/power.c @@ -366,7 +366,7 @@ iwl_mld_send_ap_tx_power_constraint_cmd(struct iwl_mld *mld, lockdep_assert_wiphy(mld->wiphy); - if (!mld_link->active) + if (!mld_link->active && vif->type != NL80211_IFTYPE_AP) return; if (link->chanreq.oper.chan->band != NL80211_BAND_6GHZ) From e0c121d545134af886b28c4c26d91abf5dd39c17 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Fri, 29 May 2026 08:57:07 +0300 Subject: [PATCH 243/337] wifi: iwlwifi: mvm: avoid oversized UATS command copy MCC_ALLOWED_AP_TYPE_CMD exceeds the fixed copied host-command buffer and triggers warnings in the gen2 enqueue path when command 0xc05 is sent. Use IWL_HCMD_DFL_NOCOPY as it was done before the offending commit. Fixes: 078df640ef05 ("wifi: iwlwifi: mld: add support for iwl_mcc_allowed_ap_type_cmd v2") Signed-off-by: Emmanuel Grumbach Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20260529085453.9af349ab459b.I348df3980764c15efce0099a35fe8a88fb2a6ee2@changeid --- drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 28 ++++++++++++++++----- 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index f05df3a3300e..6e507d6dcdd2 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2012-2014, 2018-2025 Intel Corporation + * Copyright (C) 2012-2014, 2018-2026 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -459,9 +459,14 @@ static void iwl_mvm_phy_filter_init(struct iwl_mvm *mvm, static void iwl_mvm_uats_init(struct iwl_mvm *mvm) { + struct iwl_mcc_allowed_ap_type_cmd_v1 *cmd __free(kfree) = NULL; int cmd_id = WIDE_ID(REGULATORY_AND_NVM_GROUP, MCC_ALLOWED_AP_TYPE_CMD); - struct iwl_mcc_allowed_ap_type_cmd_v1 cmd = {}; + struct iwl_host_cmd hcmd = { + .id = cmd_id, + .len[0] = sizeof(*cmd), + .dataflags[0] = IWL_HCMD_DFL_NOCOPY, + }; u8 cmd_ver; int ret; @@ -485,14 +490,25 @@ static void iwl_mvm_uats_init(struct iwl_mvm *mvm) if (!mvm->fwrt.ap_type_cmd_valid) return; - BUILD_BUG_ON(sizeof(mvm->fwrt.ap_type_cmd.mcc_to_ap_type_map) != - sizeof(cmd.mcc_to_ap_type_map)); + /* Since we free the command immediately after iwl_mvm_send_cmd, we + * must send this command in SYNC mode. + */ + lockdep_assert_held(&mvm->mutex); - memcpy(cmd.mcc_to_ap_type_map, + cmd = kzalloc_obj(*cmd); + if (!cmd) + return; + + BUILD_BUG_ON(sizeof(mvm->fwrt.ap_type_cmd.mcc_to_ap_type_map) != + sizeof(cmd->mcc_to_ap_type_map)); + + memcpy(cmd->mcc_to_ap_type_map, mvm->fwrt.ap_type_cmd.mcc_to_ap_type_map, sizeof(mvm->fwrt.ap_type_cmd.mcc_to_ap_type_map)); - ret = iwl_mvm_send_cmd_pdu(mvm, cmd_id, 0, sizeof(cmd), &cmd); + hcmd.data[0] = cmd; + + ret = iwl_mvm_send_cmd(mvm, &hcmd); if (ret < 0) IWL_ERR(mvm, "failed to send MCC_ALLOWED_AP_TYPE_CMD (%d)\n", ret); From 093305d801fae6ff9b8bb531fd78b579794c4f80 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Sun, 31 May 2026 13:30:19 +0300 Subject: [PATCH 244/337] wifi: iwlwifi: pcie: simplify the resume flow if fast resume is not used In most distributions, NetworkManager shuts the device down before entering system suspend, so fast suspend is typically not used. On older devices, resume currently tries to grab NIC access to infer whether the device was powered off while suspended. That probe is only meaningful for the fast-suspend path where the device is expected to remain alive. Unfortunately, for unclear reasons, grabbing NIC access was harmful as reported in the bugzilla ticket below. Workaround this issue by simply not grabbing NIC access if fast suspend is not used. Cc: stable@vger.kernel.org Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221501 Assisted-by: GitHub Copilot:gpt-5.3-codex Signed-off-by: Emmanuel Grumbach Link: https://patch.msgid.link/20260531133005.e2ed9e0cd44f.If283625983a843933e0c01561a421daff184e9e9@changeid Signed-off-by: Miri Korenblit --- drivers/net/wireless/intel/iwlwifi/pcie/drv.c | 56 +++++++++++-------- 1 file changed, 32 insertions(+), 24 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c index dc99e7ac4726..eb3c5a6dd088 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c @@ -1225,33 +1225,41 @@ static int _iwl_pci_resume(struct device *device, bool restore) if (!trans->op_mode) return 0; - /* - * Scratch value was altered, this means the device was powered off, we - * need to reset it completely. - * Note: MAC (bits 0:7) will be cleared upon suspend even with wowlan, - * but not bits [15:8]. So if we have bits set in lower word, assume - * the device is alive. - * Alternatively, if the scratch value is 0xFFFFFFFF, then we no longer - * have access to the device and consider it powered off. - * For older devices, just try silently to grab the NIC. - */ - if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) { - u32 scratch = iwl_read32(trans, CSR_FUNC_SCRATCH); - - if (!(scratch & CSR_FUNC_SCRATCH_POWER_OFF_MASK) || - scratch == ~0U) - device_was_powered_off = true; - } else { + if (test_bit(STATUS_DEVICE_ENABLED, &trans->status)) { /* - * bh are re-enabled by iwl_trans_pcie_release_nic_access, - * so re-enable them if _iwl_trans_pcie_grab_nic_access fails. + * Scratch value was altered, this means the device was powered + * off, we need to reset it completely. + * Note: MAC (bits 0:7) will be cleared upon suspend even with + * wowlan, but not bits [15:8]. So if we have bits set in lower + * word, assume the device is alive. + * Alternatively, if the scratch value is 0xFFFFFFFF, then we + * no longer have access to the device and consider it powered + * off. + * For older devices, just try silently to grab the NIC. */ - local_bh_disable(); - if (_iwl_trans_pcie_grab_nic_access(trans, true)) { - iwl_trans_pcie_release_nic_access(trans); + if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) { + u32 scratch = iwl_read32(trans, CSR_FUNC_SCRATCH); + + if (!(scratch & CSR_FUNC_SCRATCH_POWER_OFF_MASK) || + scratch == ~0U) { + IWL_DEBUG_WOWLAN(trans, + "Scratch 0x%08x indicates device was powered off\n", + scratch); + device_was_powered_off = true; + } } else { - device_was_powered_off = true; - local_bh_enable(); + /* + * bh are re-enabled by iwl_trans_pcie_release_nic_access, + * so re-enable them if _iwl_trans_pcie_grab_nic_access + * fails. + */ + local_bh_disable(); + if (_iwl_trans_pcie_grab_nic_access(trans, true)) { + iwl_trans_pcie_release_nic_access(trans); + } else { + device_was_powered_off = true; + local_bh_enable(); + } } } From e43ffb69e0438cddd72aaa30898b4dc446f664f8 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sun, 31 May 2026 15:14:24 -0700 Subject: [PATCH 245/337] Linux 7.1-rc6 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index f056c921ea9c..d8da45199699 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ VERSION = 7 PATCHLEVEL = 1 SUBLEVEL = 0 -EXTRAVERSION = -rc5 +EXTRAVERSION = -rc6 NAME = Baby Opossum Posse # *DOCUMENTATION* From d6b8b02a27b3dd09ec12144322b3dac46d9bc9ef Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Mon, 25 May 2026 09:22:55 -0700 Subject: [PATCH 246/337] mm/damon/ops-common: call folio_test_lru() after folio_get() damon_get_folio() speculatively calls folio_test_lru() before folio_try_get(). The folio can get freed and reallocated to a tail page. In the case, VM_BUG_ON_PGFLAGS() in const_folio_flags() can be triggered. Remove the speculative call. Also mark folio_test_lru() check right after folio_try_get() success as no more unlikely. The race should be rare. Also the problem can happen only if the kernel has enabled CONFIG_DEBUG_VM_PGFLAGS. No real world report of this issue has been made so far. This fix is based on only theoretical analysis. That said, a bug is a bug. A similar issue was also fixed via commit 3203b3ab0fcf ("mm/filemap: don't call folio_test_locked() without a reference in next_uptodate_folio()"). I don't expect this change will make a meaningful impact to DAMON performance in the real world, though I will be happy to be corrected from the real world reports. The issue was discovered [1] by Sashiko. Link: https://lore.kernel.org/20260525162256.8317-1-sj@kernel.org Link: https://lore.kernel.org/20260517234112.89245-1-sj@kernel.org [1] Fixes: 3f49584b262c ("mm/damon: implement primitives for the virtual memory address spaces") Signed-off-by: SeongJae Park Cc: Fernand Sieber Cc: Leonard Foerster Cc: Shakeel Butt Cc: # 5.15.x Signed-off-by: Andrew Morton --- mm/damon/ops-common.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mm/damon/ops-common.c b/mm/damon/ops-common.c index 8c6d613425c1..c3e4c871b0bb 100644 --- a/mm/damon/ops-common.c +++ b/mm/damon/ops-common.c @@ -32,9 +32,9 @@ struct folio *damon_get_folio(unsigned long pfn) return NULL; folio = page_folio(page); - if (!folio_test_lru(folio) || !folio_try_get(folio)) + if (!folio_try_get(folio)) return NULL; - if (unlikely(page_folio(page) != folio || !folio_test_lru(folio))) { + if (unlikely(page_folio(page) != folio) || !folio_test_lru(folio)) { folio_put(folio); folio = NULL; } From c7bde43f6daf70e05a64fbca7efdf6fa93e057dc Mon Sep 17 00:00:00 2001 From: Muchun Song Date: Mon, 25 May 2026 10:52:13 +0800 Subject: [PATCH 247/337] mm/hugetlb_vmemmap: fix incorrect vmemmap restore in rollback vmemmap_restore_pte() rebuilds restored vmemmap pages from a tail-page template derived from compound_head(). This is wrong when the current PTE already maps a page whose contents are not tail-page metadata. In the rollback path of vmemmap_remap_free(), the first restored PTE is backed by vmemmap_head and contains head-page metadata. Reconstructing that page from a tail-page template overwrites the head-page state and corrupts the restored vmemmap page. Fix this by copying the full page from the page currently mapped by the PTE. Also pass vmemmap_tail to the rollback walk so only PTEs backed by the shared tail page are restored, while the head PTE remains mapped to vmemmap_head. Add VM_WARN_ON_ONCE() checks for unexpected cases. Link: https://lore.kernel.org/20260525025213.2229628-1-songmuchun@bytedance.com Fixes: c0b495b91a47 ("mm/hugetlb: refactor code around vmemmap_walk") Signed-off-by: Muchun Song Acked-by: Kiryl Shutsemau Acked-by: Oscar Salvador (SUSE) Cc: David Hildenbrand Cc: Signed-off-by: Andrew Morton --- mm/hugetlb_vmemmap.c | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/mm/hugetlb_vmemmap.c b/mm/hugetlb_vmemmap.c index 4a077d231d3a..133b46dfb09f 100644 --- a/mm/hugetlb_vmemmap.c +++ b/mm/hugetlb_vmemmap.c @@ -207,6 +207,8 @@ static void vmemmap_remap_pte(pte_t *pte, unsigned long addr, /* Remapping the head page requires r/w */ if (unlikely(walk->nr_walked == 0 && walk->vmemmap_head)) { + VM_WARN_ON_ONCE(!PageHead((const struct page *)addr)); + list_del(&walk->vmemmap_head->lru); /* @@ -218,6 +220,8 @@ static void vmemmap_remap_pte(pte_t *pte, unsigned long addr, entry = mk_pte(walk->vmemmap_head, PAGE_KERNEL); } else { + VM_WARN_ON_ONCE(!PageTail((const struct page *)addr)); + /* * Remap the tail pages as read-only to catch illegal write * operation to the tail pages. @@ -232,33 +236,28 @@ static void vmemmap_remap_pte(pte_t *pte, unsigned long addr, static void vmemmap_restore_pte(pte_t *pte, unsigned long addr, struct vmemmap_remap_walk *walk) { - struct page *page; - struct page *from, *to; - - page = list_first_entry(walk->vmemmap_pages, struct page, lru); - list_del(&page->lru); + struct page *src = pte_page(ptep_get(pte)), *dst; /* - * Initialize tail pages in the newly allocated vmemmap page. - * - * There is folio-scope metadata that is encoded in the first few - * tail pages. - * - * Use the value last tail page in the page with the head page - * to initialize the rest of tail pages. + * When rolling back vmemmap_remap_free(), keep the copied head page + * mapping and restore only PTEs currently pointing at the shared tail + * page. */ - from = compound_head((struct page *)addr) + - PAGE_SIZE / sizeof(struct page) - 1; - to = page_to_virt(page); - for (int i = 0; i < PAGE_SIZE / sizeof(struct page); i++, to++) - *to = *from; + if (walk->vmemmap_tail && walk->vmemmap_tail != src) + return; + + VM_WARN_ON_ONCE(PageHead((const struct page *)addr)); + + dst = list_first_entry(walk->vmemmap_pages, struct page, lru); + list_del(&dst->lru); + copy_page(page_to_virt(dst), page_to_virt(src)); /* * Makes sure that preceding stores to the page contents become visible * before the set_pte_at() write. */ smp_wmb(); - set_pte_at(&init_mm, addr, pte, mk_pte(page, PAGE_KERNEL)); + set_pte_at(&init_mm, addr, pte, mk_pte(dst, PAGE_KERNEL)); } /** @@ -324,6 +323,7 @@ static int vmemmap_remap_free(unsigned long start, unsigned long end, */ walk = (struct vmemmap_remap_walk) { .remap_pte = vmemmap_restore_pte, + .vmemmap_tail = vmemmap_tail, .vmemmap_pages = vmemmap_pages, .flags = 0, }; From 40990c87a26e371594475acdc560c93cfae308a1 Mon Sep 17 00:00:00 2001 From: Yin Tirui Date: Tue, 26 May 2026 18:13:55 +0800 Subject: [PATCH 248/337] mm/huge_memory: update file PUD counter before folio_put() __split_huge_pud_locked() updates the file/shmem RSS counter after dropping the PUD mapping's folio reference. If folio_put() drops the last reference, mm_counter_file() can later read freed folio state via folio_test_swapbacked(). Move the counter update before folio_put(). Link: https://lore.kernel.org/20260526101355.1984244-1-yintirui@huawei.com Fixes: dbe54153296d ("mm/huge_memory: add vmf_insert_folio_pud()") Signed-off-by: Yin Tirui Reviewed-by: Lorenzo Stoakes Acked-by: David Hildenbrand (arm) Reviewed-by: Lance Yang Reviewed-by: Dev Jain Cc: Alistair Popple Cc: Baolin Wang Cc: Barry Song Cc: Chen Jun Cc: Kefeng Wang Cc: Liam R. Howlett Cc: Nico Pache Cc: Ryan Roberts Cc: Zi Yan Cc: Signed-off-by: Andrew Morton --- mm/huge_memory.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mm/huge_memory.c b/mm/huge_memory.c index 970e077019b7..1f78b73a0ca4 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -3015,9 +3015,9 @@ static void __split_huge_pud_locked(struct vm_area_struct *vma, pud_t *pud, if (!folio_test_referenced(folio) && pud_young(old_pud)) folio_set_referenced(folio); folio_remove_rmap_pud(folio, page, vma); - folio_put(folio); add_mm_counter(vma->vm_mm, mm_counter_file(folio), -HPAGE_PUD_NR); + folio_put(folio); } void __split_huge_pud(struct vm_area_struct *vma, pud_t *pud, From 8d878059924f12c1bc24556a92ec56add74de3c8 Mon Sep 17 00:00:00 2001 From: Yin Tirui Date: Tue, 26 May 2026 18:13:37 +0800 Subject: [PATCH 249/337] mm/huge_memory: update file PMD counter before folio_put() __split_huge_pmd_locked() updates the file/shmem RSS counter after dropping the PMD mapping's folio reference. If folio_put() drops the last reference, mm_counter_file() can later read freed folio state via folio_test_swapbacked(). Move the counter update before folio_put(). Link: https://lore.kernel.org/20260526101337.1984081-1-yintirui@huawei.com Fixes: fadae2953072 ("thp: use mm_file_counter to determine update which rss counter") Signed-off-by: Yin Tirui Reviewed-by: Lorenzo Stoakes Acked-by: David Hildenbrand (arm) Reviewed-by: Lance Yang Reviewed-by: Dev Jain Cc: Baolin Wang Cc: Barry Song Cc: Chen Jun Cc: Kefeng Wang Cc: Liam R. Howlett Cc: Nico Pache Cc: Ryan Roberts Cc: Vlastimil Babka Cc: Yang Shi Cc: Zi Yan Cc: Signed-off-by: Andrew Morton --- mm/huge_memory.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mm/huge_memory.c b/mm/huge_memory.c index 1f78b73a0ca4..653f2dc03403 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -3133,7 +3133,9 @@ static void __split_huge_pmd_locked(struct vm_area_struct *vma, pmd_t *pmd, if (!folio_test_referenced(folio) && pmd_young(old_pmd)) folio_set_referenced(folio); folio_remove_rmap_pmd(folio, page, vma); + add_mm_counter(mm, mm_counter_file(folio), -HPAGE_PMD_NR); folio_put(folio); + return; } add_mm_counter(mm, mm_counter_file(folio), -HPAGE_PMD_NR); return; From 85668fda932a5b8f15f649cf06411525a0e4c8ec Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Wed, 27 May 2026 21:47:49 +0300 Subject: [PATCH 250/337] userfaultfd: verify VMA state across UFFDIO_COPY retry Patch series "userfaultfd: verify VMA state across UFFDIO_COPY retry", v2. ... and two more small fixes. This patch (of 3): mfill_copy_folio_retry() drops the VMA lock for copy_from_user() and reacquires it afterwards. The destination VMA can be replaced during that window. The existing check compares vma_uffd_ops() before and after the retry, but if a shmem VMA with MAP_SHARED is replaced with a shmem VMA with MAP_PRIVATE (or vice versa) the replacement goes undetected. The change from MAP_PRIVATE to MAP_SHARED will treat the folio allocated with shmem_alloc_folio() as anonymous and this will cause BUG() when mfill_atomic_install_pte() will try to folio_add_new_anon_rmap(). The change from MAP_SHARED to MAP_PRIVATE allows injection of folios into the page cache of the original VMA. There is no need to change for hugetlb because it never uses mfill_copy_folio_retry(). Introduce helpers for more comprehensive comparison of VMA state: - mfill_retry_state_save() to save the relevant VMA state into a struct mfill_retry_state (original uffd_ops, relevant VMA flags, vm_file and pgoff) before dropping the lock - mfill_retry_state_changed() to compare the saved state with the state of the VMA acquired after retaking the locks - mfill_retry_state_put() to release vm_file pinning. Use DEFINE_FREE() cleanup to wrap mfill_retry_state_put() to avoid complicating error handling paths in mfill_copy_folio_retry(). Link: https://lore.kernel.org/20260527184751.4147364-1-rppt@kernel.org Link: https://lore.kernel.org/20260527184751.4147364-2-rppt@kernel.org Fixes: 292411fda25b ("mm/userfaultfd: detect VMA type change after copy retry in mfill_copy_folio_retry()") Fixes: 6ab703034f14 ("userfaultfd: mfill_atomic(): remove retry logic") Co-developed-by: Michael Bommarito Signed-off-by: Michael Bommarito Signed-off-by: Mike Rapoport (Microsoft) Suggested-by: Peter Xu Co-developed-by: David Carlier Signed-off-by: David Carlier Reviewed-by: Lorenzo Stoakes Cc: David Hildenbrand Cc: Liam R. Howlett Signed-off-by: Andrew Morton --- mm/userfaultfd.c | 85 +++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 73 insertions(+), 12 deletions(-) diff --git a/mm/userfaultfd.c b/mm/userfaultfd.c index 180bad42fc79..e5d2fb3ce2c1 100644 --- a/mm/userfaultfd.c +++ b/mm/userfaultfd.c @@ -14,6 +14,8 @@ #include #include #include +#include +#include #include #include #include "internal.h" @@ -443,16 +445,80 @@ static int mfill_copy_folio_locked(struct folio *folio, unsigned long src_addr) return ret; } -static int mfill_copy_folio_retry(struct mfill_state *state, +#define MFILL_RETRY_STATE_VMA_FLAGS \ + append_vma_flags(__VMA_UFFD_FLAGS, VMA_SHARED_BIT) + +/* + * VMA state saved before dropping the locks in mfill_copy_folio_retry(). + * Used to detect VMA replacement or incompatible changes after reacquiring the + * locks. + */ +struct mfill_retry_state { + const struct vm_uffd_ops *ops; + struct file *file; + vma_flags_t flags; + pgoff_t pgoff; +}; + +static void mfill_retry_state_save(struct mfill_retry_state *s, + struct vm_area_struct *vma) +{ + s->flags = vma_flags_and_mask(&vma->flags, MFILL_RETRY_STATE_VMA_FLAGS); + s->ops = vma_uffd_ops(vma); + s->pgoff = vma->vm_pgoff; + + if (vma->vm_file) + s->file = get_file(vma->vm_file); +} + +static bool mfill_retry_state_changed(struct mfill_retry_state *state, + struct vm_area_struct *vma) +{ + vma_flags_t flags = vma_flags_and_mask(&vma->flags, + MFILL_RETRY_STATE_VMA_FLAGS); + + /* Have any UFFD flags (missing, WP, minor) changed? */ + if (!vma_flags_same_pair(&state->flags, &flags)) + return true; + + /* VMA type or effective uffd_ops changed while the lock was dropped */ + if (state->ops != vma_uffd_ops(vma)) + return true; + + /* VMA was anonymous before; changed only if it no longer is */ + if (!state->file) + return !vma_is_anonymous(vma); + + /* VMA was file backed, but file, inode or offset has changed */ + if (!vma->vm_file || vma->vm_file->f_inode != state->file->f_inode || + state->file != vma->vm_file || vma->vm_pgoff != state->pgoff) + return true; + + return false; +} + +static void mfill_retry_state_put(struct mfill_retry_state *s) +{ + if (s->file) + fput(s->file); +} + +DEFINE_FREE(retry_put, struct mfill_retry_state *, + if (_T) mfill_retry_state_put(_T)); + +static int mfill_copy_folio_retry(struct mfill_state *mfill_state, struct folio *folio) { - const struct vm_uffd_ops *orig_ops = vma_uffd_ops(state->vma); - unsigned long src_addr = state->src_addr; + struct mfill_retry_state retry_state = { 0 }; + struct mfill_retry_state *for_free __free(retry_put) = &retry_state; + unsigned long src_addr = mfill_state->src_addr; void *kaddr; int err; + mfill_retry_state_save(&retry_state, mfill_state->vma); + /* retry copying with mm_lock dropped */ - mfill_put_vma(state); + mfill_put_vma(mfill_state); kaddr = kmap_local_folio(folio, 0); err = copy_from_user(kaddr, (const void __user *) src_addr, PAGE_SIZE); @@ -463,19 +529,14 @@ static int mfill_copy_folio_retry(struct mfill_state *state, flush_dcache_folio(folio); /* reget VMA and PMD, they could change underneath us */ - err = mfill_get_vma(state); + err = mfill_get_vma(mfill_state); if (err) return err; - /* - * The VMA type may have changed while the lock was dropped - * (e.g. replaced with a hugetlb mapping), making the caller's - * ops pointer stale. - */ - if (vma_uffd_ops(state->vma) != orig_ops) + if (mfill_retry_state_changed(&retry_state, mfill_state->vma)) return -EAGAIN; - err = mfill_establish_pmd(state); + err = mfill_establish_pmd(mfill_state); if (err) return err; From df3ee3b3bbc327f570c5451666bbaf6cf8b4436a Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Wed, 27 May 2026 21:47:50 +0300 Subject: [PATCH 251/337] userfaultfd: refuse to __mfill_atomic_pte() for unsupported VMAs __mfill_atomic_pte() unconditionally dereferences ops because there is an assumption that VMAs that can undergo mfill_* operations are vetted on registration and must have valid vm_uffd_ops. Add a guard against potential bugs and make sure __mfill_atomic_pte() bails out if ops is NULL. Link: https://lore.kernel.org/20260527184751.4147364-3-rppt@kernel.org Fixes: ad9ac3081332 ("userfaultfd: introduce vm_uffd_ops->alloc_folio()") Signed-off-by: Mike Rapoport (Microsoft) Suggested-by: Lorenzo Stoakes Reviewed-by: Lorenzo Stoakes Reviewed-by: David CARLIER Cc: David Hildenbrand Cc: Liam R. Howlett Cc: Michael Bommarito Cc: Peter Xu Signed-off-by: Andrew Morton --- mm/userfaultfd.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/mm/userfaultfd.c b/mm/userfaultfd.c index e5d2fb3ce2c1..2872c71bbf36 100644 --- a/mm/userfaultfd.c +++ b/mm/userfaultfd.c @@ -552,6 +552,11 @@ static int __mfill_atomic_pte(struct mfill_state *state, struct folio *folio; int ret; + if (!ops) { + VM_WARN_ONCE(1, "UFFDIO_COPY for unsupported VMA"); + return -EOPNOTSUPP; + } + folio = ops->alloc_folio(state->vma, state->dst_addr); if (!folio) return -ENOMEM; From 9d7bea186ba5a002456471edf36cc9b69f809397 Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Wed, 27 May 2026 21:47:51 +0300 Subject: [PATCH 252/337] userfaultfd: remove redundant check in vm_uffd_ops() Lorenzo says: static const struct vm_uffd_ops *vma_uffd_ops(struct vm_area_struct *vma) { if (vma_is_anonymous(vma)) return &anon_uffd_ops; return vma->vm_ops ? vma->vm_ops->uffd_ops : NULL; } This is doing a redundant check _and_ making life confusing, as if !vma->vm_ops is a condition that can be reached there, it can't, as vma_is_anonymous() is literally a !vma->vm_ops check :) Remove the redundant check. Link: https://lore.kernel.org/20260527184751.4147364-4-rppt@kernel.org Fixes: 0f48947c4232 ("userfaultfd: introduce vm_uffd_ops") Signed-off-by: Mike Rapoport (Microsoft) Suggested-by: Lorenzo Stoakes Reviewed-by: Lorenzo Stoakes Cc: David Hildenbrand Cc: Liam R. Howlett Cc: Peter Xu Cc: David Carlier Cc: Michael Bommarito Signed-off-by: Andrew Morton --- mm/userfaultfd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mm/userfaultfd.c b/mm/userfaultfd.c index 2872c71bbf36..80cc8be5725f 100644 --- a/mm/userfaultfd.c +++ b/mm/userfaultfd.c @@ -68,7 +68,7 @@ static const struct vm_uffd_ops *vma_uffd_ops(struct vm_area_struct *vma) { if (vma_is_anonymous(vma)) return &anon_uffd_ops; - return vma->vm_ops ? vma->vm_ops->uffd_ops : NULL; + return vma->vm_ops->uffd_ops; } static __always_inline From c6c5327dd18bec1e1bbf139b2cf5ae53608a9d30 Mon Sep 17 00:00:00 2001 From: Fernando Fernandez Mancera Date: Fri, 22 May 2026 12:47:17 +0200 Subject: [PATCH 253/337] netfilter: xt_NFQUEUE: prefer raw_smp_processor_id With PREEMPT_RCU this triggers a splat because smp_processor_id() can be preempted while inside a RCU critical section. If xt_NFQUEUE target is invoked via nft_compat_eval() path, we are inside a RCU critical section. Just use the raw version instead. Fixes: 0ca743a55991 ("netfilter: nf_tables: add compatibility layer for x_tables") Signed-off-by: Fernando Fernandez Mancera Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/netfilter/xt_NFQUEUE.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/netfilter/xt_NFQUEUE.c b/net/netfilter/xt_NFQUEUE.c index 466da23e36ff..b32d153e3a18 100644 --- a/net/netfilter/xt_NFQUEUE.c +++ b/net/netfilter/xt_NFQUEUE.c @@ -91,7 +91,7 @@ nfqueue_tg_v3(struct sk_buff *skb, const struct xt_action_param *par) if (info->queues_total > 1) { if (info->flags & NFQ_FLAG_CPU_FANOUT) { - int cpu = smp_processor_id(); + int cpu = raw_smp_processor_id(); queue = info->queuenum + cpu % info->queues_total; } else { From 193989cc6d80dd8e0460fb3992e69fa03bf0ff9b Mon Sep 17 00:00:00 2001 From: Julian Anastasov Date: Mon, 25 May 2026 07:07:44 +0300 Subject: [PATCH 254/337] ipvs: clear the svc scheduler ptr early on edit ip_vs_edit_service() while unbinding the old scheduler clears the svc->scheduler ptr after the scheduler module initiates RCU callbacks. This can cause packets to use the old scheduler at the time when svc->sched_data is already freed after RCU grace period. Fix it by clearing the ptr early in ip_vs_unbind_scheduler(), before the done_service method schedules any RCU callbacks. Also, if the new scheduler fails to initialize when replacing the old scheduler, try to restore the old scheduler while still returning the error code. Link: https://sashiko.dev/#/patchset/20260519015506.634185-1-rosenp%40gmail.com Fixes: 05f00505a89a ("ipvs: fix crash if scheduler is changed") Signed-off-by: Julian Anastasov Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- include/net/ip_vs.h | 3 +-- net/netfilter/ipvs/ip_vs_ctl.c | 13 ++++++++----- net/netfilter/ipvs/ip_vs_sched.c | 14 +++++++------- 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index a02e569813d2..e517eaaa177b 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -1824,8 +1824,7 @@ int register_ip_vs_scheduler(struct ip_vs_scheduler *scheduler); int unregister_ip_vs_scheduler(struct ip_vs_scheduler *scheduler); int ip_vs_bind_scheduler(struct ip_vs_service *svc, struct ip_vs_scheduler *scheduler); -void ip_vs_unbind_scheduler(struct ip_vs_service *svc, - struct ip_vs_scheduler *sched); +void ip_vs_unbind_scheduler(struct ip_vs_service *svc); struct ip_vs_scheduler *ip_vs_scheduler_get(const char *sched_name); void ip_vs_scheduler_put(struct ip_vs_scheduler *scheduler); struct ip_vs_conn * diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c index bd9cae44d214..16daba8cac83 100644 --- a/net/netfilter/ipvs/ip_vs_ctl.c +++ b/net/netfilter/ipvs/ip_vs_ctl.c @@ -1898,7 +1898,7 @@ ip_vs_add_service(struct netns_ipvs *ipvs, struct ip_vs_service_user_kern *u, if (ret_hooks >= 0) ip_vs_unregister_hooks(ipvs, u->af); if (svc != NULL) { - ip_vs_unbind_scheduler(svc, sched); + ip_vs_unbind_scheduler(svc); ip_vs_service_free(svc); } ip_vs_scheduler_put(sched); @@ -1962,9 +1962,8 @@ ip_vs_edit_service(struct ip_vs_service *svc, struct ip_vs_service_user_kern *u) old_sched = rcu_dereference_protected(svc->scheduler, 1); if (sched != old_sched) { if (old_sched) { - ip_vs_unbind_scheduler(svc, old_sched); - RCU_INIT_POINTER(svc->scheduler, NULL); - /* Wait all svc->sched_data users */ + ip_vs_unbind_scheduler(svc); + /* Wait all svc->scheduler/sched_data users */ synchronize_rcu(); } /* Bind the new scheduler */ @@ -1972,6 +1971,10 @@ ip_vs_edit_service(struct ip_vs_service *svc, struct ip_vs_service_user_kern *u) ret = ip_vs_bind_scheduler(svc, sched); if (ret) { ip_vs_scheduler_put(sched); + /* Try to restore the old_sched */ + if (old_sched && + !ip_vs_bind_scheduler(svc, old_sched)) + old_sched = NULL; goto out; } } @@ -2027,7 +2030,7 @@ static void __ip_vs_del_service(struct ip_vs_service *svc, bool cleanup) /* Unbind scheduler */ old_sched = rcu_dereference_protected(svc->scheduler, 1); - ip_vs_unbind_scheduler(svc, old_sched); + ip_vs_unbind_scheduler(svc); ip_vs_scheduler_put(old_sched); /* Unbind persistence engine, keep svc->pe */ diff --git a/net/netfilter/ipvs/ip_vs_sched.c b/net/netfilter/ipvs/ip_vs_sched.c index c6e421c4e299..24adc38942a0 100644 --- a/net/netfilter/ipvs/ip_vs_sched.c +++ b/net/netfilter/ipvs/ip_vs_sched.c @@ -56,19 +56,19 @@ int ip_vs_bind_scheduler(struct ip_vs_service *svc, /* * Unbind a service with its scheduler */ -void ip_vs_unbind_scheduler(struct ip_vs_service *svc, - struct ip_vs_scheduler *sched) +void ip_vs_unbind_scheduler(struct ip_vs_service *svc) { - struct ip_vs_scheduler *cur_sched; + struct ip_vs_scheduler *sched; - cur_sched = rcu_dereference_protected(svc->scheduler, 1); - /* This check proves that old 'sched' was installed */ - if (!cur_sched) + sched = rcu_dereference_protected(svc->scheduler, 1); + if (!sched) return; + /* Reset the scheduler before initiating any RCU callbacks */ + rcu_assign_pointer(svc->scheduler, NULL); + smp_wmb(); /* paired with smp_rmb() in ip_vs_schedule() */ if (sched->done_service) sched->done_service(svc); - /* svc->scheduler can be set to NULL only by caller */ } From 36d29ceec32c8206a12dc2810cf65fd394e45baa Mon Sep 17 00:00:00 2001 From: Jiayuan Chen Date: Tue, 26 May 2026 10:02:27 +0800 Subject: [PATCH 255/337] netfilter: nft_fib_ipv6: bail out of sibling walk if rt got unlinked This was reported by Sashiko [1]. The RCU walk over rt->fib6_siblings can spin forever if rt is unlinked mid-iteration: rt->fib6_siblings.next still points into the old ring, so the loop never meets &rt->fib6_siblings as its terminator. fib6_purge_rt() always does WRITE_ONCE(rt->fib6_nsiblings, 0) before list_del_rcu(), so readers can use rt->fib6_nsiblings == 0 as the detach signal. The same pattern is used in fib6_info_uses_dev() and rt6_nlmsg_size(). [1]: https://sashiko.dev/#/patchset/20260520023411.391233-1-jiayuan.chen%40linux.dev Suggested-by: Florian Westphal Fixes: 1c32b24c234b ("netfilter: nft_fib_ipv6: switch to fib6_lookup") Signed-off-by: Jiayuan Chen Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/ipv6/netfilter/nft_fib_ipv6.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/ipv6/netfilter/nft_fib_ipv6.c b/net/ipv6/netfilter/nft_fib_ipv6.c index c0a0075e2590..2dbe44715df3 100644 --- a/net/ipv6/netfilter/nft_fib_ipv6.c +++ b/net/ipv6/netfilter/nft_fib_ipv6.c @@ -191,6 +191,9 @@ static bool nft_fib6_info_nh_uses_dev(struct fib6_info *rt, if (nft_fib6_info_nh_dev_match(nh_dev, dev)) return true; + + if (!READ_ONCE(rt->fib6_nsiblings)) + return false; } return false; From 2fcba19caaeb2a33017459d3430f057967bb91b6 Mon Sep 17 00:00:00 2001 From: Fernando Fernandez Mancera Date: Tue, 26 May 2026 23:58:31 +0200 Subject: [PATCH 256/337] netfilter: synproxy: add mutex to guard hook reference counting As the synproxy infrastructure register netfilter hooks on-demand when a user adds the first iptables target or nftables expression, if done concurrently they can race each other. Introduce a mutex to serialize the refcount control blocks access from both frontends. While a per namespace mutex might be more efficient, it is not needed for target/expression like SYNPROXY. Fixes: ad49d86e07a4 ("netfilter: nf_tables: Add synproxy support") Signed-off-by: Fernando Fernandez Mancera Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_synproxy_core.c | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/net/netfilter/nf_synproxy_core.c b/net/netfilter/nf_synproxy_core.c index 036c8586f49b..ed00114f65f3 100644 --- a/net/netfilter/nf_synproxy_core.c +++ b/net/netfilter/nf_synproxy_core.c @@ -22,6 +22,8 @@ #include #include +static DEFINE_MUTEX(synproxy_mutex); + unsigned int synproxy_net_id; EXPORT_SYMBOL_GPL(synproxy_net_id); @@ -769,26 +771,31 @@ static const struct nf_hook_ops ipv4_synproxy_ops[] = { int nf_synproxy_ipv4_init(struct synproxy_net *snet, struct net *net) { - int err; + int err = 0; + mutex_lock(&synproxy_mutex); if (snet->hook_ref4 == 0) { err = nf_register_net_hooks(net, ipv4_synproxy_ops, ARRAY_SIZE(ipv4_synproxy_ops)); if (err) - return err; + goto out; } snet->hook_ref4++; - return 0; +out: + mutex_unlock(&synproxy_mutex); + return err; } EXPORT_SYMBOL_GPL(nf_synproxy_ipv4_init); void nf_synproxy_ipv4_fini(struct synproxy_net *snet, struct net *net) { + mutex_lock(&synproxy_mutex); snet->hook_ref4--; if (snet->hook_ref4 == 0) nf_unregister_net_hooks(net, ipv4_synproxy_ops, ARRAY_SIZE(ipv4_synproxy_ops)); + mutex_unlock(&synproxy_mutex); } EXPORT_SYMBOL_GPL(nf_synproxy_ipv4_fini); @@ -1193,27 +1200,32 @@ static const struct nf_hook_ops ipv6_synproxy_ops[] = { int nf_synproxy_ipv6_init(struct synproxy_net *snet, struct net *net) { - int err; + int err = 0; + mutex_lock(&synproxy_mutex); if (snet->hook_ref6 == 0) { err = nf_register_net_hooks(net, ipv6_synproxy_ops, ARRAY_SIZE(ipv6_synproxy_ops)); if (err) - return err; + goto out; } snet->hook_ref6++; - return 0; +out: + mutex_unlock(&synproxy_mutex); + return err; } EXPORT_SYMBOL_GPL(nf_synproxy_ipv6_init); void nf_synproxy_ipv6_fini(struct synproxy_net *snet, struct net *net) { + mutex_lock(&synproxy_mutex); snet->hook_ref6--; if (snet->hook_ref6 == 0) nf_unregister_net_hooks(net, ipv6_synproxy_ops, ARRAY_SIZE(ipv6_synproxy_ops)); + mutex_unlock(&synproxy_mutex); } EXPORT_SYMBOL_GPL(nf_synproxy_ipv6_fini); #endif /* CONFIG_IPV6 */ From 66eba0ffce3b7e11449946b4cbbef8ea36112f56 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Wed, 27 May 2026 12:20:19 +0200 Subject: [PATCH 257/337] netfilter: conntrack_irc: fix possible out-of-bounds read When parsing fails after we've matched the command string we should bail out instead of trying to match a different command. This helper should be deprecated, given prevalence of TLS I doubt it has any relevance in 2026. Fixes: 869f37d8e48f ("[NETFILTER]: nf_conntrack/nf_nat: add IRC helper port") Closes: https://sashiko.dev/#/patchset/20260525182924.28456-1-fw%40strlen.de Signed-off-by: Florian Westphal Reviewed-by: Fernando Fernandez Mancera Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_conntrack_irc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/netfilter/nf_conntrack_irc.c b/net/netfilter/nf_conntrack_irc.c index 522183b9a604..2ebe4cb47cf6 100644 --- a/net/netfilter/nf_conntrack_irc.c +++ b/net/netfilter/nf_conntrack_irc.c @@ -203,7 +203,7 @@ static int help(struct sk_buff *skb, unsigned int protoff, if (parse_dcc(data, data_limit, &dcc_ip, &dcc_port, &addr_beg_p, &addr_end_p)) { pr_debug("unable to parse dcc command\n"); - continue; + goto out; } pr_debug("DCC bound ip/port: %pI4:%u\n", @@ -217,7 +217,7 @@ static int help(struct sk_buff *skb, unsigned int protoff, net_warn_ratelimited("Forged DCC command from %pI4: %pI4:%u\n", &tuple->src.u3.ip, &dcc_ip, dcc_port); - continue; + goto out; } exp = nf_ct_expect_alloc(ct); From c32b26aaa2f9216520a38b3f4bfeec846eb3eb8a Mon Sep 17 00:00:00 2001 From: Tristan Madani Date: Wed, 27 May 2026 13:57:50 +0000 Subject: [PATCH 258/337] netfilter: nft_tunnel: fix use-after-free on object destroy nft_tunnel_obj_destroy() calls metadata_dst_free() which directly kfree()s the metadata_dst, ignoring the dst_entry refcount. Packets that took a reference via dst_hold() in nft_tunnel_obj_eval() and are still queued (e.g. in a netem qdisc) are left with a dangling pointer. When these packets are eventually dequeued, dst_release() operates on freed memory. Replace metadata_dst_free() with dst_release() so the metadata_dst is freed only after all references are dropped. The dst subsystem already handles metadata_dst cleanup in dst_destroy() when DST_METADATA is set. Fixes: af308b94a2a4 ("netfilter: nf_tables: add tunnel support") Cc: stable@vger.kernel.org Signed-off-by: Tristan Madani Reviewed-by: Fernando Fernandez Mancera Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nft_tunnel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/netfilter/nft_tunnel.c b/net/netfilter/nft_tunnel.c index 0b987bc2132a..68f7cfbbee06 100644 --- a/net/netfilter/nft_tunnel.c +++ b/net/netfilter/nft_tunnel.c @@ -676,7 +676,7 @@ static void nft_tunnel_obj_destroy(const struct nft_ctx *ctx, { struct nft_tunnel_obj *priv = nft_obj_data(obj); - metadata_dst_free(priv->md); + dst_release(&priv->md->dst); } static struct nft_object_type nft_tunnel_obj_type; From 3027ecbdb5fdf9200251c21d4818e4c447ef78e1 Mon Sep 17 00:00:00 2001 From: Jiayuan Chen Date: Thu, 28 May 2026 19:09:19 +0800 Subject: [PATCH 259/337] netfilter: nft_ct: bail out on template ct in get eval I noticed this issue while looking at a historic syzbot report [1]. A rule like the one below is enough to trigger the bug: table ip t { chain pre { type filter hook prerouting priority raw; ct zone set 1 ct original saddr 1.2.3.4 accept } } The first expression attaches a per-cpu template ct via nft_ct_set_zone_eval() (nf_ct_tmpl_alloc -> kzalloc, tuple is all zero, nf_ct_l3num(ct) == 0). The next expression then calls nft_ct_get_eval() on the same skb, treats the template as a real ct and hits the 16-byte memcpy path. With dreg at NFT_REG32_15 this overflows past struct nft_regs on the kernel stack; with smaller dreg values it silently clobbers adjacent registers. Reject template ct at the eval entry and in nft_ct_get_fast_eval(), mirroring the check nft_ct_set_eval() already has. Additionally, bound the address copy in NFT_CT_SRC / NFT_CT_DST by priv->len instead of by nf_ct_l3num(ct): nf_ct_get_tuple() zeroes the tuple before pkt_to_tuple() fills in only the protocol-relevant leading bytes, so the trailing bytes of tuple->{src,dst}.u3.all are well-defined zero. priv->len is validated at rule load, so the copy size is now bounded by the destination register rather than by an untrusted field on the conntrack. [1]: https://syzkaller.appspot.com/bug?id=389cf09cb72926114fce90dc85a2c3231dcb647c Fixes: 45d9bcda21f4 ("netfilter: nf_tables: validate len in nft_validate_data_load()") Suggested-by: Florian Westphal Signed-off-by: Jiayuan Chen Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nft_ct.c | 8 +++----- net/netfilter/nft_ct_fast.c | 2 +- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/net/netfilter/nft_ct.c b/net/netfilter/nft_ct.c index fa2cc556331c..357513c6dcea 100644 --- a/net/netfilter/nft_ct.c +++ b/net/netfilter/nft_ct.c @@ -78,7 +78,7 @@ static void nft_ct_get_eval(const struct nft_expr *expr, break; } - if (ct == NULL) + if (!ct || nf_ct_is_template(ct)) goto err; switch (priv->key) { @@ -180,12 +180,10 @@ static void nft_ct_get_eval(const struct nft_expr *expr, tuple = &ct->tuplehash[priv->dir].tuple; switch (priv->key) { case NFT_CT_SRC: - memcpy(dest, tuple->src.u3.all, - nf_ct_l3num(ct) == NFPROTO_IPV4 ? 4 : 16); + memcpy(dest, tuple->src.u3.all, priv->len); return; case NFT_CT_DST: - memcpy(dest, tuple->dst.u3.all, - nf_ct_l3num(ct) == NFPROTO_IPV4 ? 4 : 16); + memcpy(dest, tuple->dst.u3.all, priv->len); return; case NFT_CT_PROTO_SRC: nft_reg_store16(dest, (__force u16)tuple->src.u.all); diff --git a/net/netfilter/nft_ct_fast.c b/net/netfilter/nft_ct_fast.c index e684c8a91848..ecf7b3a404be 100644 --- a/net/netfilter/nft_ct_fast.c +++ b/net/netfilter/nft_ct_fast.c @@ -30,7 +30,7 @@ void nft_ct_get_fast_eval(const struct nft_expr *expr, break; } - if (!ct) { + if (!ct || nf_ct_is_template(ct)) { regs->verdict.code = NFT_BREAK; return; } From 67ba971ae02514d85818fe0c32549ab4bfa3bf49 Mon Sep 17 00:00:00 2001 From: Yiming Qian Date: Sat, 23 May 2026 12:29:10 +0000 Subject: [PATCH 260/337] netfilter: bridge: make ebt_snat ARP rewrite writable The ebtables SNAT target keeps the Ethernet source address rewrite behind skb_ensure_writable(skb, 0). This is intentional: at the bridge ebtables hooks the Ethernet header is addressed through skb_mac_header()/eth_hdr(), while skb->data points at the Ethernet payload. Asking skb_ensure_writable() for ETH_HLEN bytes would check the payload, not the Ethernet header, and would reintroduce the small packet regression fixed by commit 63137bc5882a. However, the optional ARP sender hardware address rewrite is different. It writes through skb_store_bits() at an offset relative to skb->data: skb_store_bits(skb, sizeof(struct arphdr), info->mac, ETH_ALEN) skb_header_pointer() only safely reads the ARP header; it does not make the later sender hardware address range writable. If that range is still held in a nonlinear skb fragment backed by a splice-imported file page, skb_store_bits() maps the frag page and copies the new MAC address directly into it. Ensure the ARP SHA range is writable before reading the ARP header and before calling skb_store_bits(). Fixes: 63137bc5882a ("netfilter: ebtables: Fixes dropping of small packets in bridge nat") Reported-by: Yiming Qian Signed-off-by: Yiming Qian Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/bridge/netfilter/ebt_snat.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/bridge/netfilter/ebt_snat.c b/net/bridge/netfilter/ebt_snat.c index 7dfbcdfc30e5..c9e229af0366 100644 --- a/net/bridge/netfilter/ebt_snat.c +++ b/net/bridge/netfilter/ebt_snat.c @@ -31,6 +31,9 @@ ebt_snat_tg(struct sk_buff *skb, const struct xt_action_param *par) const struct arphdr *ap; struct arphdr _ah; + if (skb_ensure_writable(skb, sizeof(_ah) + ETH_ALEN)) + return EBT_DROP; + ap = skb_header_pointer(skb, 0, sizeof(_ah), &_ah); if (ap == NULL) return EBT_DROP; From bb061d3de41707415269be75ebf700efb03ec212 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Tue, 12 May 2026 15:36:14 +0200 Subject: [PATCH 261/337] netfilter: nft_byteorder: remove multi-register support 64bit byteorder conversion is broken when several registers need to be converted because the source register array advances in steps for 4 bytes instead of 8: for (i = ... src64 = nft_reg_load64(&src[i]); ~~~~~ u32 *src nft_reg_store64(&dst64[i], Remove the multi-register support, it has other issues as well: Pablo points out that commit caf3ef7468f7 ("netfilter: nf_tables: prevent OOB access in nft_byteorder_eval") alters semantics: before the loop operated on registers, i.e. for ( ... ) dst32[i] = htons((u16)src32[i]) .. but after the patch it will operate on bytes, which makes this useless to convert e.g. concatenations, which store each compound in its own register. Multi-convert of u32 has one theoretical application: ct mark . meta mark . tcp dport @intervalset Because ct mark and meta mark are host byte order, use with intervals has to convert the byteorder for ct/meta mark value to network byte order (bigendian). nftables emits this: [ meta load mark => reg 1 ] [ byteorder reg 1 = hton(reg 1, 4, 4) ] [ ct load mark => reg 9 ] [ byteorder reg 9 = hton(reg 9, 4, 4) ] ... I.e. two separate calls. Theoretically it could be changed to do: [ meta load mark => reg 1 ] [ ct load mark => reg 9 ] [ byteorder reg 1 = htonl(reg 1, 4, 8) ] ... But then all it would take to change the set to meta mark . tcp dport . ct mark ... and we'd be back to two "byteorder" calls. IOW, support to convert a range of registers is both dysfunctional and dubious. Simplify this: remove the feature. Pablo Neira Ayuso points out that nftables before 1.1.0 can generate incorrect byteorder conversions, see 9fe58952c45a, "evaluate: skip byteorder conversion for selector smaller than 2 bytes" in nftables.git). Affected rulesets fail to load with this change and old userspace due to 'len != size' check. Fixes: c301f0981fdd ("netfilter: nf_tables: fix pointer math issue in nft_byteorder_eval()") Cc: # may break rule load with old nftables versions Reported-by: Michal Kubecek Link: https://lore.kernel.org/netfilter-devel/20240206104336.ctigqpkunom2ufmn@lion.mk-sys.cz/ Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nft_byteorder.c | 51 ++++++++++++++--------------------- 1 file changed, 20 insertions(+), 31 deletions(-) diff --git a/net/netfilter/nft_byteorder.c b/net/netfilter/nft_byteorder.c index 2316c77f4228..dfd41fc8d9b8 100644 --- a/net/netfilter/nft_byteorder.c +++ b/net/netfilter/nft_byteorder.c @@ -19,7 +19,6 @@ struct nft_byteorder { u8 sreg; u8 dreg; enum nft_byteorder_ops op:8; - u8 len; u8 size; }; @@ -28,13 +27,8 @@ void nft_byteorder_eval(const struct nft_expr *expr, const struct nft_pktinfo *pkt) { const struct nft_byteorder *priv = nft_expr_priv(expr); - u32 *src = ®s->data[priv->sreg]; + const u32 *src = ®s->data[priv->sreg]; u32 *dst = ®s->data[priv->dreg]; - u16 *s16, *d16; - unsigned int i; - - s16 = (void *)src; - d16 = (void *)dst; switch (priv->size) { case 8: { @@ -43,18 +37,14 @@ void nft_byteorder_eval(const struct nft_expr *expr, switch (priv->op) { case NFT_BYTEORDER_NTOH: - for (i = 0; i < priv->len / 8; i++) { - src64 = nft_reg_load64(&src[i]); - nft_reg_store64(&dst64[i], - be64_to_cpu((__force __be64)src64)); - } + src64 = nft_reg_load64(src); + + nft_reg_store64(dst64, be64_to_cpu((__force __be64)src64)); break; case NFT_BYTEORDER_HTON: - for (i = 0; i < priv->len / 8; i++) { - src64 = (__force __u64) - cpu_to_be64(nft_reg_load64(&src[i])); - nft_reg_store64(&dst64[i], src64); - } + src64 = (__force __u64)cpu_to_be64(nft_reg_load64(src)); + + nft_reg_store64(dst64, src64); break; } break; @@ -62,24 +52,20 @@ void nft_byteorder_eval(const struct nft_expr *expr, case 4: switch (priv->op) { case NFT_BYTEORDER_NTOH: - for (i = 0; i < priv->len / 4; i++) - dst[i] = ntohl((__force __be32)src[i]); + *dst = ntohl((__force __be32)*src); break; case NFT_BYTEORDER_HTON: - for (i = 0; i < priv->len / 4; i++) - dst[i] = (__force __u32)htonl(src[i]); + *dst = (__force __u32)htonl(*src); break; } break; case 2: switch (priv->op) { case NFT_BYTEORDER_NTOH: - for (i = 0; i < priv->len / 2; i++) - d16[i] = ntohs((__force __be16)s16[i]); + nft_reg_store16(dst, ntohs(nft_reg_load_be16(src))); break; case NFT_BYTEORDER_HTON: - for (i = 0; i < priv->len / 2; i++) - d16[i] = (__force __u16)htons(s16[i]); + nft_reg_store_be16(dst, htons(nft_reg_load16(src))); break; } break; @@ -137,20 +123,22 @@ static int nft_byteorder_init(const struct nft_ctx *ctx, if (err < 0) return err; - priv->len = len; + /* no longer support multi-reg conversions */ + if (len != size) + return -EOPNOTSUPP; err = nft_parse_register_load(ctx, tb[NFTA_BYTEORDER_SREG], &priv->sreg, - priv->len); + len); if (err < 0) return err; err = nft_parse_register_store(ctx, tb[NFTA_BYTEORDER_DREG], &priv->dreg, NULL, NFT_DATA_VALUE, - priv->len); + len); if (err < 0) return err; - if (nft_reg_overlap(priv->sreg, priv->dreg, priv->len)) + if (nft_reg_overlap(priv->sreg, priv->dreg, len)) return -EINVAL; return 0; @@ -167,10 +155,11 @@ static int nft_byteorder_dump(struct sk_buff *skb, goto nla_put_failure; if (nla_put_be32(skb, NFTA_BYTEORDER_OP, htonl(priv->op))) goto nla_put_failure; - if (nla_put_be32(skb, NFTA_BYTEORDER_LEN, htonl(priv->len))) - goto nla_put_failure; if (nla_put_be32(skb, NFTA_BYTEORDER_SIZE, htonl(priv->size))) goto nla_put_failure; + /* compatibility for old userspace which permitted size != len */ + if (nla_put_be32(skb, NFTA_BYTEORDER_LEN, htonl(priv->size))) + goto nla_put_failure; return 0; nla_put_failure: From 02896a7fa4cd3ec61d60ba30136841e4f04bdeac Mon Sep 17 00:00:00 2001 From: Nikolay Kuratov Date: Tue, 26 May 2026 19:29:32 +0300 Subject: [PATCH 262/337] net/mlx5: Reorder completion before putting command entry in cmd_work_handler Assuming callback != NULL && !page_queue, cmd_work_handler takes command entry with refcnt == 1 from mlx5_cmd_invoke. If either semaphore timeout or index allocation error happens, it does final cmd_ent_put(ent). To avoid access to freed memory, notify slotted completion before cmd_ent_put. This is theoretical issue found by Svace static analyser. Cc: stable@vger.kernel.org Fixes: 485d65e135712 ("net/mlx5: Add a timeout to acquire the command queue semaphore") Fixes: 0e2909c6bec90 ("net/mlx5: Fix variable not being completed when function returns") Signed-off-by: Nikolay Kuratov Reviewed-by: Md Haris Iqbal Reviewed-by: Moshe Shemesh Acked-by: Tariq Toukan Link: https://patch.msgid.link/20260526162932.501584-1-kniv@yandex-team.ru Signed-off-by: Paolo Abeni --- drivers/net/ethernet/mellanox/mlx5/core/cmd.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c index c89417c1a1f9..e2895972cc82 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c @@ -1002,12 +1002,13 @@ static void cmd_work_handler(struct work_struct *work) ent->callback(-EBUSY, ent->context); mlx5_free_cmd_msg(dev, ent->out); free_msg(dev, ent->in); + complete(&ent->slotted); cmd_ent_put(ent); } else { ent->ret = -EBUSY; complete(&ent->done); + complete(&ent->slotted); } - complete(&ent->slotted); return; } alloc_ret = cmd_alloc_index(cmd, ent); @@ -1017,13 +1018,14 @@ static void cmd_work_handler(struct work_struct *work) ent->callback(-EAGAIN, ent->context); mlx5_free_cmd_msg(dev, ent->out); free_msg(dev, ent->in); + complete(&ent->slotted); cmd_ent_put(ent); } else { ent->ret = -EAGAIN; complete(&ent->done); + complete(&ent->slotted); } up(&cmd->vars.sem); - complete(&ent->slotted); return; } } else { From d3f0a606b9f278ece8a0df626ded9c4044071235 Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Fri, 29 May 2026 23:57:45 +0800 Subject: [PATCH 263/337] dm cache policy smq: check allocation under invalidate lock commit 2d1f7b65f5de ("dm cache policy smq: fix missing locks in invalidating cache blocks") added mq->lock around the destructive part of smq_invalidate_mapping(), but left the e->allocated check outside the critical section. That leaves a check-then-act race. Two concurrent invalidators can both observe e->allocated as true before either of them takes mq->lock. The first invalidator that acquires the lock removes the entry from the queues and hash table and then calls free_entry(), which clears e->allocated and puts the entry back on the free list. The second invalidator can then acquire mq->lock and continue with the stale result of the unlocked check. This can corrupt the SMQ queues or hash table by deleting an entry that is no longer on those structures. It can also hit the allocation check in free_entry() when the same entry is freed again. Move the allocation check under mq->lock so the predicate and the destructive operations are serialized by the same lock. Fixes: 2d1f7b65f5de ("dm cache policy smq: fix missing locks in invalidating cache blocks") Signed-off-by: Guangshuo Li Signed-off-by: Mikulas Patocka --- drivers/md/dm-cache-policy-smq.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/md/dm-cache-policy-smq.c b/drivers/md/dm-cache-policy-smq.c index dd77a93fd68d..1ae304c2f573 100644 --- a/drivers/md/dm-cache-policy-smq.c +++ b/drivers/md/dm-cache-policy-smq.c @@ -1590,18 +1590,22 @@ static int smq_invalidate_mapping(struct dm_cache_policy *p, dm_cblock_t cblock) struct smq_policy *mq = to_smq_policy(p); struct entry *e = get_entry(&mq->cache_alloc, from_cblock(cblock)); unsigned long flags; - - if (!e->allocated) - return -ENODATA; + int r = 0; spin_lock_irqsave(&mq->lock, flags); + if (!e->allocated) { + r = -ENODATA; + goto out; + } // FIXME: what if this block has pending background work? del_queue(mq, e); h_remove(&mq->table, e); free_entry(&mq->cache_alloc, e); + +out: spin_unlock_irqrestore(&mq->lock, flags); - return 0; + return r; } static uint32_t smq_get_hint(struct dm_cache_policy *p, dm_cblock_t cblock) From 5057e1aca011e51ef51498c940ef96f3d3e8a305 Mon Sep 17 00:00:00 2001 From: Jamal Hadi Salim Date: Sun, 31 May 2026 12:08:12 -0400 Subject: [PATCH 264/337] net/sched: act_api: use RCU with deferred freeing for action lifecycle When NEWTFILTER and DELFILTER are run concurrently it is possible to create a race with an associated action. Let's illustrate with CPU0 running NEWTFILTER and CPU1 running DELFILTER: 0: mutex_lock() <-- holds the idr lock 0: rcu_read_lock() 0: p = idr_find(idr, index) <-- action p is valid (RCU protects IDR) 0: mutex_unlock() <-- releases the idr lock 1: refcount_dec_and_mutex_lock() <-- refcnt 1->0, mutex held 1: idr_remove(idr, index) <-- Action removed from IDR 1: mutex_unlock() <-- mutex released allowing us to delete the action 1: tcf_action_cleanup(p); kfree(p) <-- Kfrees p immediately, no deferral 0: refcount_inc_not_zero(&p->tcfa_refcnt) <-- ouch, UAF p points to freed memory This patch fixes the race condition between NEWTFILTER and DELFILTER by adding struct rcu_head to tc_action used in the deferral and introducing a call_rcu() in the delete path to defer the final kfree(). Note: this is a revert of commit d7fb60b9cafb ("net_sched: get rid of tcfa_rcu") but also modernization/simplification to directly use kfree_rcu(). Let's illustrate the new restored code path: 0: rcu_read_lock() 1: refcount_dec_and_mutex_lock() <-- refcnt 1->0, mutex held 1: idr_remove(idr, index) 1: mutex_unlock() 1: call_rcu(&p->tcfa_rcu, tcf_action_rcu_free) <-- defer kfree after grace period 0: p = idr_find(idr, index) 0: refcount_inc_not_zero(&p->tcfa_refcnt) <-- fails, refcnt already 0 1: rcu_read_unlock() <-- release so freeing can run after grace period After CPU1 calls idr_remove(), the object is no longer reachable through the IDR. CPU0's subsequent idr_find() will return NULL, and even if it still held a stale pointer, the immediate kfree() is now deferred until after the RCU grace period, so no UAF can occur. Fixes: d7fb60b9cafb ("net_sched: get rid of tcfa_rcu") Suggested-by: Jakub Kicinski Reported-by: Kyle Zeng Tested-by: Victor Nogueira Tested-by: syzbot@syzkaller.appspotmail.com Signed-off-by: Jamal Hadi Salim Tested-by: Kyle Zeng Reviewed-by: Pedro Tammela Reviewed-by: Eric Dumazet Reviewed-by: Victor Nogueira Link: https://patch.msgid.link/20260531160812.68020-1-jhs@mojatatu.com Signed-off-by: Jakub Kicinski --- include/net/act_api.h | 1 + net/sched/act_api.c | 7 +------ 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/include/net/act_api.h b/include/net/act_api.h index d11b79107930..fd2967ee08f7 100644 --- a/include/net/act_api.h +++ b/include/net/act_api.h @@ -45,6 +45,7 @@ struct tc_action { struct tc_cookie __rcu *user_cookie; struct tcf_chain __rcu *goto_chain; u32 tcfa_flags; + struct rcu_head tcfa_rcu; u8 hw_stats; u8 used_hw_stats; bool used_hw_stats_valid; diff --git a/net/sched/act_api.c b/net/sched/act_api.c index 332fd9695e54..04ea11c90e03 100644 --- a/net/sched/act_api.c +++ b/net/sched/act_api.c @@ -112,11 +112,6 @@ struct tcf_chain *tcf_action_set_ctrlact(struct tc_action *a, int action, } EXPORT_SYMBOL(tcf_action_set_ctrlact); -/* XXX: For standalone actions, we don't need a RCU grace period either, because - * actions are always connected to filters and filters are already destroyed in - * RCU callbacks, so after a RCU grace period actions are already disconnected - * from filters. Readers later can not find us. - */ static void free_tcf(struct tc_action *p) { struct tcf_chain *chain = rcu_dereference_protected(p->goto_chain, 1); @@ -129,7 +124,7 @@ static void free_tcf(struct tc_action *p) if (chain) tcf_chain_put_by_act(chain); - kfree(p); + kfree_rcu(p, tcfa_rcu); } static void offload_action_hw_count_set(struct tc_action *act, From 2a58899d11009bffc7b4b32a571858f381121837 Mon Sep 17 00:00:00 2001 From: Yizhou Zhao Date: Wed, 27 May 2026 16:18:01 +0800 Subject: [PATCH 265/337] 6lowpan: fix off-by-one in multicast context address compression The second memcpy in lowpan_iphc_mcast_ctx_addr_compress() uses &data[1] as destination and &ipaddr->s6_addr[11] as source, but both should be offset by one: &data[2] and &ipaddr->s6_addr[12] respectively. This off-by-one has two consequences: 1. data[1] is overwritten with s6_addr[11], corrupting the RIID field in the compressed multicast address 2. data[5] is never written, so uninitialized kernel stack memory is transmitted over the network via lowpan_push_hc_data(), leaking kernel stack contents The correct inline data layout must match what the decompression function lowpan_uncompress_multicast_ctx_daddr() expects: data[0..1] = s6_addr[1..2] (flags/scope + RIID) data[2..5] = s6_addr[12..15] (group ID) Also zero-initialize the data array as a defensive measure against similar bugs in the future. Fixes: 5609c185f24d ("6lowpan: iphc: add support for stateful compression") Reported-by: Yizhou Zhao Reported-by: Yuxiang Yang Reported-by: Ao Wang Reported-by: Xuewei Feng Reported-by: Qi Li Reported-by: Ke Xu Signed-off-by: Yizhou Zhao Acked-by: Alexander Aring Link: https://patch.msgid.link/20260527081806.42747-1-zhaoyz24@mails.tsinghua.edu.cn Signed-off-by: Jakub Kicinski --- net/6lowpan/iphc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/6lowpan/iphc.c b/net/6lowpan/iphc.c index e116d308a8df..37eaff3f7b69 100644 --- a/net/6lowpan/iphc.c +++ b/net/6lowpan/iphc.c @@ -1086,12 +1086,12 @@ static u8 lowpan_iphc_mcast_ctx_addr_compress(u8 **hc_ptr, const struct lowpan_iphc_ctx *ctx, const struct in6_addr *ipaddr) { - u8 data[6]; + u8 data[6] = {}; /* flags/scope, reserved (RIID) */ memcpy(data, &ipaddr->s6_addr[1], 2); /* group ID */ - memcpy(&data[1], &ipaddr->s6_addr[11], 4); + memcpy(&data[2], &ipaddr->s6_addr[12], 4); lowpan_push_hc_data(hc_ptr, data, 6); return LOWPAN_IPHC_DAM_00; From a213a8950414c684999dcf03edeea6c46ede172e Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 27 May 2026 13:36:29 +0000 Subject: [PATCH 266/337] l2tp: pppol2tp: hold reference to session in pppol2tp_ioctl() pppol2tp_ioctl() read sock->sk->sk_user_data directly without any locks or reference counting. If a controllable sleep was induced during copy_from_user() (e.g. via a userfaultfd page fault sleep), a concurrent socket close could trigger pppol2tp_session_close() asynchronously. This frees the l2tp_session structure via the l2tp_session_del_work workqueue. Upon resuming, the ioctl thread dereferences the stale session pointer, resulting in a Use-After-Free (UAF). Fix this by securely fetching the session reference using the RCU-safe, refcounted helper pppol2tp_sock_to_session(sk) on entry. This locks the session's refcount across the sleep. We structured the function to exit via standard err breaks, guaranteeing that l2tp_session_put() is cleanly called on all return paths to drop the reference. To preserve existing behavior we validate the session and its magic signature only for the specific L2TP commands that require it. This ensures that generic/unknown ioctls called on an unconnected socket still return -ENOIOCTLCMD and correctly fall back to generic handlers (e.g. in sock_do_ioctl()). Signed-off-by: Lee Jones Fixes: fd558d186df2 ("l2tp: Split pppol2tp patch into separate l2tp and ppp parts") Link: https://patch.msgid.link/20260527133630.2120612-1-lee@kernel.org Signed-off-by: Jakub Kicinski --- net/l2tp/l2tp_ppp.c | 92 +++++++++++++++++++++++++++------------------ 1 file changed, 55 insertions(+), 37 deletions(-) diff --git a/net/l2tp/l2tp_ppp.c b/net/l2tp/l2tp_ppp.c index 99d6582f41de..e0b1915be1a6 100644 --- a/net/l2tp/l2tp_ppp.c +++ b/net/l2tp/l2tp_ppp.c @@ -1045,64 +1045,76 @@ static int pppol2tp_ioctl(struct socket *sock, unsigned int cmd, { struct pppol2tp_ioc_stats stats; struct l2tp_session *session; + int err = 0; + + session = pppol2tp_sock_to_session(sock->sk); + + /* Validate session presence and magic integrity ONLY for commands + * that belong to L2TP and require a valid session. + */ + switch (cmd) { + case PPPIOCGMRU: + case PPPIOCGFLAGS: + case PPPIOCSMRU: + case PPPIOCSFLAGS: + case PPPIOCGL2TPSTATS: + if (!session) + return -ENOTCONN; + + if (session->magic != L2TP_SESSION_MAGIC) { + l2tp_session_put(session); + return -EBADF; + } + break; + default: + break; + } switch (cmd) { case PPPIOCGMRU: case PPPIOCGFLAGS: - session = sock->sk->sk_user_data; - if (!session) - return -ENOTCONN; - - if (WARN_ON(session->magic != L2TP_SESSION_MAGIC)) - return -EBADF; - /* Not defined for tunnels */ - if (!session->session_id && !session->peer_session_id) - return -ENOSYS; + if (!session->session_id && !session->peer_session_id) { + err = -ENOSYS; + break; + } - if (put_user(0, (int __user *)arg)) - return -EFAULT; + if (put_user(0, (int __user *)arg)) { + err = -EFAULT; + break; + } break; case PPPIOCSMRU: case PPPIOCSFLAGS: - session = sock->sk->sk_user_data; - if (!session) - return -ENOTCONN; - - if (WARN_ON(session->magic != L2TP_SESSION_MAGIC)) - return -EBADF; - /* Not defined for tunnels */ - if (!session->session_id && !session->peer_session_id) - return -ENOSYS; + if (!session->session_id && !session->peer_session_id) { + err = -ENOSYS; + break; + } - if (!access_ok((int __user *)arg, sizeof(int))) - return -EFAULT; + if (!access_ok((int __user *)arg, sizeof(int))) { + err = -EFAULT; + break; + } break; case PPPIOCGL2TPSTATS: - session = sock->sk->sk_user_data; - if (!session) - return -ENOTCONN; - - if (WARN_ON(session->magic != L2TP_SESSION_MAGIC)) - return -EBADF; - /* Session 0 represents the parent tunnel */ if (!session->session_id && !session->peer_session_id) { u32 session_id; - int err; if (copy_from_user(&stats, (void __user *)arg, - sizeof(stats))) - return -EFAULT; + sizeof(stats))) { + err = -EFAULT; + break; + } session_id = stats.session_id; err = pppol2tp_tunnel_copy_stats(&stats, session->tunnel); if (err < 0) - return err; + break; stats.session_id = session_id; } else { @@ -1112,15 +1124,21 @@ static int pppol2tp_ioctl(struct socket *sock, unsigned int cmd, stats.tunnel_id = session->tunnel->tunnel_id; stats.using_ipsec = l2tp_tunnel_uses_xfrm(session->tunnel); - if (copy_to_user((void __user *)arg, &stats, sizeof(stats))) - return -EFAULT; + if (copy_to_user((void __user *)arg, &stats, sizeof(stats))) { + err = -EFAULT; + break; + } break; default: - return -ENOIOCTLCMD; + err = -ENOIOCTLCMD; + break; } - return 0; + if (session) + l2tp_session_put(session); + + return err; } /***************************************************************************** From 3522b21fd7e1863d0734537737bd59f1b90d0190 Mon Sep 17 00:00:00 2001 From: Mark Bloch Date: Thu, 28 May 2026 22:14:10 +0300 Subject: [PATCH 267/337] devlink: Release nested relation on devlink free devlink relation state is normally released from devl_unregister(), which calls devlink_rel_put(). This misses devlink instances that get a nested relation before registration and then fail probe before devl_register() is reached. That flow can happen for SFs. The child devlink gets linked to its parent before registration, then a later probe error calls devlink_free() directly. Since the instance was never registered, devl_unregister() is not called and devlink->rel is leaked. Release any pending relation from devlink_free() as well. The registered path is unchanged because devl_unregister() already clears devlink->rel before devlink_free() runs. Fixes: c137743bce02 ("devlink: introduce object and nested devlink relationship infra") Signed-off-by: Mark Bloch Reviewed-by: Jiri Pirko Link: https://patch.msgid.link/20260528191411.3270532-1-mbloch@nvidia.com Signed-off-by: Jakub Kicinski --- net/devlink/core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/devlink/core.c b/net/devlink/core.c index eeb6a71f5f56..fe9f6a0a67d5 100644 --- a/net/devlink/core.c +++ b/net/devlink/core.c @@ -518,6 +518,8 @@ void devlink_free(struct devlink *devlink) { ASSERT_DEVLINK_NOT_REGISTERED(devlink); + devlink_rel_put(devlink); + WARN_ON(!list_empty(&devlink->trap_policer_list)); WARN_ON(!list_empty(&devlink->trap_group_list)); WARN_ON(!list_empty(&devlink->trap_list)); From 4cd92957e8f8cc4ebfe8a5d4203c14c592fde6b1 Mon Sep 17 00:00:00 2001 From: Yuqi Xu Date: Fri, 29 May 2026 23:25:37 +0800 Subject: [PATCH 268/337] wifi: nl80211: reject oversized EMA RNR lists nl80211_parse_rnr_elems() stores the parsed element count in a u8-backed cfg80211_rnr_elems::cnt field and uses that count to size the flexible array allocation. Reject nested NL80211_ATTR_EMA_RNR_ELEMS input once the count reaches 255, before incrementing it again. This keeps the parser aligned with the data structure it fills and matches the existing bound check used by nl80211_parse_mbssid_elems(). Fixes: dbbb27e183b1 ("cfg80211: support RNR for EMA AP") Cc: stable@kernel.org Reported-by: Yuan Tan Reported-by: Zhengchuan Liang Reported-by: Xin Liu Assisted-by: Codex:gpt-5.4 Signed-off-by: Yuqi Xu Signed-off-by: Ren Wei Link: https://patch.msgid.link/20260529152542.1412734-1-n05ec@lzu.edu.cn Signed-off-by: Johannes Berg --- net/wireless/nl80211.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 7db9cd433801..dac2e8643c49 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -6366,6 +6366,9 @@ nl80211_parse_rnr_elems(struct wiphy *wiphy, struct nlattr *attrs, if (ret) return ERR_PTR(ret); + if (num_elems >= 255) + return ERR_PTR(-EINVAL); + num_elems++; } From 6c0cf89f36ac0c0fd8687a4ccdce2efb23a9c663 Mon Sep 17 00:00:00 2001 From: Deepanshu Kartikey Date: Sun, 31 May 2026 06:47:21 +0530 Subject: [PATCH 269/337] wifi: mac80211: limit injected antenna index in ieee80211_parse_tx_radiotap When parsing the radiotap header of an injected frame, ieee80211_parse_tx_radiotap() uses the IEEE80211_RADIOTAP_ANTENNA value directly as a shift count: info->control.antennas |= BIT(*iterator.this_arg); *iterator.this_arg is an 8-bit value taken straight from the frame supplied by userspace, so BIT() can be asked to shift by up to 255. That is undefined behaviour on the unsigned long and is reported by UBSAN: UBSAN: shift-out-of-bounds in net/mac80211/tx.c:2174:30 shift exponent 235 is too large for 64-bit type 'unsigned long' Call Trace: ieee80211_parse_tx_radiotap+0xadb/0x1950 net/mac80211/tx.c:2174 ieee80211_monitor_start_xmit+0xb1f/0x1250 net/mac80211/tx.c:2451 ... packet_sendmsg+0x3eb6/0x50f0 net/packet/af_packet.c:3109 info->control.antennas is a 2-bit bitmap (u8 antennas:2), so only antenna indices 0 and 1 can ever be represented. Ignore any larger value instead of shifting out of bounds. Reported-by: syzbot+8e0622f6d9446420271f@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=8e0622f6d9446420271f Fixes: ef246a1480cc ("wifi: mac80211: support antenna control in injection") Signed-off-by: Deepanshu Kartikey Link: https://patch.msgid.link/20260531011721.102941-1-kartikey406@gmail.com Signed-off-by: Johannes Berg --- net/mac80211/tx.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index b487d2330f25..ea7f63e1fc17 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -2181,7 +2181,9 @@ bool ieee80211_parse_tx_radiotap(struct sk_buff *skb, case IEEE80211_RADIOTAP_ANTENNA: /* this can appear multiple times, keep a bitmap */ - info->control.antennas |= BIT(*iterator.this_arg); + /* control.antennas is only a 2-bit bitmap */ + if (*iterator.this_arg < 2) + info->control.antennas |= BIT(*iterator.this_arg); break; case IEEE80211_RADIOTAP_DATA_RETRIES: From 73bf3cca7de6a73f53b6a52dc3b1c82ae5667a4d Mon Sep 17 00:00:00 2001 From: Oscar Maes Date: Thu, 28 May 2026 16:03:20 +0200 Subject: [PATCH 270/337] pcnet32: stop holding device spin lock during napi_complete_done napi_complete_done may call gro_flush_normal (though not currently, as GRO is unsupported at the moment), which may result in packet TX. This will eventually result in calling pcnet32_start_xmit - resulting in a deadlock while trying to re-acquire the already locked spin lock. It is safe to split the spinlock block into two, because the hardware registers are still protected from concurrent access, and the two blocks perform unrelated operations that don't need to happen atomically. Fixes: 5b2ec6f2be51 ("pcnet32: use napi_complete_done()") Reviewed-by: Andrew Lunn Signed-off-by: Oscar Maes Reviewed-by: Alexander Lobakin Link: https://patch.msgid.link/20260528140320.5556-1-oscmaes92@gmail.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/amd/pcnet32.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/amd/pcnet32.c b/drivers/net/ethernet/amd/pcnet32.c index 911808ab13a7..4f3076d4ea34 100644 --- a/drivers/net/ethernet/amd/pcnet32.c +++ b/drivers/net/ethernet/amd/pcnet32.c @@ -1407,8 +1407,10 @@ static int pcnet32_poll(struct napi_struct *napi, int budget) pcnet32_restart(dev, CSR0_START); netif_wake_queue(dev); } + spin_unlock_irqrestore(&lp->lock, flags); if (work_done < budget && napi_complete_done(napi, work_done)) { + spin_lock_irqsave(&lp->lock, flags); /* clear interrupt masks */ val = lp->a->read_csr(ioaddr, CSR3); val &= 0x00ff; @@ -1416,9 +1418,9 @@ static int pcnet32_poll(struct napi_struct *napi, int budget) /* Set interrupt enable. */ lp->a->write_csr(ioaddr, CSR0, CSR0_INTEN); + spin_unlock_irqrestore(&lp->lock, flags); } - spin_unlock_irqrestore(&lp->lock, flags); return work_done; } From b748765019fe9e9234660327090fc1a9665cdbdd Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Fri, 29 May 2026 19:39:23 +0000 Subject: [PATCH 271/337] net: Annotate sk->sk_write_space() for UDP SOCKMAP. UDP TX skb->destructor() is sock_wfree(), and UDP holds lock_sock() only for UDP_CORK / MSG_MORE sendmsg(). Otherwise, sk->sk_write_space() may be read locklessly while SOCKMAP rewrites sk->sk_write_space(). Let's use WRITE_ONCE() and READ_ONCE() for sk->sk_write_space(). Note that the write side is annotated by commit 2ef2b20cf4e0 ("net: annotate data-races around sk->sk_{data_ready,write_space}"). Fixes: 7b98cd42b049 ("bpf: sockmap: Add UDP support") Signed-off-by: Kuniyuki Iwashima Reviewed-by: Jakub Sitnicki Link: https://patch.msgid.link/20260529193941.3897256-1-kuniyu@google.com Signed-off-by: Jakub Kicinski --- net/core/sock.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/net/core/sock.c b/net/core/sock.c index b37b664b6eb9..d097025c116a 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -2676,8 +2676,12 @@ void sock_wfree(struct sk_buff *skb) int old; if (!sock_flag(sk, SOCK_USE_WRITE_QUEUE)) { + void (*sk_write_space)(struct sock *sk); + + sk_write_space = READ_ONCE(sk->sk_write_space); + if (sock_flag(sk, SOCK_RCU_FREE) && - sk->sk_write_space == sock_def_write_space) { + sk_write_space == sock_def_write_space) { rcu_read_lock(); free = __refcount_sub_and_test(len, &sk->sk_wmem_alloc, &old); @@ -2693,7 +2697,7 @@ void sock_wfree(struct sk_buff *skb) * after sk_write_space() call */ WARN_ON(refcount_sub_and_test(len - 1, &sk->sk_wmem_alloc)); - sk->sk_write_space(sk); + sk_write_space(sk); len = 1; } /* From e10902df24488ca722303133acfc82490f7d59ad Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Mon, 1 Jun 2026 18:20:55 +0000 Subject: [PATCH 272/337] tcp: Add preempt_{disable,enable}_nested() in reqsk_queue_hash_req(). syzbot reported a weird reqsk->rsk_refcnt underflow in __inet_csk_reqsk_queue_drop(). The captured reqsk_put() in __inet_csk_reqsk_queue_drop() is called only when it successfully removes reqsk from ehash. Moreover, reqsk_timer_handler() calls another reqsk_put() after that. This indicates that the reqsk was missing both refcnts for ehash and the timer itself. Since all the syzbot reports had PREEMPT_RT enabled, the only possible scenario is that reqsk_queue_hash_req() is preempted after mod_timer() and before refcount_set(), and then the timer triggered after 1s aborts the reqsk due to its listener's close(). Let's wrap mod_timer() and refcount_set() with preempt_disable_nested() and preempt_enable_nested(). Note that inet_ehash_insert() holds the normal spin_lock() (mutex in PREEMPT_RT), so it must be called outside of preempt_disable_nested(), but this is fine. The lookup path just ignores 0 sk_refcnt entries in ehash and tries to create another reqsk, but this will fail at inet_ehash_insert(). [0]: refcount_t: underflow; use-after-free. WARNING: lib/refcount.c:28 at refcount_warn_saturate+0xb2/0x110 lib/refcount.c:28, CPU#0: ktimers/0/16 Modules linked in: CPU: 0 UID: 0 PID: 16 Comm: ktimers/0 Tainted: G L syzkaller #0 PREEMPT_{RT,(full)} Tainted: [L]=SOFTLOCKUP Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 04/18/2026 RIP: 0010:refcount_warn_saturate+0xb2/0x110 lib/refcount.c:28 Code: e4 7d d1 0a 67 48 0f b9 3a eb 4a e8 38 3d 23 fd 48 8d 3d e1 7d d1 0a 67 48 0f b9 3a eb 37 e8 25 3d 23 fd 48 8d 3d de 7d d1 0a <67> 48 0f b9 3a eb 24 e8 12 3d 23 fd 48 8d 3d db 7d d1 0a 67 48 0f RSP: 0000:ffffc90000157948 EFLAGS: 00010246 RAX: ffffffff84a1301b RBX: 0000000000000003 RCX: ffff88801ca98000 RDX: 0000000000000100 RSI: 0000000000000000 RDI: ffffffff8f72ae00 RBP: ffffffff99ae3b01 R08: ffff88801ca98000 R09: 0000000000000005 R10: 0000000000000100 R11: 0000000000000004 R12: ffff8880425ef568 R13: ffff8880425ef4f8 R14: ffff8880425ef578 R15: 0000000000000000 FS: 0000000000000000(0000) GS:ffff888126386000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00007f7b46710e9c CR3: 000000000dbb6000 CR4: 00000000003526f0 Call Trace: __refcount_sub_and_test include/linux/refcount.h:400 [inline] __refcount_dec_and_test include/linux/refcount.h:432 [inline] refcount_dec_and_test include/linux/refcount.h:450 [inline] reqsk_put include/net/request_sock.h:136 [inline] __inet_csk_reqsk_queue_drop+0x3ce/0x440 net/ipv4/inet_connection_sock.c:1007 reqsk_timer_handler+0x651/0xdf0 net/ipv4/inet_connection_sock.c:1137 call_timer_fn+0x192/0x5e0 kernel/time/timer.c:1748 expire_timers kernel/time/timer.c:1799 [inline] __run_timers kernel/time/timer.c:2374 [inline] __run_timer_base+0x6a3/0x9f0 kernel/time/timer.c:2386 run_timer_base kernel/time/timer.c:2395 [inline] run_timer_softirq+0x67/0x170 kernel/time/timer.c:2403 handle_softirqs+0x1de/0x6d0 kernel/softirq.c:622 __do_softirq kernel/softirq.c:656 [inline] run_ktimerd+0x69/0x100 kernel/softirq.c:1151 smpboot_thread_fn+0x541/0xa50 kernel/smpboot.c:160 kthread+0x388/0x470 kernel/kthread.c:436 ret_from_fork+0x514/0xb70 arch/x86/kernel/process.c:158 ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245 Fixes: d2d6422f8bd1 ("x86: Allow to enable PREEMPT_RT.") Reported-by: syzbot+e809069bc15f26300526@syzkaller.appspotmail.com Closes: https://lore.kernel.org/all/6a1a7bcf.0a9e871e.332604.000b.GAE@google.com/ Signed-off-by: Kuniyuki Iwashima Reviewed-by: Eric Dumazet Reviewed-by: Sebastian Andrzej Siewior Link: https://patch.msgid.link/20260601182101.3183993-1-kuniyu@google.com Signed-off-by: Jakub Kicinski --- net/ipv4/inet_connection_sock.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index dbcd37dfdc15..5b934ce8d98a 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -1148,6 +1148,9 @@ static bool reqsk_queue_hash_req(struct request_sock *req) /* The timer needs to be setup after a successful insertion. */ req->timeout = tcp_timeout_init((struct sock *)req); timer_setup(&req->rsk_timer, reqsk_timer_handler, TIMER_PINNED); + + preempt_disable_nested(); + mod_timer(&req->rsk_timer, jiffies + req->timeout); /* before letting lookups find us, make sure all req fields @@ -1155,6 +1158,9 @@ static bool reqsk_queue_hash_req(struct request_sock *req) */ smp_wmb(); refcount_set(&req->rsk_refcnt, 2 + 1); + + preempt_enable_nested(); + return true; } From afd0f17ca46258cec3a5cc48b8df9327fe772490 Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Sat, 30 May 2026 06:42:58 +0000 Subject: [PATCH 273/337] hsr: Remove WARN_ONCE() in hsr_addr_is_self(). syzbot reported the warning [0] in hsr_addr_is_self(), whose assumption is simply wrong. hsr->self_node is cleared in hsr_del_self_node(), which is called from hsr_dellink(). Since dev->rtnl_link_ops->dellink() is called before unregister_netdevice_many(), there is a window when user can find the device but without hsr->self_node. Let's remove WARN_ONCE() in hsr_addr_is_self(). [0]: HSR: No self node WARNING: net/hsr/hsr_framereg.c:39 at hsr_addr_is_self+0x211/0x3f0 net/hsr/hsr_framereg.c:39, CPU#0: syz.4.16848/17220 Modules linked in: CPU: 0 UID: 0 PID: 17220 Comm: syz.4.16848 Tainted: G L syzkaller #0 PREEMPT_{RT,(full)} Tainted: [L]=SOFTLOCKUP Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 04/18/2026 RIP: 0010:hsr_addr_is_self+0x211/0x3f0 net/hsr/hsr_framereg.c:39 Code: 33 2f 41 0f b7 dd 89 ee 09 de 31 ff e8 c8 b4 c6 f6 09 dd 74 54 e8 0f b0 c6 f6 31 ed eb 53 e8 06 b0 c6 f6 48 8d 3d 2f 50 9c 04 <67> 48 0f b9 3a 31 ed eb 42 e8 c1 13 1f 00 89 c5 31 ff 89 c6 e8 96 RSP: 0018:ffffc900041c70e0 EFLAGS: 00010283 RAX: ffffffff8afdc6ca RBX: ffffffff8afdc4e6 RCX: 0000000000080000 RDX: ffffc90010493000 RSI: 0000000000000948 RDI: ffffffff8f9a1700 RBP: 0000000000000001 R08: 0000000000000000 R09: 0000000000000000 R10: ffffc900041c71e8 R11: fffff52000838e3f R12: dffffc0000000000 R13: ffff888041f9e3c0 R14: ffff888086ee3802 R15: 0000000000000000 FS: 00007f6fe985d6c0(0000) GS:ffff888126176000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00007f80bd437dac CR3: 0000000025096000 CR4: 00000000003526f0 DR0: ffffffffffffffff DR1: 00000000000001f8 DR2: 0000000000000002 DR3: ffffffffefffff15 DR6: 00000000ffff0ff0 DR7: 0000000000000400 Call Trace: check_local_dest net/hsr/hsr_forward.c:592 [inline] fill_frame_info net/hsr/hsr_forward.c:728 [inline] hsr_forward_skb+0xa11/0x2a80 net/hsr/hsr_forward.c:739 hsr_dev_xmit+0x253/0x370 net/hsr/hsr_device.c:236 __netdev_start_xmit include/linux/netdevice.h:5368 [inline] netdev_start_xmit include/linux/netdevice.h:5377 [inline] xmit_one net/core/dev.c:3888 [inline] dev_hard_start_xmit+0x2df/0x860 net/core/dev.c:3904 __dev_queue_xmit+0x1428/0x3900 net/core/dev.c:4870 neigh_output include/net/neighbour.h:556 [inline] ip_finish_output2+0xcec/0x10b0 net/ipv4/ip_output.c:237 ip_send_skb net/ipv4/ip_output.c:1510 [inline] ip_push_pending_frames+0x8b/0x110 net/ipv4/ip_output.c:1530 raw_sendmsg+0x1547/0x1a50 net/ipv4/raw.c:659 sock_sendmsg_nosec net/socket.c:787 [inline] __sock_sendmsg net/socket.c:802 [inline] ____sys_sendmsg+0x7da/0x9c0 net/socket.c:2698 ___sys_sendmsg+0x2a5/0x360 net/socket.c:2752 __sys_sendmsg net/socket.c:2784 [inline] __do_sys_sendmsg net/socket.c:2789 [inline] __se_sys_sendmsg net/socket.c:2787 [inline] __x64_sys_sendmsg+0x1c3/0x2a0 net/socket.c:2787 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline] do_syscall_64+0x15f/0xf80 arch/x86/entry/syscall_64.c:94 entry_SYSCALL_64_after_hwframe+0x77/0x7f RIP: 0033:0x7f6feb62ce59 Code: ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 e8 ff ff ff f7 d8 64 89 01 48 RSP: 002b:00007f6fe985d028 EFLAGS: 00000246 ORIG_RAX: 000000000000002e RAX: ffffffffffffffda RBX: 00007f6feb8a6090 RCX: 00007f6feb62ce59 RDX: 0000000000000000 RSI: 0000200000000000 RDI: 0000000000000004 RBP: 00007f6feb6c2d6f R08: 0000000000000000 R09: 0000000000000000 R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000 R13: 00007f6feb8a6128 R14: 00007f6feb8a6090 R15: 00007ffcf01cc488 Fixes: f266a683a480 ("net/hsr: Better frame dispatch") Reported-by: syzbot+652670cf249077eb498b@syzkaller.appspotmail.com Closes: https://lore.kernel.org/netdev/6a1a861e.b111c304.35cd64.0016.GAE@google.com/ Signed-off-by: Kuniyuki Iwashima Reviewed-by: Fernando Fernandez Mancera Link: https://patch.msgid.link/20260530064300.340793-1-kuniyu@google.com Signed-off-by: Jakub Kicinski --- net/hsr/hsr_framereg.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/net/hsr/hsr_framereg.c b/net/hsr/hsr_framereg.c index b514e43766ef..a28dfd8490c5 100644 --- a/net/hsr/hsr_framereg.c +++ b/net/hsr/hsr_framereg.c @@ -35,10 +35,8 @@ bool hsr_addr_is_self(struct hsr_priv *hsr, unsigned char *addr) rcu_read_lock(); sn = rcu_dereference(hsr->self_node); - if (!sn) { - WARN_ONCE(1, "HSR: No self node\n"); + if (!sn) goto out; - } if (ether_addr_equal(addr, sn->macaddress_A) || ether_addr_equal(addr, sn->macaddress_B)) From 16e408e607a94b646fb14a2a98422c6877ae4b3c Mon Sep 17 00:00:00 2001 From: Yizhou Zhao Date: Wed, 27 May 2026 16:31:58 +0800 Subject: [PATCH 274/337] net: garp: fix unsigned integer underflow in garp_pdu_parse_attr The receive-side GARP attribute parser computes dlen with reversed operands: dlen = sizeof(*ga) - ga->len; ga->len is the on-wire attribute length and includes the GARP attribute header. For normal attributes with data, ga->len is larger than sizeof(*ga), so the subtraction underflows in unsigned arithmetic. The resulting value is later passed to garp_attr_lookup(), whose length argument is u8. After truncation, the parsed data length usually no longer matches the length stored for locally registered attributes, so received Join/Leave events are ignored. This breaks the GARP receive path for common attributes, such as GVRP VLAN registration attributes. Compute the data length as the attribute length minus the header length. Fixes: eca9ebac651f ("net: Add GARP applicant-only participant") Reported-by: Yizhou Zhao Reported-by: Yuxiang Yang Reported-by: Ao Wang Reported-by: Xuewei Feng Reported-by: Qi Li Reported-by: Ke Xu Signed-off-by: Yizhou Zhao Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260527083200.42861-1-zhaoyz24@mails.tsinghua.edu.cn Signed-off-by: Jakub Kicinski --- net/802/garp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/802/garp.c b/net/802/garp.c index 6f563b6797d9..c7a39f298ad6 100644 --- a/net/802/garp.c +++ b/net/802/garp.c @@ -453,7 +453,7 @@ static int garp_pdu_parse_attr(struct garp_applicant *app, struct sk_buff *skb, if (!pskb_may_pull(skb, ga->len)) return -1; skb_pull(skb, ga->len); - dlen = sizeof(*ga) - ga->len; + dlen = ga->len - sizeof(*ga); if (attrtype > app->app->maxattr) return 0; From 20cf0fb715c41111469577e85e35d15f099473e0 Mon Sep 17 00:00:00 2001 From: Yuqi Xu Date: Fri, 29 May 2026 21:01:44 +0800 Subject: [PATCH 275/337] net: rds: clear i_sends on setup unwind The RDS IB connection teardown path is written so it can run during partial startup and on repeated shutdown attempts. It uses NULL pointers to distinguish resources that are still owned from resources that have already been released. When rds_ib_setup_qp() fails after allocating i_sends but before allocating i_recvs, the sends_out path frees i_sends without clearing the pointer. A later shutdown pass can still treat that stale pointer as a live send ring allocation. Clear i_sends after vfree() in the error unwind path so the existing shutdown logic continues to use the correct ownership state. Fixes: 3b12f73a5c29 ("rds: ib: add error handle") Cc: stable@kernel.org Reported-by: Yuan Tan Reported-by: Zhengchuan Liang Reported-by: Xin Liu Signed-off-by: Yuqi Xu Signed-off-by: Ren Wei Reviewed-by: Allison Henderson Link: https://patch.msgid.link/5a0f7624bb9845a7b67d26166a150b59e7f394ce.1779632468.git.xuyq21@lenovo.com Signed-off-by: Jakub Kicinski --- net/rds/ib_cm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/rds/ib_cm.c b/net/rds/ib_cm.c index 0c64c504f79d..4001de0c4959 100644 --- a/net/rds/ib_cm.c +++ b/net/rds/ib_cm.c @@ -656,6 +656,7 @@ static int rds_ib_setup_qp(struct rds_connection *conn) sends_out: vfree(ic->i_sends); + ic->i_sends = NULL; ack_dma_out: rds_dma_hdr_free(rds_ibdev->dev, ic->i_ack, ic->i_ack_dma, From 8173d22b211f615015f7b35f48ab11a6dd78dc99 Mon Sep 17 00:00:00 2001 From: David Thompson Date: Fri, 29 May 2026 21:03:00 +0000 Subject: [PATCH 276/337] net: lan743x: permit VLAN-tagged packets up to configured MTU VLAN-tagged interfaces on lan743x devices were previously unreachable via SSH and failed to respond to large ping packets (e.g. "ping -s 1469" given MTU=1500). In these scenarios, "ethtool -S" reports non-zero "RX Oversize Frame Errors". According to Microchip AN2948, the MAC_RX FSE (VLAN field size enforcement) bit determines whether frames with VLAN tags exceeding the base MTU plus tag length are discarded. The driver must set the MAC_RX.FSE bit before setting MAC_RX.RXEN to allow VLAN-tagged frames up to the interface MTU, preventing them from being treated as oversized. As a result, both the base and VLAN-tagged interfaces can use the same MTU without receive errors. Fixes: 23f0703c125b ("lan743x: Add main source files for new lan743x driver") Signed-off-by: David Thompson Reviewed-by: Thangaraj Samynathan Reviewed-by: Nicolai Buchwitz Tested-by: Nicolai Buchwitz # lan7430 on arm64 (RevPi Link: https://patch.msgid.link/20260529210300.433135-1-davthompson@nvidia.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/microchip/lan743x_main.c | 32 +++++++++++++++++++ drivers/net/ethernet/microchip/lan743x_main.h | 1 + 2 files changed, 33 insertions(+) diff --git a/drivers/net/ethernet/microchip/lan743x_main.c b/drivers/net/ethernet/microchip/lan743x_main.c index f3332417162e..ffac22883e49 100644 --- a/drivers/net/ethernet/microchip/lan743x_main.c +++ b/drivers/net/ethernet/microchip/lan743x_main.c @@ -1219,6 +1219,36 @@ static void lan743x_mac_set_address(struct lan743x_adapter *adapter, "MAC address set to %pM\n", addr); } +static void lan743x_mac_rx_enable_fse(struct lan743x_adapter *adapter) +{ + u32 mac_rx; + bool rxen; + + mac_rx = lan743x_csr_read(adapter, MAC_RX); + if (mac_rx & MAC_RX_FSE_) + return; + + rxen = mac_rx & MAC_RX_RXEN_; + if (rxen) { + mac_rx &= ~MAC_RX_RXEN_; + lan743x_csr_write(adapter, MAC_RX, mac_rx); + lan743x_csr_wait_for_bit(adapter, MAC_RX, MAC_RX_RXD_, + 1, 1000, 20000, 100); + } + + /* Per AN2948, hardware prevents modification of the FSE bit while the + * MAC receiver is enabled (RXEN bit set). Use separate register write + * to assert the FSE bit before enabling the RXEN bit in MAC_RX + */ + mac_rx |= MAC_RX_FSE_; + lan743x_csr_write(adapter, MAC_RX, mac_rx); + + if (rxen) { + mac_rx |= MAC_RX_RXEN_; + lan743x_csr_write(adapter, MAC_RX, mac_rx); + } +} + static int lan743x_mac_init(struct lan743x_adapter *adapter) { bool mac_address_valid = true; @@ -1258,6 +1288,8 @@ static int lan743x_mac_init(struct lan743x_adapter *adapter) lan743x_mac_set_address(adapter, adapter->mac_address); eth_hw_addr_set(netdev, adapter->mac_address); + lan743x_mac_rx_enable_fse(adapter); + return 0; } diff --git a/drivers/net/ethernet/microchip/lan743x_main.h b/drivers/net/ethernet/microchip/lan743x_main.h index 160d94a7cee6..1573c8f9c993 100644 --- a/drivers/net/ethernet/microchip/lan743x_main.h +++ b/drivers/net/ethernet/microchip/lan743x_main.h @@ -182,6 +182,7 @@ #define MAC_RX (0x104) #define MAC_RX_MAX_SIZE_SHIFT_ (16) #define MAC_RX_MAX_SIZE_MASK_ (0x3FFF0000) +#define MAC_RX_FSE_ BIT(2) #define MAC_RX_RXD_ BIT(1) #define MAC_RX_RXEN_ BIT(0) From b455410146bf723c7ebcb49ecd5becc0d6611482 Mon Sep 17 00:00:00 2001 From: Tapio Reijonen Date: Fri, 29 May 2026 06:18:57 +0000 Subject: [PATCH 277/337] net: fec: fix pinctrl default state restore order on resume In fec_resume(), fec_enet_clk_enable() is called before pinctrl_pm_select_default_state() in the non-WoL path, inverting the ordering used in fec_suspend() which correctly switches to the sleep pinctrl state before disabling clocks. For PHYs with the PHY_RST_AFTER_CLK_EN flag (e.g. TI DP83848 or SMSC LAN87xx), fec_enet_clk_enable() triggers a hardware reset pulse via the phy-reset GPIO. With the GPIO pin still in sleep pinctrl state at that point, the GPIO write has no physical effect and the PHY never receives the required reset after clock enable, leading to unreliable link establishment after system resume. Fix by restoring the default pinctrl state before enabling clocks, making resume the proper mirror of suspend. The call is made unconditionally: fec_suspend() only switches to the sleep pinctrl state on the non-WoL path and leaves the pins in the default state when WoL is enabled, so on a WoL resume the device is already in the default state and pinctrl_pm_select_default_state() is a no-op. Fixes: de40ed31b3c5 ("net: fec: add Wake-on-LAN support") Signed-off-by: Tapio Reijonen Reviewed-by: Wei Fang Link: https://patch.msgid.link/20260529-b4-fec-resume-pinctrl-order-v3-1-6eda0f592fca@vaisala.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/freescale/fec_main.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index f89aa94ce020..6ebde65d7f1b 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -5594,6 +5594,7 @@ static int fec_resume(struct device *dev) if (fep->rpm_active) pm_runtime_force_resume(dev); + pinctrl_pm_select_default_state(&fep->pdev->dev); ret = fec_enet_clk_enable(ndev, true); if (ret) { rtnl_unlock(); @@ -5610,8 +5611,6 @@ static int fec_resume(struct device *dev) val &= ~(FEC_ECR_MAGICEN | FEC_ECR_SLEEP); writel(val, fep->hwp + FEC_ECNTRL); fep->wol_flag &= ~FEC_WOL_FLAG_SLEEP_ON; - } else { - pinctrl_pm_select_default_state(&fep->pdev->dev); } fec_restart(ndev); netif_tx_lock_bh(ndev); From 5eba3e48d78edd7551b992cb7ba687019b3a78da Mon Sep 17 00:00:00 2001 From: Zhao Zhang Date: Sat, 30 May 2026 23:57:14 +0800 Subject: [PATCH 278/337] sctp: diag: reject stale associations in dump_one path The SCTP exact sock_diag lookup can hold a transport reference, block on lock_sock(sk), and then resume after sctp_association_free() has marked the association dead and freed its bind address list. When that happens, inet_assoc_attr_size() and inet_diag_msg_sctpasoc_fill() can still dereference association state that is no longer valid for reporting. In particular, inet_diag_msg_sctpasoc_fill() may read an empty bind-address list as a real sctp_sockaddr_entry and trigger an out-of-bounds read from unrelated association memory. Reject the association after taking the socket lock if it has been reaped or detached from the endpoint, and report the lookup as stale. This keeps the exact dump-one path from formatting torn association state. Fixes: 8f840e47f190 ("sctp: add the sctp_diag.c file") Cc: stable@kernel.org Reported-by: Yuan Tan Reported-by: Yifan Wu Reported-by: Juefei Pu Reported-by: Zhengchuan Liang Reported-by: Xin Liu Signed-off-by: Zhao Zhang Signed-off-by: Ren Wei Acked-by: Xin Long Link: https://patch.msgid.link/fac6043fa20a2ff68e12958c431836f692c51268.1780113823.git.zzhan461@ucr.edu Signed-off-by: Jakub Kicinski --- net/sctp/diag.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/net/sctp/diag.c b/net/sctp/diag.c index 2afb376299fe..d758f5c3e06e 100644 --- a/net/sctp/diag.c +++ b/net/sctp/diag.c @@ -266,15 +266,15 @@ static int sctp_sock_dump_one(struct sctp_endpoint *ep, struct sctp_transport *t lock_sock(sk); - rep = nlmsg_new(inet_assoc_attr_size(sk, assoc), GFP_KERNEL); - if (!rep) { - release_sock(sk); - return -ENOMEM; + if (ep != assoc->ep || assoc->base.dead) { + err = -ESTALE; + goto out_unlock; } - if (ep != assoc->ep) { - err = -EAGAIN; - goto out; + rep = nlmsg_new(inet_assoc_attr_size(sk, assoc), GFP_KERNEL); + if (!rep) { + err = -ENOMEM; + goto out_unlock; } err = inet_sctp_diag_fill(sk, assoc, rep, req, sk_user_ns(NETLINK_CB(skb).sk), @@ -289,8 +289,9 @@ static int sctp_sock_dump_one(struct sctp_endpoint *ep, struct sctp_transport *t return nlmsg_unicast(sock_net(skb->sk)->diag_nlsk, rep, NETLINK_CB(skb).portid); out: - release_sock(sk); kfree_skb(rep); +out_unlock: + release_sock(sk); return err; } From 02e545c4297a26dbbc41df81b831e7f605bcd306 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 1 Jun 2026 09:22:37 -1000 Subject: [PATCH 279/337] sched_ext: Don't warn on NULL cgrp_moving_from in scx_cgroup_move_task() A WARN fires when systemd's user manager writes "+cpu +memory +pids" to its own subtree_control while a sched_ext scheduler is loaded: WARNING: at kernel/sched/ext.c:3227 scx_cgroup_move_task+0xa8/0xb0 scx_cgroup_move_task+0xa8/0xb0 sched_move_task+0x134/0x290 cpu_cgroup_attach+0x39/0x70 cgroup_migrate_execute+0x37d/0x450 cgroup_update_dfl_csses+0x1e3/0x270 cgroup_subtree_control_write+0x3e7/0x440 scx_cgroup_can_attach() arms cgrp_moving_from only when a task's cpu cgroup changes. It can still be NULL when scx_cgroup_move_task() runs, through this sequence: Step Result --------------------------------- ---------------------------------- 1. cpu enabled on cgroup G cpu css = A 2. cpu toggled off then on for G A killed, B created (same cgroup) 3. an exiting task keeps A alive migration skips it, A now stale 4. +memory migrates G stale A vs current B pulls cpu in 5. cpu attach runs for all tasks hits a live, cpu-unchanged task 6. scx_cgroup_move_task() on it cgrp_moving_from NULL -> WARN The mismatch is that scx_cgroup_can_attach() keys on cgroup identity while migration drives the move on css identity, so a NULL cgrp_moving_from here is a legitimate css-only migration, not a missing prep. The call is already gated on cgrp_moving_from, so just drop the warning. ops.cgroup_prep_move() and ops.cgroup_move() stay paired. Fixes: 819513666966 ("sched_ext: Add cgroup support") Cc: stable@vger.kernel.org # v6.12+ Reported-by: Matt Fleming Closes: https://lore.kernel.org/all/20260601124156.2205704-1-mfleming@cloudflare.com/ Signed-off-by: Tejun Heo Reviewed-by: Andrea Righi --- kernel/sched/ext.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/kernel/sched/ext.c b/kernel/sched/ext.c index c1762420cc35..8e88a25bc602 100644 --- a/kernel/sched/ext.c +++ b/kernel/sched/ext.c @@ -4402,11 +4402,13 @@ void scx_cgroup_move_task(struct task_struct *p) return; /* - * @p must have ops.cgroup_prep_move() called on it and thus - * cgrp_moving_from set. + * scx_cgroup_can_attach() sets cgrp_moving_from only when the task's + * cgroup changes. Migration keys off css rather than cgroup identity, + * so it can hand an unchanged-cgroup task here with cgrp_moving_from + * NULL. Nothing to report to the BPF scheduler then, so skip it and + * keep prep_move and move paired. */ - if (SCX_HAS_OP(sch, cgroup_move) && - !WARN_ON_ONCE(!p->scx.cgrp_moving_from)) + if (SCX_HAS_OP(sch, cgroup_move) && p->scx.cgrp_moving_from) SCX_CALL_OP_TASK(sch, cgroup_move, task_rq(p), p, p->scx.cgrp_moving_from, tg_cgrp(task_group(p))); From 57aff991119693e09b414aff3267c0eae5e81da0 Mon Sep 17 00:00:00 2001 From: Ridong Chen Date: Tue, 2 Jun 2026 17:10:38 +0800 Subject: [PATCH 280/337] cgroup/cpuset: Change Ridong's email The chenridong@huaweicloud.com is no longer a valid email, replace it with the personal email ridong.chen@linux.dev Signed-off-by: Ridong Chen Acked-by: Waiman Long Signed-off-by: Tejun Heo --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 74c86cf9bc65..634eb67acd06 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6526,7 +6526,7 @@ F: include/linux/blk-cgroup.h CONTROL GROUP - CPUSET M: Waiman Long -R: Chen Ridong +R: Ridong Chen L: cgroups@vger.kernel.org S: Maintained T: git git://git.kernel.org/pub/scm/linux/kernel/git/tj/cgroup.git From f723ccaff2fb72b71ae8a9fd283f0dee4d9ae7a3 Mon Sep 17 00:00:00 2001 From: Jiayuan Chen Date: Fri, 29 May 2026 23:22:18 +0800 Subject: [PATCH 281/337] ipv6: anycast: insert aca into global hash under idev->lock syzbot reported a splat [1]: a slab-use-after-free in ipv6_chk_acast_addr(), which walks the global inet6_acaddr_lst[] hash under RCU and dereferences a struct ifacaddr6 that has already been freed while still linked in the hash, so a later reader walks into a dangling node. In __ipv6_dev_ac_inc() the aca is allocated with refcount 1, then aca_get() bumps it to 2 to keep it alive across the unlocked region. It is published to idev->ac_list under idev->lock, but ipv6_add_acaddr_hash() runs after write_unlock_bh(). A concurrent teardown (ipv6_ac_destroy_dev() from addrconf_ifdown(), under RTNL) can slip into that window: CPU0 __ipv6_dev_ac_inc CPU1 ipv6_ac_destroy_dev (RTNL) ------------------------------ ------------------------------------ aca_alloc() refcnt 1 aca_get() refcnt 2 write_lock_bh(idev->lock) add aca to ac_list write_unlock_bh(idev->lock) write_lock_bh(idev->lock) pull aca off ac_list write_unlock_bh(idev->lock) ipv6_del_acaddr_hash(aca) hlist_del_init_rcu() is a no-op, aca is not in the hash yet aca_put() refcnt 2->1 ipv6_add_acaddr_hash(aca) aca now inserted into the hash aca_put() refcnt 1->0 call_rcu(aca_free_rcu) -> kfree(aca) The hash removal becomes a no-op because the insertion has not happened yet, so once CPU0 inserts and drops the last reference, the aca is freed while still linked in inet6_acaddr_lst[], and readers dereference freed memory after the slab slot is reused. This window opened once RTNL stopped serializing the join path against device teardown. Move ipv6_add_acaddr_hash() inside the idev->lock section so the ac_list and hash insertions are atomic with respect to teardown: a racing remover now either misses the aca entirely or finds it in both lists. acaddr_hash_lock is now nested under idev->lock, which is acquired in softirq context, so switch all acaddr_hash_lock sites to spin_lock_bh() to avoid the irq lock inversion reported in [2]. [1] https://syzkaller.appspot.com/bug?extid=a01df04303c131efbf3a [2] https://lore.kernel.org/netdev/6a194ef7.ba3b1513.1890b4.0000.GAE@google.com/ Reported-by: syzbot+819eb928d120d2bdad0e@syzkaller.appspotmail.com Closes: https://lore.kernel.org/all/6a191f87.ce022c6e.138e56.0003.GAE@google.com/T/ Reviewed-by: Kuniyuki Iwashima Fixes: eb1ac9ff6c4a ("ipv6: anycast: Don't hold RTNL for IPV6_JOIN_ANYCAST.") Signed-off-by: Jiayuan Chen Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260529152219.235475-1-jiayuan.chen@linux.dev Signed-off-by: Jakub Kicinski --- net/ipv6/anycast.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/net/ipv6/anycast.c b/net/ipv6/anycast.c index 67a42e01dfc3..be6dac8a8566 100644 --- a/net/ipv6/anycast.c +++ b/net/ipv6/anycast.c @@ -243,16 +243,16 @@ static void ipv6_add_acaddr_hash(struct net *net, struct ifacaddr6 *aca) { unsigned int hash = inet6_acaddr_hash(net, &aca->aca_addr); - spin_lock(&acaddr_hash_lock); + spin_lock_bh(&acaddr_hash_lock); hlist_add_head_rcu(&aca->aca_addr_lst, &inet6_acaddr_lst[hash]); - spin_unlock(&acaddr_hash_lock); + spin_unlock_bh(&acaddr_hash_lock); } static void ipv6_del_acaddr_hash(struct ifacaddr6 *aca) { - spin_lock(&acaddr_hash_lock); + spin_lock_bh(&acaddr_hash_lock); hlist_del_init_rcu(&aca->aca_addr_lst); - spin_unlock(&acaddr_hash_lock); + spin_unlock_bh(&acaddr_hash_lock); } static void aca_get(struct ifacaddr6 *aca) @@ -371,10 +371,10 @@ int __ipv6_dev_ac_inc(struct inet6_dev *idev, const struct in6_addr *addr) aca->aca_next = idev->ac_list; rcu_assign_pointer(idev->ac_list, aca); - write_unlock_bh(&idev->lock); - ipv6_add_acaddr_hash(net, aca); + write_unlock_bh(&idev->lock); + ip6_ins_rt(net, f6i); addrconf_join_solict(idev->dev, &aca->aca_addr); @@ -649,8 +649,8 @@ void ipv6_anycast_cleanup(void) { int i; - spin_lock(&acaddr_hash_lock); + spin_lock_bh(&acaddr_hash_lock); for (i = 0; i < IN6_ADDR_HSIZE; i++) WARN_ON(!hlist_empty(&inet6_acaddr_lst[i])); - spin_unlock(&acaddr_hash_lock); + spin_unlock_bh(&acaddr_hash_lock); } From e8694f7cc29287e843648d1075177b9a2000d957 Mon Sep 17 00:00:00 2001 From: Fedor Pchelkin Date: Mon, 1 Jun 2026 12:41:56 +0300 Subject: [PATCH 282/337] wifi: fix leak if split 6 GHz scanning fails rdev->int_scan_req is leaked if cfg80211_scan() fails. Note that it's supposed to be released at ___cfg80211_scan_done() but this doesn't happen as rdev->scan_req is NULL at that point, too, leading to the early return from the freeing function. unreferenced object 0xffff8881161d0800 (size 512): comm "wpa_supplicant", pid 379, jiffies 4294749765 hex dump (first 32 bytes): 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00 00 00 00 00 00 00 00 f0 81 13 16 81 88 ff ff ................ backtrace (crc c867fdb6): kmemleak_alloc+0x89/0x90 __kmalloc_noprof+0x2fd/0x410 cfg80211_scan+0x133/0x730 nl80211_trigger_scan+0xc69/0x1cc0 genl_family_rcv_msg_doit+0x204/0x2f0 genl_rcv_msg+0x431/0x6b0 netlink_rcv_skb+0x143/0x3f0 genl_rcv+0x27/0x40 netlink_unicast+0x4f6/0x820 netlink_sendmsg+0x797/0xce0 __sock_sendmsg+0xc4/0x160 ____sys_sendmsg+0x5e4/0x890 ___sys_sendmsg+0xf8/0x180 __sys_sendmsg+0x136/0x1e0 __x64_sys_sendmsg+0x76/0xc0 x64_sys_call+0x13f0/0x17d0 Found by Linux Verification Center (linuxtesting.org). Fixes: c8cb5b854b40 ("nl80211/cfg80211: support 6 GHz scanning") Signed-off-by: Fedor Pchelkin Link: https://patch.msgid.link/20260601094157.92703-1-pchelkin@ispras.ru Signed-off-by: Johannes Berg --- net/wireless/scan.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 358cbc9e43d8..27a56ee2e8f0 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -1071,6 +1071,7 @@ int cfg80211_scan(struct cfg80211_registered_device *rdev) struct cfg80211_scan_request_int *request; struct cfg80211_scan_request_int *rdev_req = rdev->scan_req; u32 n_channels = 0, idx, i; + int err; if (!(rdev->wiphy.flags & WIPHY_FLAG_SPLIT_SCAN_6GHZ)) { rdev_req->req.first_part = true; @@ -1100,8 +1101,14 @@ int cfg80211_scan(struct cfg80211_registered_device *rdev) rdev_req->req.scan_6ghz = false; rdev_req->req.first_part = true; + err = rdev_scan(rdev, request); + if (err) { + kfree(request); + return err; + } + rdev->int_scan_req = request; - return rdev_scan(rdev, request); + return 0; } void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, From cb9959ab5f99611d27a06586add84811fe8102dc Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 3 Jun 2026 11:18:11 +0200 Subject: [PATCH 283/337] wifi: cfg80211: enforce HE/EHT cap/oper consistency Xiang Mei reports that mac80211 could crash if eht_cap is set but eht_oper isn't. Rather than fixing that for the individual user(s), enforce that both HE/EHT have consistent elements. Reported-by: Xiang Mei Fixes: 22c64f37e1d4 ("wifi: mac80211: Update MCS15 support in link_conf") Link: https://patch.msgid.link/20260603091812.101894-2-johannes@sipsolutions.net Signed-off-by: Johannes Berg --- net/wireless/nl80211.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index dac2e8643c49..76c537a6e8b5 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -6714,6 +6714,12 @@ static int nl80211_calculate_ap_params(struct cfg80211_ap_settings *params) return -EINVAL; } + if (!!params->he_cap != !!params->he_oper) + return -EINVAL; + + if (!!params->eht_cap != !!params->eht_oper) + return -EINVAL; + return 0; } From 43c441edacf953b39517a44f5e5e10a93618b226 Mon Sep 17 00:00:00 2001 From: Zhang Cen Date: Thu, 28 May 2026 15:56:41 +0800 Subject: [PATCH 284/337] Bluetooth: RFCOMM: hold listener socket in rfcomm_connect_ind() rfcomm_get_sock_by_channel() scans rfcomm_sk_list under the list lock, but returns the selected listener after dropping that lock without taking a reference. rfcomm_connect_ind() then locks the listener, queues a child socket on it, and may notify it after unlocking it. The buggy scenario involves two paths, with each column showing the order within that path: rfcomm_connect_ind(): listener close: 1. Find parent in 1. close() enters rfcomm_get_sock_by_channel() rfcomm_sock_release(). 2. Drop rfcomm_sk_list.lock 2. rfcomm_sock_shutdown() without pinning parent. closes the listener. 3. Call lock_sock(parent) and 3. rfcomm_sock_kill() bt_accept_enqueue(parent, unlinks and puts parent. sk, true). 4. Read parent flags and may 4. parent can be freed. call sk_state_change(). If close wins the race, parent can be freed before rfcomm_connect_ind() reaches lock_sock(), bt_accept_enqueue(), or the deferred-setup callback. Take a reference on the listener before leaving rfcomm_sk_list.lock. After lock_sock() succeeds, recheck that it is still in BT_LISTEN before queueing a child, cache the deferred-setup bit while the parent is locked, and drop the reference after the last parent use. KASAN reported a slab-use-after-free in lock_sock_nested() from rfcomm_connect_ind(), with the freeing stack going through rfcomm_sock_kill() and rfcomm_sock_release(). Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Zhang Cen Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/rfcomm/sock.c | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/net/bluetooth/rfcomm/sock.c b/net/bluetooth/rfcomm/sock.c index bd7d959c6e9e..805ed5d28ed6 100644 --- a/net/bluetooth/rfcomm/sock.c +++ b/net/bluetooth/rfcomm/sock.c @@ -122,7 +122,7 @@ static struct sock *__rfcomm_get_listen_sock_by_addr(u8 channel, bdaddr_t *src) } /* Find socket with channel and source bdaddr. - * Returns closest match. + * Returns closest match with an extra reference held. */ static struct sock *rfcomm_get_sock_by_channel(int state, u8 channel, bdaddr_t *src) { @@ -136,15 +136,25 @@ static struct sock *rfcomm_get_sock_by_channel(int state, u8 channel, bdaddr_t * if (rfcomm_pi(sk)->channel == channel) { /* Exact match. */ - if (!bacmp(&rfcomm_pi(sk)->src, src)) + if (!bacmp(&rfcomm_pi(sk)->src, src)) { + sock_hold(sk); break; + } /* Closest match */ - if (!bacmp(&rfcomm_pi(sk)->src, BDADDR_ANY)) + if (!bacmp(&rfcomm_pi(sk)->src, BDADDR_ANY)) { + if (sk1) + sock_put(sk1); + sk1 = sk; + sock_hold(sk1); + } } } + if (sk && sk1) + sock_put(sk1); + read_unlock(&rfcomm_sk_list.lock); return sk ? sk : sk1; @@ -941,6 +951,7 @@ int rfcomm_connect_ind(struct rfcomm_session *s, u8 channel, struct rfcomm_dlc * { struct sock *sk, *parent; bdaddr_t src, dst; + bool defer_setup = false; int result = 0; BT_DBG("session %p channel %d", s, channel); @@ -954,6 +965,11 @@ int rfcomm_connect_ind(struct rfcomm_session *s, u8 channel, struct rfcomm_dlc * lock_sock(parent); + if (parent->sk_state != BT_LISTEN) + goto done; + + defer_setup = test_bit(BT_SK_DEFER_SETUP, &bt_sk(parent)->flags); + /* Check for backlog size */ if (sk_acceptq_is_full(parent)) { BT_DBG("backlog full %d", parent->sk_ack_backlog); @@ -981,9 +997,11 @@ int rfcomm_connect_ind(struct rfcomm_session *s, u8 channel, struct rfcomm_dlc * done: release_sock(parent); - if (test_bit(BT_SK_DEFER_SETUP, &bt_sk(parent)->flags)) + if (defer_setup) parent->sk_state_change(parent); + sock_put(parent); + return result; } From de23fb62259aa01d294f77238ae3b835eb674413 Mon Sep 17 00:00:00 2001 From: Zhang Cen Date: Thu, 28 May 2026 17:45:06 +0800 Subject: [PATCH 285/337] Bluetooth: MGMT: validate advertising TLV before type checks tlv_data_is_valid() reads each advertising data field length from data[i], then inspects data[i + 1] for managed EIR types before checking that the current field still fits inside the supplied buffer. A malformed field whose length byte is the last byte of the buffer can therefore make the parser read one byte past the advertising data. KASAN reported the following when a malformed MGMT_OP_ADD_ADVERTISING request reached that path: BUG: KASAN: vmalloc-out-of-bounds in tlv_data_is_valid() Read of size 1 Call trace: tlv_data_is_valid() add_advertising() hci_mgmt_cmd() hci_sock_sendmsg() Move the existing element-length check before any type-octet inspection so each non-empty element is proven to contain its type byte before the parser looks at data[i + 1]. Fixes: 2bb36870e8cb ("Bluetooth: Unify advertising instance flags check") Reviewed-by: Paul Menzel Signed-off-by: Zhang Cen Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/mgmt.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index de5bd6b637b2..027b266ccc74 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -8638,6 +8638,12 @@ static bool tlv_data_is_valid(struct hci_dev *hdev, u32 adv_flags, u8 *data, if (!cur_len) continue; + /* If the current field length would exceed the total data + * length, then it's invalid. + */ + if (i + cur_len >= len) + return false; + if (data[i + 1] == EIR_FLAGS && (!is_adv_data || flags_managed(adv_flags))) return false; @@ -8654,12 +8660,6 @@ static bool tlv_data_is_valid(struct hci_dev *hdev, u32 adv_flags, u8 *data, if (data[i + 1] == EIR_APPEARANCE && appearance_managed(adv_flags)) return false; - - /* If the current field length would exceed the total data - * length, then it's invalid. - */ - if (i + cur_len >= len) - return false; } return true; From 23882b828c3c8c51d0c946446a396b10abb3b16b Mon Sep 17 00:00:00 2001 From: SeungJu Cheon Date: Mon, 25 May 2026 20:04:43 +0900 Subject: [PATCH 286/337] Bluetooth: RFCOMM: validate skb length in MCC handlers The RFCOMM MCC handlers cast skb->data to protocol-specific structs without validating skb->len first. A malicious remote device can send truncated MCC frames and trigger out-of-bounds reads in these handlers. Fix this by using skb_pull_data() to validate and access the required data before dereferencing it. rfcomm_recv_rpn() requires special handling since ETSI TS 07.10 allows 1-byte RPN requests. Handle this by validating only the DLCI byte first, and validating the full struct only when len > 1. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Suggested-by: Muhammad Bilal Signed-off-by: SeungJu Cheon Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/rfcomm/core.c | 69 +++++++++++++++++++++++++++---------- 1 file changed, 50 insertions(+), 19 deletions(-) diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c index d11bd5337d57..364b9381c2dc 100644 --- a/net/bluetooth/rfcomm/core.c +++ b/net/bluetooth/rfcomm/core.c @@ -1431,10 +1431,15 @@ static int rfcomm_apply_pn(struct rfcomm_dlc *d, int cr, struct rfcomm_pn *pn) static int rfcomm_recv_pn(struct rfcomm_session *s, int cr, struct sk_buff *skb) { - struct rfcomm_pn *pn = (void *) skb->data; + struct rfcomm_pn *pn; struct rfcomm_dlc *d; - u8 dlci = pn->dlci; + u8 dlci; + pn = skb_pull_data(skb, sizeof(*pn)); + if (!pn) + return -EILSEQ; + + dlci = pn->dlci; BT_DBG("session %p state %ld dlci %d", s, s->state, dlci); if (!dlci) @@ -1483,8 +1488,8 @@ static int rfcomm_recv_pn(struct rfcomm_session *s, int cr, struct sk_buff *skb) static int rfcomm_recv_rpn(struct rfcomm_session *s, int cr, int len, struct sk_buff *skb) { - struct rfcomm_rpn *rpn = (void *) skb->data; - u8 dlci = __get_dlci(rpn->dlci); + struct rfcomm_rpn *rpn; + u8 dlci; u8 bit_rate = 0; u8 data_bits = 0; @@ -1495,15 +1500,16 @@ static int rfcomm_recv_rpn(struct rfcomm_session *s, int cr, int len, struct sk_ u8 xoff_char = 0; u16 rpn_mask = RFCOMM_RPN_PM_ALL; - BT_DBG("dlci %d cr %d len 0x%x bitr 0x%x line 0x%x flow 0x%x xonc 0x%x xoffc 0x%x pm 0x%x", - dlci, cr, len, rpn->bit_rate, rpn->line_settings, rpn->flow_ctrl, - rpn->xon_char, rpn->xoff_char, rpn->param_mask); - - if (!cr) - return 0; - if (len == 1) { - /* This is a request, return default (according to ETSI TS 07.10) settings */ + rpn = skb_pull_data(skb, 1); + if (!rpn) + return -EILSEQ; + + dlci = __get_dlci(rpn->dlci); + + if (!cr) + return 0; + bit_rate = RFCOMM_RPN_BR_9600; data_bits = RFCOMM_RPN_DATA_8; stop_bits = RFCOMM_RPN_STOP_1; @@ -1514,6 +1520,19 @@ static int rfcomm_recv_rpn(struct rfcomm_session *s, int cr, int len, struct sk_ goto rpn_out; } + rpn = skb_pull_data(skb, sizeof(*rpn)); + if (!rpn) + return -EILSEQ; + + dlci = __get_dlci(rpn->dlci); + + BT_DBG("dlci %d cr %d len 0x%x bitr 0x%x line 0x%x flow 0x%x xonc 0x%x xoffc 0x%x pm 0x%x", + dlci, cr, len, rpn->bit_rate, rpn->line_settings, rpn->flow_ctrl, + rpn->xon_char, rpn->xoff_char, rpn->param_mask); + + if (!cr) + return 0; + /* Check for sane values, ignore/accept bit_rate, 8 bits, 1 stop bit, * no parity, no flow control lines, normal XON/XOFF chars */ @@ -1589,9 +1608,14 @@ static int rfcomm_recv_rpn(struct rfcomm_session *s, int cr, int len, struct sk_ static int rfcomm_recv_rls(struct rfcomm_session *s, int cr, struct sk_buff *skb) { - struct rfcomm_rls *rls = (void *) skb->data; - u8 dlci = __get_dlci(rls->dlci); + struct rfcomm_rls *rls; + u8 dlci; + rls = skb_pull_data(skb, sizeof(*rls)); + if (!rls) + return -EILSEQ; + + dlci = __get_dlci(rls->dlci); BT_DBG("dlci %d cr %d status 0x%x", dlci, cr, rls->status); if (!cr) @@ -1608,10 +1632,15 @@ static int rfcomm_recv_rls(struct rfcomm_session *s, int cr, struct sk_buff *skb static int rfcomm_recv_msc(struct rfcomm_session *s, int cr, struct sk_buff *skb) { - struct rfcomm_msc *msc = (void *) skb->data; + struct rfcomm_msc *msc; struct rfcomm_dlc *d; - u8 dlci = __get_dlci(msc->dlci); + u8 dlci; + msc = skb_pull_data(skb, sizeof(*msc)); + if (!msc) + return -EILSEQ; + + dlci = __get_dlci(msc->dlci); BT_DBG("dlci %d cr %d v24 0x%x", dlci, cr, msc->v24_sig); d = rfcomm_dlc_get(s, dlci); @@ -1644,17 +1673,19 @@ static int rfcomm_recv_msc(struct rfcomm_session *s, int cr, struct sk_buff *skb static int rfcomm_recv_mcc(struct rfcomm_session *s, struct sk_buff *skb) { - struct rfcomm_mcc *mcc = (void *) skb->data; + struct rfcomm_mcc *mcc; u8 type, cr, len; + mcc = skb_pull_data(skb, sizeof(*mcc)); + if (!mcc) + return -EILSEQ; + cr = __test_cr(mcc->type); type = __get_mcc_type(mcc->type); len = __get_mcc_len(mcc->len); BT_DBG("%p type 0x%x cr %d", s, type, cr); - skb_pull(skb, 2); - switch (type) { case RFCOMM_PN: rfcomm_recv_pn(s, cr, skb); From dd214733544427587a95f66dbf3adff072568990 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Thu, 21 May 2026 10:45:17 -0400 Subject: [PATCH 287/337] Bluetooth: L2CAP: reject BR/EDR signaling packets over MTUsig net/bluetooth/l2cap_core.c:l2cap_sig_channel() accepts BR/EDR signaling packets up to the channel MTU and dispatches each command without enforcing the signaling MTU (MTUsig). A Bluetooth BR/EDR peer within radio range can send a fixed-channel CID 0x0001 packet that is larger than MTUsig and contains many L2CAP_ECHO_REQ commands before pairing. In a real-radio stock-kernel run, one 681-byte signaling packet containing 168 zero-length ECHO_REQ commands made the target transmit 168 ECHO_RSP frames over about 220 ms. Impact: a Bluetooth BR/EDR peer within radio range, before pairing, can force 168 ECHO_RSP frames from one 681-byte fixed-channel signaling packet containing packed ECHO_REQ commands. Define Linux's BR/EDR signaling MTU as the spec minimum of 48 bytes and reject any larger signaling packet with one L2CAP_COMMAND_REJECT_RSP carrying L2CAP_REJ_MTU_EXCEEDED before any command is dispatched. The Bluetooth Core spec wording for MTUExceeded says the reject identifier shall match the first request command in the packet, and that packets containing only responses shall be silently discarded. Linux intentionally deviates from that prescription: silently discarding desynchronizes the peer because the remote stack never learns its responses were dropped, and locating the first request command requires walking command headers past MTUsig, i.e. processing bytes from a packet we have already decided is too large to process. We therefore always emit one reject and use the identifier from the first command header, a single fixed-offset byte read. The unrestricted BR/EDR signaling parser and ECHO_REQ response path both trace to the initial git import; no later introducing commit is available for a Fixes tag. Cc: stable@vger.kernel.org Suggested-by: Luiz Augusto von Dentz Link: https://lore.kernel.org/r/20260518002800.1361430-1-michael.bommarito@gmail.com Link: https://lore.kernel.org/r/20260520135034.1060859-1-michael.bommarito@gmail.com Link: https://lore.kernel.org/r/20260521000555.3712030-1-michael.bommarito@gmail.com Assisted-by: Claude:claude-opus-4-7 Assisted-by: Codex:gpt-5-5-xhigh Signed-off-by: Michael Bommarito Signed-off-by: Luiz Augusto von Dentz --- include/net/bluetooth/l2cap.h | 1 + net/bluetooth/l2cap_core.c | 46 +++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 5172afee5494..e0a1f2293679 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -33,6 +33,7 @@ /* L2CAP defaults */ #define L2CAP_DEFAULT_MTU 672 #define L2CAP_DEFAULT_MIN_MTU 48 +#define L2CAP_SIG_MTU 48 /* BR/EDR signaling MTU */ #define L2CAP_DEFAULT_FLUSH_TO 0xFFFF #define L2CAP_EFS_DEFAULT_FLUSH_TO 0xFFFFFFFF #define L2CAP_DEFAULT_TX_WINDOW 63 diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 45b175399e8d..c4ccfbda9d78 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -5643,6 +5643,15 @@ static inline void l2cap_sig_send_rej(struct l2cap_conn *conn, u16 ident) l2cap_send_cmd(conn, ident, L2CAP_COMMAND_REJ, sizeof(rej), &rej); } +static inline void l2cap_sig_send_mtu_rej(struct l2cap_conn *conn, u8 ident) +{ + struct l2cap_cmd_rej_mtu rej; + + rej.reason = cpu_to_le16(L2CAP_REJ_MTU_EXCEEDED); + rej.max_mtu = cpu_to_le16(L2CAP_SIG_MTU); + l2cap_send_cmd(conn, ident, L2CAP_COMMAND_REJ, sizeof(rej), &rej); +} + static inline void l2cap_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb) { @@ -5655,6 +5664,43 @@ static inline void l2cap_sig_channel(struct l2cap_conn *conn, if (hcon->type != ACL_LINK) goto drop; + /* + * Bluetooth Core v5.4, Vol 3, Part A, Section 4: the BR/EDR + * signaling channel has a fixed signaling MTU (MTUsig) whose + * minimum and default is 48 octets. Section 4.1 says that on + * an MTUExceeded command reject the identifier "shall match + * the first request command in the L2CAP packet" and that + * packets containing only response commands "shall be + * silently discarded". + * + * Linux intentionally deviates from that prescription: + * + * 1. Silently discarding desynchronizes the peer. The + * remote stack never learns its responses were dropped, + * so any state machine waiting on a paired response + * stalls until its own timer fires. + * + * 2. Locating "the first request command" requires walking + * command headers past MTUsig, i.e. processing bytes + * from a packet we have already decided is too large to + * process. + * + * Reject every over-MTUsig signaling packet with one + * L2CAP_REJ_MTU_EXCEEDED command reject. The reject's + * reason field is what tells the peer that the whole packet + * was discarded; the identifier value is informational, so + * we use the identifier from the first command header, a + * single fixed-offset byte read. + */ + if (skb->len > L2CAP_SIG_MTU) { + u8 ident = skb->data[1]; + + BT_DBG("signaling packet exceeds MTU: %u > %u", + skb->len, L2CAP_SIG_MTU); + l2cap_sig_send_mtu_rej(conn, ident); + goto drop; + } + while (skb->len >= L2CAP_CMD_HDR_SIZE) { u16 len; From 5c65b96b549ea2dcfde497436bf9e048deb87758 Mon Sep 17 00:00:00 2001 From: Yuqi Xu Date: Fri, 29 May 2026 16:54:23 +0800 Subject: [PATCH 288/337] Bluetooth: hci_sync: reject oversized Broadcast Announcement prepend Existing advertising instances can already hold the maximum extended advertising payload. When hci_adv_bcast_annoucement() prepends the Broadcast Announcement service data to that payload, the combined data may no longer fit in the temporary buffer used to rebuild the advertising data. Reject that case before copying the existing payload and report the failure through the device log. This keeps the existing advertising data intact and avoids overrunning the temporary buffer. Fixes: 5725bc608252 ("Bluetooth: hci_sync: Fix broadcast/PA when using an existing instance") Cc: stable@kernel.org Reported-by: Yuan Tan Reported-by: Zhengchuan Liang Reported-by: Xin Liu Assisted-by: Codex:GPT-5.4 Signed-off-by: Yuqi Xu Signed-off-by: Ren Wei Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/hci_sync.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c index aeccd8084cba..df23245d6ccd 100644 --- a/net/bluetooth/hci_sync.c +++ b/net/bluetooth/hci_sync.c @@ -1725,6 +1725,11 @@ static int hci_adv_bcast_annoucement(struct hci_dev *hdev, struct adv_info *adv) /* Generate Broadcast ID */ get_random_bytes(bid, sizeof(bid)); len = eir_append_service_data(ad, 0, 0x1852, bid, sizeof(bid)); + if (adv->adv_data_len > sizeof(ad) - len) { + bt_dev_err(hdev, "No room for Broadcast Announcement"); + return -EINVAL; + } + memcpy(ad + len, adv->adv_data, adv->adv_data_len); hci_set_adv_instance_data(hdev, adv->instance, len + adv->adv_data_len, ad, 0, NULL); From 6770d3a8acdf9151769180cc3710346c4cfbe6f0 Mon Sep 17 00:00:00 2001 From: Zhang Cen Date: Fri, 29 May 2026 11:22:09 +0800 Subject: [PATCH 289/337] Bluetooth: bnep: reject short frames before parsing A BNEP peer can send a short BNEP SDU. bnep_rx_frame() reads the packet type byte immediately and, for control packets, reads the control opcode and setup UUID-size byte before proving that those bytes are present. bnep_rx_control() also dereferences the control opcode without rejecting an empty control payload. Use skb_pull_data() for the fixed fields in bnep_rx_frame() so a NULL return gates each dereference. Split the control handler so the frame path can pass an opcode that has already been pulled, and keep the byte-buffer wrapper for extension control payloads. For BNEP_SETUP_CONN_REQ, name the UUID-size byte before pulling the setup payload. struct bnep_setup_conn_req carries destination and source service UUIDs after that byte, each uuid_size bytes, so the parser now documents that tuple explicitly instead of leaving the pull length as an opaque multiplication. Validation reproduced this kernel report: KASAN slab-out-of-bounds in bnep_rx_frame.isra.0+0x130c/0x1790 The buggy address belongs to the object at ffff88800c0f7908 which belongs to the cache kmalloc-8 of size 8 The buggy address is located 0 bytes to the right of allocated 1-byte region [ffff88800c0f7908, ffff88800c0f7909) Read of size 1 Call trace: dump_stack_lvl+0xb3/0x140 (?:?) print_address_description+0x57/0x3a0 (?:?) bnep_rx_frame+0x130c/0x1790 (net/bluetooth/bnep/core.c:306) print_report+0xb9/0x2b0 (?:?) __virt_addr_valid+0x1ba/0x3a0 (?:?) srso_alias_return_thunk+0x5/0xfbef5 (?:?) kasan_addr_to_slab+0x21/0x60 (?:?) kasan_report+0xe0/0x110 (?:?) process_one_work+0xfce/0x17e0 (kernel/workqueue.c:3200) worker_thread+0x65c/0xe40 (?:?) __kthread_parkme+0x184/0x230 (?:?) kthread+0x35e/0x470 (?:?) _raw_spin_unlock_irq+0x28/0x50 (?:?) ret_from_fork+0x586/0x870 (?:?) __switch_to+0x74f/0xdc0 (?:?) ret_from_fork_asm+0x1a/0x30 (?:?) Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Assisted-by: Codex:gpt-5.5 Signed-off-by: Zhang Cen Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/bnep/core.c | 59 ++++++++++++++++++++++++--------------- 1 file changed, 37 insertions(+), 22 deletions(-) diff --git a/net/bluetooth/bnep/core.c b/net/bluetooth/bnep/core.c index 0de5df690bd0..5c5f53ff30e8 100644 --- a/net/bluetooth/bnep/core.c +++ b/net/bluetooth/bnep/core.c @@ -206,14 +206,11 @@ static int bnep_ctrl_set_mcfilter(struct bnep_session *s, u8 *data, int len) return 0; } -static int bnep_rx_control(struct bnep_session *s, void *data, int len) +static int bnep_rx_control_cmd(struct bnep_session *s, u8 cmd, void *data, + int len) { - u8 cmd = *(u8 *)data; int err = 0; - data++; - len--; - switch (cmd) { case BNEP_CMD_NOT_UNDERSTOOD: case BNEP_SETUP_CONN_RSP: @@ -254,6 +251,14 @@ static int bnep_rx_control(struct bnep_session *s, void *data, int len) return err; } +static int bnep_rx_control(struct bnep_session *s, void *data, int len) +{ + if (len < 1) + return -EILSEQ; + + return bnep_rx_control_cmd(s, *(u8 *)data, data + 1, len - 1); +} + static int bnep_rx_extension(struct bnep_session *s, struct sk_buff *skb) { struct bnep_ext_hdr *h; @@ -299,19 +304,26 @@ static int bnep_rx_frame(struct bnep_session *s, struct sk_buff *skb) { struct net_device *dev = s->dev; struct sk_buff *nskb; + u8 *data; u8 type, ctrl_type; dev->stats.rx_bytes += skb->len; - type = *(u8 *) skb->data; - skb_pull(skb, 1); - ctrl_type = *(u8 *)skb->data; + data = skb_pull_data(skb, sizeof(type)); + if (!data) + goto badframe; + type = *data; if ((type & BNEP_TYPE_MASK) >= sizeof(__bnep_rx_hlen)) goto badframe; if ((type & BNEP_TYPE_MASK) == BNEP_CONTROL) { - if (bnep_rx_control(s, skb->data, skb->len) < 0) { + data = skb_pull_data(skb, sizeof(ctrl_type)); + if (!data) + goto badframe; + ctrl_type = *data; + + if (bnep_rx_control_cmd(s, ctrl_type, skb->data, skb->len) < 0) { dev->stats.tx_errors++; kfree_skb(skb); return 0; @@ -324,24 +336,27 @@ static int bnep_rx_frame(struct bnep_session *s, struct sk_buff *skb) /* Verify and pull ctrl message since it's already processed */ switch (ctrl_type) { - case BNEP_SETUP_CONN_REQ: - /* Pull: ctrl type (1 b), len (1 b), data (len bytes) */ - if (!skb_pull(skb, 2 + *(u8 *)(skb->data + 1) * 2)) - goto badframe; - break; - case BNEP_FILTER_MULTI_ADDR_SET: - case BNEP_FILTER_NET_TYPE_SET: { - u8 *hdr; + case BNEP_SETUP_CONN_REQ: { + u8 uuid_size; - /* Pull ctrl type (1 b) + len (2 b) */ - hdr = skb_pull_data(skb, 3); - if (!hdr) + /* Pull uuid_size and the dst/src service UUIDs. */ + data = skb_pull_data(skb, sizeof(uuid_size)); + if (!data) goto badframe; - /* Pull data (len bytes); length is big-endian */ - if (!skb_pull(skb, get_unaligned_be16(&hdr[1]))) + uuid_size = *data; + if (!skb_pull(skb, uuid_size + uuid_size)) goto badframe; break; } + case BNEP_FILTER_MULTI_ADDR_SET: + case BNEP_FILTER_NET_TYPE_SET: + /* Pull: len (2 b), data (len bytes) */ + data = skb_pull_data(skb, sizeof(u16)); + if (!data) + goto badframe; + if (!skb_pull(skb, get_unaligned_be16(data))) + goto badframe; + break; default: kfree_skb(skb); return 0; From 37b3009bf5976e8ab77c8b9a9bc3bbd7ff49e37f Mon Sep 17 00:00:00 2001 From: Bharath Reddy Date: Mon, 1 Jun 2026 08:54:26 +0530 Subject: [PATCH 290/337] Bluetooth: fix memory leak in error path of hci_alloc_dev() Early failures in Bluetooth HCI UART configuration leak SRCU percpu memory. When device initialization fails before hci_register_dev() completes, the HCI_UNREGISTER flag is never set. As a result, when the device reference count reaches zero, bt_host_release() evaluates this flag as false and falls back to a direct kfree(hdev). Because hci_release_dev() is bypassed, the SRCU struct initialized early in hci_alloc_dev() is never cleaned up, resulting in a leak of percpu memory. Fix the leak by explicitly calling cleanup_srcu_struct() in the fallback (unregistered) branch of bt_host_release() before freeing the device. Reported-by: syzbot+535ecc844591e50588a5@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=535ecc844591e50588a5 Tested-by: syzbot+535ecc844591e50588a5@syzkaller.appspotmail.com Fixes: 1d6123102e9f ("Bluetooth: hci_core: Fix use-after-free in vhci_flush()") Signed-off-by: Bharath Reddy Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/hci_sysfs.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/net/bluetooth/hci_sysfs.c b/net/bluetooth/hci_sysfs.c index 041ce9adc378..8957ce7c21b7 100644 --- a/net/bluetooth/hci_sysfs.c +++ b/net/bluetooth/hci_sysfs.c @@ -83,10 +83,12 @@ static void bt_host_release(struct device *dev) { struct hci_dev *hdev = to_hci_dev(dev); - if (hci_dev_test_flag(hdev, HCI_UNREGISTER)) + if (hci_dev_test_flag(hdev, HCI_UNREGISTER)) { hci_release_dev(hdev); - else + } else { + cleanup_srcu_struct(&hdev->srcu); kfree(hdev); + } module_put(THIS_MODULE); } From 5cbf290b79351971f20c7a533247e8d58a3f970c Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Mon, 1 Jun 2026 14:45:42 -0400 Subject: [PATCH 291/337] Bluetooth: ISO: Fix not releasing hdev reference on iso_conn_big_sync hci_get_route() returns a reference-counted hci_dev pointer via hci_dev_hold(). The function exits normally or with an error without ever releasing it. Fixes: 07a9342b94a9 ("Bluetooth: ISO: Send BIG Create Sync via hci_sync") Reported-by: Sashiko Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/iso.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c index 876649556d3c..3d707c43bdc0 100644 --- a/net/bluetooth/iso.c +++ b/net/bluetooth/iso.c @@ -1595,6 +1595,7 @@ static void iso_conn_big_sync(struct sock *sk) release_sock(sk); hci_dev_unlock(hdev); + hci_dev_put(hdev); } static int iso_sock_recvmsg(struct socket *sock, struct msghdr *msg, From f50331f2a1441ec49988832c3a95f2edacc47322 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Mon, 1 Jun 2026 14:52:09 -0400 Subject: [PATCH 292/337] Bluetooth: ISO: Fix a use-after-free of the hci_conn pointer In iso_sock_rebind_bc(), the bis pointer is cached, then the socket lock is dropped: bis = iso_pi(sk)->conn->hcon; /* Release the socket before lookups since that requires hci_dev_lock * which shall not be acquired while holding sock_lock for proper * ordering. */ release_sock(sk); hci_dev_lock(bis->hdev); During the unlocked window, could a concurrent close() destroy the connection and free the bis structure, causing hci_dev_lock(bis->hdev) to access memory after it is freed, fix this by using the hdev reference which was safely acquired via iso_conn_get_hdev(). Fixes: d3413703d5f8 ("Bluetooth: ISO: Add support to bind to trigger PAST") Reported-by: Sashiko Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/iso.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c index 3d707c43bdc0..c21ed2bb3cf8 100644 --- a/net/bluetooth/iso.c +++ b/net/bluetooth/iso.c @@ -1082,7 +1082,7 @@ static int iso_sock_rebind_bc(struct sock *sk, struct sockaddr_iso *sa, * ordering. */ release_sock(sk); - hci_dev_lock(bis->hdev); + hci_dev_lock(hdev); lock_sock(sk); if (!iso_pi(sk)->conn || iso_pi(sk)->conn->hcon != bis) { From 9ca7053d6215d89c33f28893bfd1625a32919d3f Mon Sep 17 00:00:00 2001 From: SeungJu Cheon Date: Mon, 1 Jun 2026 20:19:07 +0900 Subject: [PATCH 293/337] Bluetooth: ISO: Fix data-race on iso_pi fields in hci_get_route calls iso_connect_bis(), iso_connect_cis(), iso_listen_bis(), and iso_conn_big_sync() call hci_get_route() using iso_pi(sk)->dst, iso_pi(sk)->src, and iso_pi(sk)->src_type without holding lock_sock(). These fields may be modified concurrently by connect() or setsockopt() on the same socket, resulting in data-races reported by KCSAN. Fix this by snapshotting the required fields under lock_sock() before calling hci_get_route(). BUG: KCSAN: data-race in memcmp+0x45/0xb0 race at unknown origin, with read to 0xffff8880122135cf of 1 bytes by task 333 on cpu 1: memcmp+0x45/0xb0 hci_get_route+0x27e/0x490 iso_connect_cis+0x4c/0xa10 iso_sock_connect+0x60e/0xb30 __sys_connect_file+0xbd/0xe0 __sys_connect+0xe0/0x110 __x64_sys_connect+0x40/0x50 x64_sys_call+0xcad/0x1c60 do_syscall_64+0x133/0x590 entry_SYSCALL_64_after_hwframe+0x77/0x7f Fixes: 241f51931c35 ("Bluetooth: ISO: Avoid circular locking dependency") Signed-off-by: SeungJu Cheon Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/iso.c | 60 +++++++++++++++++++++++++++++++++------------ 1 file changed, 44 insertions(+), 16 deletions(-) diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c index c21ed2bb3cf8..3abd8111dda8 100644 --- a/net/bluetooth/iso.c +++ b/net/bluetooth/iso.c @@ -337,12 +337,20 @@ static int iso_connect_bis(struct sock *sk) struct iso_conn *conn; struct hci_conn *hcon; struct hci_dev *hdev; + bdaddr_t src, dst; + u8 src_type, bc_sid; int err; - BT_DBG("%pMR (SID 0x%2.2x)", &iso_pi(sk)->src, iso_pi(sk)->bc_sid); + lock_sock(sk); + bacpy(&src, &iso_pi(sk)->src); + bacpy(&dst, &iso_pi(sk)->dst); + src_type = iso_pi(sk)->src_type; + bc_sid = iso_pi(sk)->bc_sid; + release_sock(sk); - hdev = hci_get_route(&iso_pi(sk)->dst, &iso_pi(sk)->src, - iso_pi(sk)->src_type); + BT_DBG("%pMR (SID 0x%2.2x)", &src, bc_sid); + + hdev = hci_get_route(&dst, &src, src_type); if (!hdev) return -EHOSTUNREACH; @@ -430,12 +438,19 @@ static int iso_connect_cis(struct sock *sk) struct iso_conn *conn; struct hci_conn *hcon; struct hci_dev *hdev; + bdaddr_t src, dst; + u8 src_type; int err; - BT_DBG("%pMR -> %pMR", &iso_pi(sk)->src, &iso_pi(sk)->dst); + lock_sock(sk); + bacpy(&src, &iso_pi(sk)->src); + bacpy(&dst, &iso_pi(sk)->dst); + src_type = iso_pi(sk)->src_type; + release_sock(sk); - hdev = hci_get_route(&iso_pi(sk)->dst, &iso_pi(sk)->src, - iso_pi(sk)->src_type); + BT_DBG("%pMR -> %pMR", &src, &dst); + + hdev = hci_get_route(&dst, &src, src_type); if (!hdev) return -EHOSTUNREACH; @@ -1212,18 +1227,25 @@ static int iso_sock_connect(struct socket *sock, struct sockaddr_unsized *addr, static int iso_listen_bis(struct sock *sk) { - struct hci_dev *hdev; - int err = 0; struct iso_conn *conn; struct hci_conn *hcon; + struct hci_dev *hdev; + bdaddr_t src, dst; + u8 src_type, bc_sid; + int err = 0; - BT_DBG("%pMR -> %pMR (SID 0x%2.2x)", &iso_pi(sk)->src, - &iso_pi(sk)->dst, iso_pi(sk)->bc_sid); + lock_sock(sk); + bacpy(&src, &iso_pi(sk)->src); + bacpy(&dst, &iso_pi(sk)->dst); + src_type = iso_pi(sk)->src_type; + bc_sid = iso_pi(sk)->bc_sid; + release_sock(sk); + + BT_DBG("%pMR -> %pMR (SID 0x%2.2x)", &src, &dst, bc_sid); write_lock(&iso_sk_list.lock); - if (__iso_get_sock_listen_by_sid(&iso_pi(sk)->src, &iso_pi(sk)->dst, - iso_pi(sk)->bc_sid)) + if (__iso_get_sock_listen_by_sid(&src, &dst, bc_sid)) err = -EADDRINUSE; write_unlock(&iso_sk_list.lock); @@ -1231,8 +1253,7 @@ static int iso_listen_bis(struct sock *sk) if (err) return err; - hdev = hci_get_route(&iso_pi(sk)->dst, &iso_pi(sk)->src, - iso_pi(sk)->src_type); + hdev = hci_get_route(&dst, &src, src_type); if (!hdev) return -EHOSTUNREACH; @@ -1568,9 +1589,16 @@ static void iso_conn_big_sync(struct sock *sk) { int err; struct hci_dev *hdev; + bdaddr_t src, dst; + u8 src_type; - hdev = hci_get_route(&iso_pi(sk)->dst, &iso_pi(sk)->src, - iso_pi(sk)->src_type); + lock_sock(sk); + bacpy(&src, &iso_pi(sk)->src); + bacpy(&dst, &iso_pi(sk)->dst); + src_type = iso_pi(sk)->src_type; + release_sock(sk); + + hdev = hci_get_route(&dst, &src, src_type); if (!hdev) return; From 4847c5bca22227100ae69e96af86618b6fd2671f Mon Sep 17 00:00:00 2001 From: SeungJu Cheon Date: Mon, 1 Jun 2026 20:19:08 +0900 Subject: [PATCH 294/337] Bluetooth: SCO: Fix data-race on sco_pi fields in sco_connect sco_sock_connect() copies the destination address into sco_pi(sk)->dst under lock_sock(), then releases the lock and calls sco_connect(), which reads dst, src, setting, and codec without holding lock_sock() in hci_get_route() and hci_connect_sco(). These fields may be modified concurrently by connect(), bind(), or setsockopt() on the same socket, resulting in data-races reported by KCSAN. Fix this by snapshotting dst, src, setting, and codec under lock_sock() at the start of sco_connect() before passing them to hci_get_route() and hci_connect_sco(). BUG: KCSAN: data-race in memcmp+0x45/0xb0 race at unknown origin, with read to 0xffff88800e6b0dd0 of 1 bytes by task 315 on cpu 0: memcmp+0x45/0xb0 hci_connect_acl+0x1b7/0x6b0 hci_connect_sco+0x4d/0xb30 sco_sock_connect+0x27b/0xd60 __sys_connect_file+0xbd/0xe0 __sys_connect+0xe0/0x110 __x64_sys_connect+0x40/0x50 x64_sys_call+0xcad/0x1c60 do_syscall_64+0x133/0x590 entry_SYSCALL_64_after_hwframe+0x77/0x7f Fixes: 9a8ec9e8ebb5 ("Bluetooth: SCO: Fix possible circular locking dependency on sco_connect_cfm") Signed-off-by: SeungJu Cheon Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/sco.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c index f1799c6a6f87..140869e5b2df 100644 --- a/net/bluetooth/sco.c +++ b/net/bluetooth/sco.c @@ -312,11 +312,21 @@ static int sco_connect(struct sock *sk) struct sco_conn *conn; struct hci_conn *hcon; struct hci_dev *hdev; + bdaddr_t src, dst; + struct bt_codec codec; + __u16 setting; int err, type; - BT_DBG("%pMR -> %pMR", &sco_pi(sk)->src, &sco_pi(sk)->dst); + lock_sock(sk); + bacpy(&src, &sco_pi(sk)->src); + bacpy(&dst, &sco_pi(sk)->dst); + setting = sco_pi(sk)->setting; + codec = sco_pi(sk)->codec; + release_sock(sk); - hdev = hci_get_route(&sco_pi(sk)->dst, &sco_pi(sk)->src, BDADDR_BREDR); + BT_DBG("%pMR -> %pMR", &src, &dst); + + hdev = hci_get_route(&dst, &src, BDADDR_BREDR); if (!hdev) return -EHOSTUNREACH; @@ -327,7 +337,7 @@ static int sco_connect(struct sock *sk) else type = SCO_LINK; - switch (sco_pi(sk)->setting & SCO_AIRMODE_MASK) { + switch (setting & SCO_AIRMODE_MASK) { case SCO_AIRMODE_TRANSP: if (!lmp_transp_capable(hdev) || !lmp_esco_capable(hdev)) { err = -EOPNOTSUPP; @@ -336,8 +346,8 @@ static int sco_connect(struct sock *sk) break; } - hcon = hci_connect_sco(hdev, type, &sco_pi(sk)->dst, - sco_pi(sk)->setting, &sco_pi(sk)->codec, + hcon = hci_connect_sco(hdev, type, &dst, + setting, &codec, READ_ONCE(sk->sk_sndtimeo)); if (IS_ERR(hcon)) { err = PTR_ERR(hcon); From 149324fc762c2a7acef9c26790566f81f475e51f Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Tue, 2 Jun 2026 16:48:34 -0400 Subject: [PATCH 295/337] Bluetooth: MGMT: Fix backward compatibility with userspace bluetoothd has a bug with makes it send extra bytes as part of MGMT_OP_ADD_EXT_ADV_DATA which are now being checked to be the exact the expected length, relax this so only when the expected length is greater than the data length to cause an error since that would result in accessing invalid memory, otherwise just ignore the extra bytes. Link: https://lore.kernel.org/linux-bluetooth/20260602204749.210857-1-luiz.dentz@gmail.com/T/#u Fixes: d3f7d17960ed ("Bluetooth: MGMT: validate Add Extended Advertising Data length") Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/mgmt.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 027b266ccc74..f4aa814a0397 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -9114,8 +9114,9 @@ static int add_ext_adv_data(struct sock *sk, struct hci_dev *hdev, void *data, BT_DBG("%s", hdev->name); - expected_len = struct_size(cp, data, cp->adv_data_len + cp->scan_rsp_len); - if (expected_len != data_len) + expected_len = struct_size(cp, data, cp->adv_data_len + + cp->scan_rsp_len); + if (expected_len > data_len) return mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_EXT_ADV_DATA, MGMT_STATUS_INVALID_PARAMS); From 22ba97ea9cc1f63a0d0244fae38057ed452b6ac7 Mon Sep 17 00:00:00 2001 From: Jason Xing Date: Sat, 30 May 2026 12:26:30 +0800 Subject: [PATCH 296/337] xsk: cache csum_start/csum_offset to fix TOCTOU in xsk_skb_metadata() The TX metadata area resides in the UMEM buffer which is memory-mapped and concurrently writable by userspace. In xsk_skb_metadata(), csum_start and csum_offset are read from shared memory for bounds validation, then read again for skb assignment. A malicious userspace application can race to overwrite these values between the two reads, bypassing the bounds check and causing out-of-bounds memory access during checksum computation in the transmit path. Fix this by reading csum_start and csum_offset into local variables once, then using the local copies for both validation and assignment. Note that other metadata fields (flags, launch_time) and the cached csum fields may be mutually inconsistent due to concurrent userspace writes, but this is benign: the only security-critical invariant is that each field's validated value is the same one used, which local caching guarantees. Closes: https://lore.kernel.org/all/20260503200927.73EA1C2BCB4@smtp.kernel.org/ Reviewed-by: Maciej Fijalkowski Signed-off-by: Jason Xing Acked-by: Stanislav Fomichev Fixes: 48eb03dd2630 ("xsk: Add TX timestamp and TX checksum offload support") Link: https://patch.msgid.link/20260530042630.80626-1-kerneljasonxing@gmail.com Signed-off-by: Jakub Kicinski --- net/xdp/xsk.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/net/xdp/xsk.c b/net/xdp/xsk.c index 5e5786cd9af5..f8c8a8c9dfba 100644 --- a/net/xdp/xsk.c +++ b/net/xdp/xsk.c @@ -802,6 +802,7 @@ static int xsk_skb_metadata(struct sk_buff *skb, void *buffer, u32 hr) { struct xsk_tx_metadata *meta = NULL; + u16 csum_start, csum_offset; if (unlikely(pool->tx_metadata_len == 0)) return -EINVAL; @@ -811,13 +812,15 @@ static int xsk_skb_metadata(struct sk_buff *skb, void *buffer, return -EINVAL; if (meta->flags & XDP_TXMD_FLAGS_CHECKSUM) { - if (unlikely(meta->request.csum_start + - meta->request.csum_offset + + csum_start = READ_ONCE(meta->request.csum_start); + csum_offset = READ_ONCE(meta->request.csum_offset); + + if (unlikely(csum_start + csum_offset + sizeof(__sum16) > desc->len)) return -EINVAL; - skb->csum_start = hr + meta->request.csum_start; - skb->csum_offset = meta->request.csum_offset; + skb->csum_start = hr + csum_start; + skb->csum_offset = csum_offset; skb->ip_summed = CHECKSUM_PARTIAL; if (unlikely(pool->tx_sw_csum)) { From 56d0885514491e5ed8f7593400879ab77c52504c Mon Sep 17 00:00:00 2001 From: Jonas Jelonek Date: Thu, 28 May 2026 20:52:40 +0000 Subject: [PATCH 297/337] net: sfp: initialize i2c_block_size at adapter configure time sfp->i2c_block_size is only assigned in sfp_sm_mod_probe(), which runs from the state machine timer after SFP_F_PRESENT has been set. Between those two points, sfp_module_eeprom() (the ethtool -m callback) gates only on SFP_F_PRESENT and can be entered with i2c_block_size still at its kzalloc'd value of 0. On a pure-I2C adapter, sfp_i2c_read() then issues an i2c_transfer() with msgs[1].len = 0 inside a loop that subtracts this_len from len each iteration; on adapters that succeed a zero-length read the loop never advances, spinning while holding rtnl_lock. This was previously addressed by initializing i2c_block_size in sfp_alloc() (commit 813c2dd78618), but the initialization was dropped when i2c_block_size was split from i2c_max_block_size. Initialize sfp->i2c_block_size from sfp->i2c_max_block_size in sfp_i2c_configure(), so the field is valid as soon as the adapter is known. sfp_sm_mod_probe() still reassigns it on each module insertion to recover from a per-module clamp to 1 (sfp_id_needs_byte_io). Fixes: 7662abf4db94 ("net: phy: sfp: Add support for SMBus module access") Cc: stable@vger.kernel.org Signed-off-by: Jonas Jelonek Link: https://patch.msgid.link/20260528205242.971410-2-jelonek.jonas@gmail.com Signed-off-by: Jakub Kicinski --- drivers/net/phy/sfp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c index bd970f753beb..b94b9c433a21 100644 --- a/drivers/net/phy/sfp.c +++ b/drivers/net/phy/sfp.c @@ -822,6 +822,7 @@ static int sfp_i2c_configure(struct sfp *sfp, struct i2c_adapter *i2c) return -EINVAL; } + sfp->i2c_block_size = sfp->i2c_max_block_size; return 0; } From 2cdeaba5a1087f0f83e56729ea5c730b498639d9 Mon Sep 17 00:00:00 2001 From: Yizhou Zhao Date: Fri, 29 May 2026 18:50:16 +0800 Subject: [PATCH 298/337] appletalk: aarp: zero-initialize aarp_entry to prevent heap info leak aarp_alloc() allocates struct aarp_entry without zeroing it, but only initializes refcnt and packet_queue. When an unresolved AARP entry is created, hwaddr[ETH_ALEN] is left uninitialized. aarp_seq_show() later prints this field with %pM when users read /proc/net/atalk/arp. This can expose 6 bytes of stale heap data for each unresolved entry. Fix this by zero-initializing struct aarp_entry at allocation time. Reported-by: Yizhou Zhao Reported-by: Yuxiang Yang Reported-by: Ao Wang Reported-by: Xuewei Feng Reported-by: Qi Li Reported-by: Ke Xu Signed-off-by: Yizhou Zhao Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260529105017.81531-1-zhaoyz24@mails.tsinghua.edu.cn Signed-off-by: Jakub Kicinski --- net/appletalk/aarp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/appletalk/aarp.c b/net/appletalk/aarp.c index 30493ea3c010..078fb7a6efa5 100644 --- a/net/appletalk/aarp.c +++ b/net/appletalk/aarp.c @@ -393,7 +393,7 @@ static void aarp_purge(void) */ static struct aarp_entry *aarp_alloc(void) { - struct aarp_entry *a = kmalloc_obj(*a, GFP_ATOMIC); + struct aarp_entry *a = kzalloc_obj(*a, GFP_ATOMIC); if (!a) return NULL; From a910fb8f7b9e4c566db363e6c2ec378dc7153995 Mon Sep 17 00:00:00 2001 From: Geetha sowjanya Date: Fri, 29 May 2026 17:07:57 +0530 Subject: [PATCH 299/337] octeontx2-pf: Fix NDC sync operation errors On system reboot "rvu_nicpf 0002:03:00.0: NDC sync operation failed" error messages are shown, even if the operations is successful. This is due to wrong if error check in ndc_syc() function. Fixes: 42c45ac1419c ("octeontx2-af: Sync NIX and NPA contexts from NDC to LLC/DRAM") Signed-off-by: Geetha sowjanya Signed-off-by: Subbaraya Sundeep Reviewed-by: Simon Horman Link: https://patch.msgid.link/1780054677-17249-1-git-send-email-sbhatta@marvell.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c index ee623476e5ff..f9fbf0c17648 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c @@ -3473,7 +3473,7 @@ static void otx2_ndc_sync(struct otx2_nic *pf) req->nix_lf_rx_sync = 1; req->npa_lf_sync = 1; - if (!otx2_sync_mbox_msg(mbox)) + if (otx2_sync_mbox_msg(mbox)) dev_err(pf->dev, "NDC sync operation failed\n"); mutex_unlock(&mbox->lock); From 9a85ec3dc28b6df246801c19e4d9bae6297a25b0 Mon Sep 17 00:00:00 2001 From: Suman Ghosh Date: Fri, 29 May 2026 17:07:05 +0530 Subject: [PATCH 300/337] octeontx2-af: Fix initialization of mcam's entry2target_pffunc field NPC mcam entry stores a mapping between mcam entry and target pcifunc. During initialization of this field, API kmalloc_array has been used which caused some junk values to array. Whereas, the array is expected to be initialized by 0. This patch fixes the same by using kcalloc instead of kmalloc_array. Fixes: 55307fcb9258 ("octeontx2-af: Add mbox messages to install and delete MCAM rules") Signed-off-by: Suman Ghosh Signed-off-by: Subbaraya Sundeep Reviewed-by: Simon Horman Link: https://patch.msgid.link/1780054625-17090-1-git-send-email-sbhatta@marvell.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c index 607d0cf1a778..6bbda0593fcd 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c @@ -2192,8 +2192,8 @@ int npc_mcam_rsrcs_init(struct rvu *rvu, int blkaddr) goto free_entry_cntr_map; /* Alloc memory for saving target device of mcam rule */ - mcam->entry2target_pffunc = kmalloc_array(mcam->total_entries, - sizeof(u16), GFP_KERNEL); + mcam->entry2target_pffunc = kcalloc(mcam->total_entries, + sizeof(u16), GFP_KERNEL); if (!mcam->entry2target_pffunc) goto free_cntr_refcnt; From c1f07a7f2d47aeb9878301e7bb36bc1c2bc2be8e Mon Sep 17 00:00:00 2001 From: Jianyu Li Date: Mon, 1 Jun 2026 19:36:39 +0800 Subject: [PATCH 301/337] af_unix: Fix inq_len update problem in partial read Currently inq_len is updated only when the whole skb is consumed. If only part of the data is read, following SIOCINQ query would get value greater than what actually left. This change update inq_len timely in unix_stream_read_generic(), and adjust unix_stream_read_skb() accordingly to prevent repetitive update. Fixes: f4e1fb04c123 ("af_unix: Use cached value for SOCK_STREAM in unix_inq_len().") Signed-off-by: Jianyu Li Reviewed-by: Kuniyuki Iwashima Link: https://patch.msgid.link/20260601113640.231897-2-jianyu.li@mediatek.com Signed-off-by: Jakub Kicinski --- net/unix/af_unix.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index dc71ed79be4a..0d9cd977c7b7 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -2886,7 +2886,7 @@ static int unix_stream_read_skb(struct sock *sk, skb_read_actor_t recv_actor) return -EAGAIN; } - WRITE_ONCE(u->inq_len, u->inq_len - skb->len); + WRITE_ONCE(u->inq_len, u->inq_len - unix_skb_len(skb)); #if IS_ENABLED(CONFIG_AF_UNIX_OOB) if (skb == u->oob_skb) { @@ -3063,11 +3063,12 @@ static int unix_stream_read_generic(struct unix_stream_read_state *state, unix_detach_fds(&scm, skb); } - if (unix_skb_len(skb)) - break; - spin_lock(&sk->sk_receive_queue.lock); - WRITE_ONCE(u->inq_len, u->inq_len - skb->len); + WRITE_ONCE(u->inq_len, u->inq_len - chunk); + if (unix_skb_len(skb)) { + spin_unlock(&sk->sk_receive_queue.lock); + break; + } __skb_unlink(skb, &sk->sk_receive_queue); spin_unlock(&sk->sk_receive_queue.lock); From dd8975ad710ea1f3d7c7a36295072fd5ee59ca0a Mon Sep 17 00:00:00 2001 From: Jianyu Li Date: Mon, 1 Jun 2026 19:36:40 +0800 Subject: [PATCH 302/337] af_unix: Add test for SCM_INQ on partial read Add test to verify that when a skb is partially consumed, unix_inq_len() return correct remaining byte count. Before: # RUN scm_inq.stream.partial_read ... # scm_inq.c:165:partial_read:Expected remain (512) == *(int *)CMSG_DATA(cmsg) (768) # partial_read: Test terminated by assertion # FAIL scm_inq.stream.partial_read not ok 2 scm_inq.stream.partial_read After: # RUN scm_inq.stream.partial_read ... # OK scm_inq.stream.partial_read ok 2 scm_inq.stream.partial_read Signed-off-by: Jianyu Li Reviewed-by: Kuniyuki Iwashima Link: https://patch.msgid.link/20260601113640.231897-3-jianyu.li@mediatek.com Signed-off-by: Jakub Kicinski --- tools/testing/selftests/net/af_unix/scm_inq.c | 54 ++++++++++++++++++- 1 file changed, 52 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/net/af_unix/scm_inq.c b/tools/testing/selftests/net/af_unix/scm_inq.c index 3a86be9bda17..6268b5bf50be 100644 --- a/tools/testing/selftests/net/af_unix/scm_inq.c +++ b/tools/testing/selftests/net/af_unix/scm_inq.c @@ -8,8 +8,9 @@ #include "kselftest_harness.h" -#define NR_CHUNKS 100 -#define MSG_LEN 256 +#define NR_CHUNKS 100 +#define MSG_LEN 256 +#define NR_PARTIAL_READS 3 FIXTURE(scm_inq) { @@ -120,4 +121,53 @@ TEST_F(scm_inq, basic) recv_chunks(_metadata, self); } +TEST_F(scm_inq, partial_read) +{ + char buf[MSG_LEN * NR_PARTIAL_READS] = {}; + char cmsg_buf[CMSG_SPACE(sizeof(int))]; + struct msghdr msg = {}; + struct iovec iov = {}; + struct cmsghdr *cmsg; + int err, inq, ret, i; + int remain; + + err = setsockopt(self->fd[1], SOL_SOCKET, SO_INQ, &(int){1}, sizeof(int)); + if (variant->type != SOCK_STREAM) { + ASSERT_EQ(-ENOPROTOOPT, -errno); + return; + } + ASSERT_EQ(0, err); + + ret = send(self->fd[0], buf, sizeof(buf), 0); + ASSERT_EQ(sizeof(buf), ret); + + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = cmsg_buf; + msg.msg_controllen = sizeof(cmsg_buf); + + iov.iov_base = buf; + iov.iov_len = MSG_LEN; + + for (i = 0; i < NR_PARTIAL_READS; i++) { + remain = MSG_LEN * (NR_PARTIAL_READS - 1 - i); + + memset(buf, 0, MSG_LEN); + memset(cmsg_buf, 0, sizeof(cmsg_buf)); + ret = recvmsg(self->fd[1], &msg, 0); + ASSERT_EQ(MSG_LEN, ret); + + cmsg = CMSG_FIRSTHDR(&msg); + ASSERT_NE(NULL, cmsg); + ASSERT_EQ(CMSG_LEN(sizeof(int)), cmsg->cmsg_len); + ASSERT_EQ(SOL_SOCKET, cmsg->cmsg_level); + ASSERT_EQ(SCM_INQ, cmsg->cmsg_type); + ASSERT_EQ(remain, *(int *)CMSG_DATA(cmsg)); + + ret = ioctl(self->fd[1], SIOCINQ, &inq); + ASSERT_EQ(0, ret); + ASSERT_EQ(remain, inq); + } +} + TEST_HARNESS_MAIN From d3915a1f5a4bc0ac911032903c3c6ab8df9fcc7c Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 2 Jun 2026 16:15:47 +0000 Subject: [PATCH 303/337] ipv4: restrict IPOPT_SSRR and IPOPT_LSRR options This patch restricts setting Loose Source and Record Route (LSRR) and Strict Source and Record Route (SSRR) IP options to users with CAP_NET_RAW capability. This prevents unprivileged applications from forcing packets to route through attacker-controlled nodes to leak TCP ISN and possibly other protocol information. While LSRR and SSRR are commonly filtered in many network environments, they may still be supported and forwarded along some network paths. RFC 7126 (Recommendations on Filtering of IPv4 Packets Containing IPv4 Options) recommend to drop these options in 4.3 and 4.4. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Reported-by: Tamir Shahar Reported-by: Amit Klein Signed-off-by: Eric Dumazet Reviewed-by: David Ahern Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260602161547.2642155-1-edumazet@google.com Signed-off-by: Jakub Kicinski --- net/ipv4/ip_options.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/net/ipv4/ip_options.c b/net/ipv4/ip_options.c index be8815ce3ac2..09d745112c15 100644 --- a/net/ipv4/ip_options.c +++ b/net/ipv4/ip_options.c @@ -530,6 +530,10 @@ int ip_options_get(struct net *net, struct ip_options_rcu **optp, kfree(opt); return -EINVAL; } + if (opt->opt.srr && !ns_capable(net->user_ns, CAP_NET_RAW)) { + kfree(opt); + return -EPERM; + } kfree(*optp); *optp = opt; return 0; From 672bd0519e27c357c43b7f8c0d653fce3817d06e Mon Sep 17 00:00:00 2001 From: Kurt Kanzenbach Date: Fri, 29 May 2026 19:11:47 +0200 Subject: [PATCH 304/337] ptp: vclock: Switch from RCU to SRCU The usage of PTP vClocks leads immediately to the following issues with ptp4l with LOCKDEP and DEBUG_ATOMIC_SLEEP enabled: "BUG: sleeping function called from invalid context". ptp_convert_timestamp() acquires a mutex_t within a RCU read section. This is illegal, because acquiring a mutex_t can result in voluntary scheduling request which is not allowed within a RCU read section. Replace the RCU usage with SRCU where sleeping is allowed. Reported-by: Florian Zeitz Closes: https://lore.kernel.org/all/00a8cce8-410e-4038-98af-49be6d93d7bd@schettke.com/ Fixes: 67d93ffc0f3c ("ptp: vclock: use mutex to fix "sleep on atomic" bug") Signed-off-by: Kurt Kanzenbach Reviewed-by: Sebastian Andrzej Siewior Link: https://patch.msgid.link/20260529-vclock_rcu-v2-1-02a5531fab92@linutronix.de Signed-off-by: Jakub Kicinski --- drivers/ptp/ptp_vclock.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/ptp/ptp_vclock.c b/drivers/ptp/ptp_vclock.c index 915a4f6defc9..84cb527f59cc 100644 --- a/drivers/ptp/ptp_vclock.c +++ b/drivers/ptp/ptp_vclock.c @@ -19,6 +19,8 @@ static DEFINE_SPINLOCK(vclock_hash_lock); static DEFINE_READ_MOSTLY_HASHTABLE(vclock_hash, 8); +DEFINE_STATIC_SRCU(vclock_srcu); + static void ptp_vclock_hash_add(struct ptp_vclock *vclock) { spin_lock(&vclock_hash_lock); @@ -37,7 +39,7 @@ static void ptp_vclock_hash_del(struct ptp_vclock *vclock) spin_unlock(&vclock_hash_lock); - synchronize_rcu(); + synchronize_srcu(&vclock_srcu); } static int ptp_vclock_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) @@ -276,14 +278,16 @@ ktime_t ptp_convert_timestamp(const ktime_t *hwtstamp, int vclock_index) { unsigned int hash = vclock_index % HASH_SIZE(vclock_hash); struct ptp_vclock *vclock; - u64 ns; u64 vclock_ns = 0; + int srcu_idx; + u64 ns; ns = ktime_to_ns(*hwtstamp); - rcu_read_lock(); + srcu_idx = srcu_read_lock(&vclock_srcu); - hlist_for_each_entry_rcu(vclock, &vclock_hash[hash], vclock_hash_node) { + hlist_for_each_entry_srcu(vclock, &vclock_hash[hash], vclock_hash_node, + srcu_read_lock_held(&vclock_srcu)) { if (vclock->clock->index != vclock_index) continue; @@ -294,7 +298,7 @@ ktime_t ptp_convert_timestamp(const ktime_t *hwtstamp, int vclock_index) break; } - rcu_read_unlock(); + srcu_read_unlock(&vclock_srcu, srcu_idx); return ns_to_ktime(vclock_ns); } From 9d8d28738f24b75616d6ca7a27cb4aed88520343 Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Tue, 2 Jun 2026 22:14:08 +1000 Subject: [PATCH 305/337] mptcp: fix missing wakeups in edge scenarios The mptcp_recvmsg() can fill MPTCP socket receive queue via mptcp_move_skbs(), but currently does not try to wakeup any listener, because the same process is going to check the receive queue soon. When multiple threads are reading from the same fd, the above can cause stall. Add the missing wakeup. Fixes: 6771bfd9ee24 ("mptcp: update mptcp ack sequence from work queue") Cc: stable@vger.kernel.org Signed-off-by: Paolo Abeni Reviewed-by: Matthieu Baerts (NGI0) Signed-off-by: Matthieu Baerts (NGI0) Link: https://patch.msgid.link/20260602-net-mptcp-misc-fixes-7-1-rc7-v2-1-856831229976@kernel.org Signed-off-by: Jakub Kicinski --- net/mptcp/protocol.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c index a72a6ad6ee8b..5a20ab2789ae 100644 --- a/net/mptcp/protocol.c +++ b/net/mptcp/protocol.c @@ -2276,6 +2276,10 @@ static bool mptcp_move_skbs(struct sock *sk) mptcp_backlog_spooled(sk, moved, &skbs); } mptcp_data_unlock(sk); + + if (enqueued && mptcp_epollin_ready(sk)) + sk->sk_data_ready(sk); + return enqueued; } From d1918b36edcaed0ec4ef6888b2358c6b1ddcff47 Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Tue, 2 Jun 2026 22:14:09 +1000 Subject: [PATCH 306/337] mptcp: fix retransmission loop when csum is enabled Sashiko noted that retransmission with csum enabled can actually transmit new data, but currently the relevant code does not update accordingly snd_nxt. The may cause incoming ack drop and an endless retransmission loop. Address the issue incrementing snd_nxt as needed. Fixes: 4e14867d5e91 ("mptcp: tune re-injections for csum enabled mode") Cc: stable@vger.kernel.org Signed-off-by: Paolo Abeni Reviewed-by: Matthieu Baerts (NGI0) Signed-off-by: Matthieu Baerts (NGI0) Link: https://patch.msgid.link/20260602-net-mptcp-misc-fixes-7-1-rc7-v2-2-856831229976@kernel.org Signed-off-by: Jakub Kicinski --- net/mptcp/protocol.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c index 5a20ab2789ae..7fac5fac2097 100644 --- a/net/mptcp/protocol.c +++ b/net/mptcp/protocol.c @@ -2869,6 +2869,10 @@ static void __mptcp_retrans(struct sock *sk) msk->bytes_retrans += len; dfrag->already_sent = max(dfrag->already_sent, len); + /* With csum enabled retransmission can send new data. */ + if (after64(dfrag->already_sent + dfrag->data_seq, msk->snd_nxt)) + WRITE_ONCE(msk->snd_nxt, dfrag->already_sent + dfrag->data_seq); + reset_timer: mptcp_check_and_set_pending(sk); From 8ab24fdebc369c0dfb90f82c1650b1e66662bb45 Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Tue, 2 Jun 2026 22:14:10 +1000 Subject: [PATCH 307/337] mptcp: close TOCTOU race while computing rcv_wnd The MPTCP output path access locklessly the MPTCP-level ack_seq in multiple times, using possibly different values for the data_ack in the DSS option and to compute the announced rcv wnd for the same packet. Refactor the cote to avoid inconsistencies which may confuse the peer. Also ensure that the MPTCP level rcv wnd is updated only when the egress packet actually contains a DSS ack. Fixes: fa3fe2b15031 ("mptcp: track window announced to peer") Cc: stable@vger.kernel.org Signed-off-by: Paolo Abeni Reviewed-by: Matthieu Baerts (NGI0) Signed-off-by: Matthieu Baerts (NGI0) Link: https://patch.msgid.link/20260602-net-mptcp-misc-fixes-7-1-rc7-v2-3-856831229976@kernel.org Signed-off-by: Jakub Kicinski --- net/mptcp/options.c | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/net/mptcp/options.c b/net/mptcp/options.c index 8a1c5698983c..2d25f319f328 100644 --- a/net/mptcp/options.c +++ b/net/mptcp/options.c @@ -570,7 +570,6 @@ static bool mptcp_established_options_dss(struct sock *sk, struct sk_buff *skb, struct mptcp_ext *mpext; unsigned int ack_size; bool ret = false; - u64 ack_seq; opts->csum_reqd = READ_ONCE(msk->csum_enabled); mpext = skb ? mptcp_get_ext(skb) : NULL; @@ -601,14 +600,11 @@ static bool mptcp_established_options_dss(struct sock *sk, struct sk_buff *skb, return ret; } - ack_seq = READ_ONCE(msk->ack_seq); if (READ_ONCE(msk->use_64bit_ack)) { ack_size = TCPOLEN_MPTCP_DSS_ACK64; - opts->ext_copy.data_ack = ack_seq; opts->ext_copy.ack64 = 1; } else { ack_size = TCPOLEN_MPTCP_DSS_ACK32; - opts->ext_copy.data_ack32 = (uint32_t)ack_seq; opts->ext_copy.ack64 = 0; } opts->ext_copy.use_ack = 1; @@ -1297,19 +1293,14 @@ bool mptcp_incoming_options(struct sock *sk, struct sk_buff *skb) return true; } -static void mptcp_set_rwin(struct tcp_sock *tp, struct tcphdr *th) +static u64 mptcp_set_rwin(struct mptcp_sock *msk, struct tcp_sock *tp, + struct tcphdr *th, u64 ack_seq) { const struct sock *ssk = (const struct sock *)tp; - struct mptcp_subflow_context *subflow; - u64 ack_seq, rcv_wnd_old, rcv_wnd_new; - struct mptcp_sock *msk; + u64 rcv_wnd_old, rcv_wnd_new; u32 new_win; u64 win; - subflow = mptcp_subflow_ctx(ssk); - msk = mptcp_sk(subflow->conn); - - ack_seq = READ_ONCE(msk->ack_seq); rcv_wnd_new = ack_seq + tp->rcv_wnd; rcv_wnd_old = atomic64_read(&msk->rcv_wnd_sent); @@ -1362,7 +1353,7 @@ static void mptcp_set_rwin(struct tcp_sock *tp, struct tcphdr *th) update_wspace: WRITE_ONCE(msk->old_wspace, tp->rcv_wnd); - subflow->rcv_wnd_sent = rcv_wnd_new; + return rcv_wnd_new; } static void mptcp_track_rwin(struct tcp_sock *tp) @@ -1474,13 +1465,25 @@ void mptcp_write_options(struct tcphdr *th, __be32 *ptr, struct tcp_sock *tp, *ptr++ = mptcp_option(MPTCPOPT_DSS, len, 0, flags); if (mpext->use_ack) { + struct mptcp_sock *msk; + u64 ack_seq; + + /* DSS option is set only by mptcp_established_options, + * the caller is __tcp_transmit_skb() and ssk is always + * not NULL. + */ + subflow = mptcp_subflow_ctx(ssk); + msk = mptcp_sk(subflow->conn); + ack_seq = READ_ONCE(msk->ack_seq); if (mpext->ack64) { - put_unaligned_be64(mpext->data_ack, ptr); + put_unaligned_be64(ack_seq, ptr); ptr += 2; } else { - put_unaligned_be32(mpext->data_ack32, ptr); + put_unaligned_be32(ack_seq, ptr); ptr += 1; } + subflow->rcv_wnd_sent = mptcp_set_rwin(msk, tp, th, + ack_seq); } if (mpext->use_map) { @@ -1708,9 +1711,6 @@ void mptcp_write_options(struct tcphdr *th, __be32 *ptr, struct tcp_sock *tp, i += 4; } } - - if (tp) - mptcp_set_rwin(tp, th); } __be32 mptcp_get_reset_option(const struct sk_buff *skb) From da23be77e1292cd611e736c3aa17da633d7ddce7 Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Tue, 2 Jun 2026 22:14:11 +1000 Subject: [PATCH 308/337] mptcp: allow subflow rcv wnd to shrink In MPTCP connection, the `window` field in the TCP header refers to the MPTCP-level rcv_nxt and it's right edge should not move backward. Such constraint is enforced at DSS option generation time. At the same time, the TCP stack ensures independently that the TCP-level rcv wnd right's edge does not move backward. That in turn causes artificial inflating of the MPTCP rcv window when the incoming data is acked at the TCP level and is OoO in the MPTCP sequence space (or lands in the backlog). As a consequence, the incoming traffic can exceed the receiver rcvbuf size even when the sender is not misbehaving. Prevent such scenario forcibly allowing the TCP subflow to shrink the TCP-level rcv wnd regardless of the current netns setting. Fixes: f3589be0c420 ("mptcp: never shrink offered window") Cc: stable@vger.kernel.org Signed-off-by: Paolo Abeni Reviewed-by: Matthieu Baerts (NGI0) Signed-off-by: Matthieu Baerts (NGI0) Link: https://patch.msgid.link/20260602-net-mptcp-misc-fixes-7-1-rc7-v2-4-856831229976@kernel.org Signed-off-by: Jakub Kicinski --- net/mptcp/options.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/net/mptcp/options.c b/net/mptcp/options.c index 2d25f319f328..51ca334678b4 100644 --- a/net/mptcp/options.c +++ b/net/mptcp/options.c @@ -566,6 +566,7 @@ static bool mptcp_established_options_dss(struct sock *sk, struct sk_buff *skb, { struct mptcp_subflow_context *subflow = mptcp_subflow_ctx(sk); struct mptcp_sock *msk = mptcp_sk(subflow->conn); + struct tcp_sock *tp = tcp_sk(sk); unsigned int dss_size = 0; struct mptcp_ext *mpext; unsigned int ack_size; @@ -614,6 +615,12 @@ static bool mptcp_established_options_dss(struct sock *sk, struct sk_buff *skb, if (dss_size == 0) ack_size += TCPOLEN_MPTCP_DSS_BASE; + /* The caller is __tcp_transmit_skb(), and will compute the new rcv + * wnd soon: ensure that the window can shrink. + */ + if (skb) + tp->rcv_wnd = tp->rcv_nxt - tp->rcv_wup; + dss_size += ack_size; *size = ALIGN(dss_size, 4); From 14e9fea30b68fc75b2b3d97396a7e6adb544bd2a Mon Sep 17 00:00:00 2001 From: Tao Cui Date: Tue, 2 Jun 2026 22:14:12 +1000 Subject: [PATCH 309/337] mptcp: pm: fix extra_subflows underflow on userspace PM subflow creation The userspace PM increments extra_subflows after __mptcp_subflow_connect() succeeds, but __mptcp_subflow_connect() calls mptcp_pm_close_subflow() on failure to roll back the pre-increment done by the kernel PM's fill_*() helpers. Because the userspace PM hasn't incremented yet at that point, this decrement is spurious and causes extra_subflows to underflow. Fix it by aligning the userspace PM with the kernel PM: increment extra_subflows before calling __mptcp_subflow_connect(), so the existing error path in subflow.c correctly rolls it back on failure. Also simplify the error handling by taking pm.lock only when needed for cleanup. Fixes: 77e4b94a3de6 ("mptcp: update userspace pm infos") Cc: stable@vger.kernel.org Signed-off-by: Tao Cui Reviewed-by: Matthieu Baerts (NGI0) Signed-off-by: Matthieu Baerts (NGI0) Link: https://patch.msgid.link/20260602-net-mptcp-misc-fixes-7-1-rc7-v2-5-856831229976@kernel.org Signed-off-by: Jakub Kicinski --- net/mptcp/pm_userspace.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/net/mptcp/pm_userspace.c b/net/mptcp/pm_userspace.c index 8cbc1920afb4..0d3a95e676f1 100644 --- a/net/mptcp/pm_userspace.c +++ b/net/mptcp/pm_userspace.c @@ -408,19 +408,21 @@ int mptcp_pm_nl_subflow_create_doit(struct sk_buff *skb, struct genl_info *info) local.flags = entry.flags; local.ifindex = entry.ifindex; + spin_lock_bh(&msk->pm.lock); + msk->pm.extra_subflows++; + spin_unlock_bh(&msk->pm.lock); + lock_sock(sk); err = __mptcp_subflow_connect(sk, &local, &addr_r); release_sock(sk); - if (err) + if (err) { GENL_SET_ERR_MSG_FMT(info, "connect error: %d", err); - spin_lock_bh(&msk->pm.lock); - if (err) + spin_lock_bh(&msk->pm.lock); mptcp_userspace_pm_delete_local_addr(msk, &entry); - else - msk->pm.extra_subflows++; - spin_unlock_bh(&msk->pm.lock); + spin_unlock_bh(&msk->pm.lock); + } create_err: sock_put(sk); From 06fd2bec7aebf393288e4b78924482fe170caabc Mon Sep 17 00:00:00 2001 From: Tao Cui Date: Tue, 2 Jun 2026 22:14:13 +1000 Subject: [PATCH 310/337] selftests: mptcp: add test for extra_subflows underflow on userspace PM Add a test to verify that when userspace PM fails to create a subflow (e.g. using an unreachable address), the extra_subflows counter is not decremented below zero. Fixes: 77e4b94a3de6 ("mptcp: update userspace pm infos") Cc: stable@vger.kernel.org Signed-off-by: Tao Cui Reviewed-by: Matthieu Baerts (NGI0) Signed-off-by: Matthieu Baerts (NGI0) Link: https://patch.msgid.link/20260602-net-mptcp-misc-fixes-7-1-rc7-v2-6-856831229976@kernel.org Signed-off-by: Jakub Kicinski --- tools/testing/selftests/net/mptcp/mptcp_join.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tools/testing/selftests/net/mptcp/mptcp_join.sh b/tools/testing/selftests/net/mptcp/mptcp_join.sh index 5acd12021e6e..4b3f71e66609 100755 --- a/tools/testing/selftests/net/mptcp/mptcp_join.sh +++ b/tools/testing/selftests/net/mptcp/mptcp_join.sh @@ -4100,6 +4100,10 @@ userspace_tests() chk_rm_nr 0 1 chk_mptcp_info subflows 0 subflows 0 chk_subflows_total 1 1 + # check counters are not affected by errors at creation time + userspace_pm_add_sf $ns2 10.0.12.2 10 2>/dev/null + chk_mptcp_info subflows 0 subflows 0 + chk_subflows_total 1 1 kill_events_pids mptcp_lib_kill_group_wait $tests_pid fi From 57132affbc89c02e1bf73fdf5724311bdc9a29da Mon Sep 17 00:00:00 2001 From: "Matthieu Baerts (NGI0)" Date: Tue, 2 Jun 2026 22:14:14 +1000 Subject: [PATCH 311/337] mptcp: sockopt: check timestamping ret value sock_set_timestamping() can fail for different reasons. The returned value should then be checked. If sock_set_timestamping() fails for at least one subflow, the first error is now reported to the userspace, similar to what is done with other socket options. Fixes: 9061f24bf82e ("mptcp: sockopt: propagate timestamp request to subflows") Cc: stable@vger.kernel.org Reported-by: Willem de Bruijn Closes: https://lore.kernel.org/willemdebruijn.kernel.178a41a53d041@gmail.com Reviewed-by: Mat Martineau Signed-off-by: Matthieu Baerts (NGI0) Link: https://patch.msgid.link/20260602-net-mptcp-misc-fixes-7-1-rc7-v2-7-856831229976@kernel.org Signed-off-by: Jakub Kicinski --- net/mptcp/sockopt.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/net/mptcp/sockopt.c b/net/mptcp/sockopt.c index 87b5796d0135..91aa57f1d0fd 100644 --- a/net/mptcp/sockopt.c +++ b/net/mptcp/sockopt.c @@ -241,15 +241,19 @@ static int mptcp_setsockopt_sol_socket_timestamping(struct mptcp_sock *msk, mptcp_for_each_subflow(msk, subflow) { struct sock *ssk = mptcp_subflow_tcp_sock(subflow); + int err; lock_sock(ssk); - sock_set_timestamping(ssk, optname, timestamping); + err = sock_set_timestamping(ssk, optname, timestamping); release_sock(ssk); + + if (err < 0 && ret == 0) + ret = err; } release_sock(sk); - return 0; + return ret; } static int mptcp_setsockopt_sol_socket_linger(struct mptcp_sock *msk, sockptr_t optval, From 7690137e70ab0fb1f8b5a30e6f087f8ee908b680 Mon Sep 17 00:00:00 2001 From: "Matthieu Baerts (NGI0)" Date: Tue, 2 Jun 2026 22:14:15 +1000 Subject: [PATCH 312/337] mptcp: sockopt: set sockopt on all subflows The mptcp_setsockopt_all_sf(), currently used only with TCP_MAXSEG, stopped when one subflow returned an error. Even if it is not wrong, this is different from the other helpers trying to set the option on all subflows, and then returning an error if at least one of them had an issue. Follow this behaviour, for a question of uniformity. Fixes: 51c5fd09e1b4 ("mptcp: add TCP_MAXSEG sockopt support") Cc: stable@vger.kernel.org Reviewed-by: Mat Martineau Signed-off-by: Matthieu Baerts (NGI0) Link: https://patch.msgid.link/20260602-net-mptcp-misc-fixes-7-1-rc7-v2-8-856831229976@kernel.org Signed-off-by: Jakub Kicinski --- net/mptcp/sockopt.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/net/mptcp/sockopt.c b/net/mptcp/sockopt.c index 91aa57f1d0fd..fcf6feb2a9eb 100644 --- a/net/mptcp/sockopt.c +++ b/net/mptcp/sockopt.c @@ -817,10 +817,11 @@ static int mptcp_setsockopt_all_sf(struct mptcp_sock *msk, int level, mptcp_for_each_subflow(msk, subflow) { struct sock *ssk = mptcp_subflow_tcp_sock(subflow); + int err; - ret = tcp_setsockopt(ssk, level, optname, optval, optlen); - if (ret) - break; + err = tcp_setsockopt(ssk, level, optname, optval, optlen); + if (err < 0 && ret == 0) + ret = err; } if (!ret) From c378b1a6f8dd3e02eb08661f4d5d50f236eead03 Mon Sep 17 00:00:00 2001 From: Gang Yan Date: Tue, 2 Jun 2026 22:14:16 +1000 Subject: [PATCH 313/337] mptcp: check desc->count in read_sock __tcp_read_sock() checks desc->count after each skb is consumed and breaks the loop when it reaches 0. The MPTCP variant lacks this check. This is a functional bug, other subsystems also rely on this check: TLS strparser sets desc->count to 0 once a full TLS record is assembled and depends on this break to stop reading. Add the same desc->count check to __mptcp_read_sock(), mirroring __tcp_read_sock(). Fixes: 250d9766a984 ("mptcp: implement .read_sock") Cc: stable@vger.kernel.org Co-developed-by: Geliang Tang Signed-off-by: Geliang Tang Signed-off-by: Gang Yan Reviewed-by: Matthieu Baerts (NGI0) Signed-off-by: Matthieu Baerts (NGI0) Link: https://patch.msgid.link/20260602-net-mptcp-misc-fixes-7-1-rc7-v2-9-856831229976@kernel.org Signed-off-by: Jakub Kicinski --- net/mptcp/protocol.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c index 7fac5fac2097..cb9515f505aa 100644 --- a/net/mptcp/protocol.c +++ b/net/mptcp/protocol.c @@ -4428,6 +4428,8 @@ static int __mptcp_read_sock(struct sock *sk, read_descriptor_t *desc, } mptcp_eat_recv_skb(sk, skb); + if (!desc->count) + break; } if (noack) From 5e939544f9d2b4d5c052a07cfcde97de44263946 Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Tue, 2 Jun 2026 22:14:17 +1000 Subject: [PATCH 314/337] mptcp: fix uninit-value in mptcp_established_options syzbot reported the following uninit splat: BUG: KMSAN: uninit-value in mptcp_write_data_fin net/mptcp/options.c:542 [inline] BUG: KMSAN: uninit-value in mptcp_established_options_dss net/mptcp/options.c:590 [inline] BUG: KMSAN: uninit-value in mptcp_established_options+0x112f/0x3530 net/mptcp/options.c:874 mptcp_write_data_fin net/mptcp/options.c:542 [inline] mptcp_established_options_dss net/mptcp/options.c:590 [inline] mptcp_established_options+0x112f/0x3530 net/mptcp/options.c:874 tcp_established_options+0x312/0xcc0 net/ipv4/tcp_output.c:1192 __tcp_transmit_skb+0x5dc/0x5fe0 net/ipv4/tcp_output.c:1575 __tcp_send_ack+0x967/0xad0 net/ipv4/tcp_output.c:4499 tcp_send_ack+0x3d/0x60 net/ipv4/tcp_output.c:4505 mptcp_subflow_shutdown+0x164/0x690 net/mptcp/protocol.c:3137 mptcp_check_send_data_fin+0x31b/0x3d0 net/mptcp/protocol.c:3218 __mptcp_wr_shutdown net/mptcp/protocol.c:3234 [inline] __mptcp_close+0x860/0x1360 net/mptcp/protocol.c:3313 mptcp_close+0x42/0x260 net/mptcp/protocol.c:3367 inet_release+0x1ee/0x2a0 net/ipv4/af_inet.c:442 __sock_release net/socket.c:722 [inline] sock_close+0xd6/0x2f0 net/socket.c:1514 __fput+0x60e/0x1010 fs/file_table.c:510 ____fput+0x25/0x30 fs/file_table.c:538 task_work_run+0x208/0x2b0 kernel/task_work.c:233 resume_user_mode_work include/linux/resume_user_mode.h:50 [inline] __exit_to_user_mode_loop kernel/entry/common.c:67 [inline] exit_to_user_mode_loop+0x306/0x1b60 kernel/entry/common.c:98 __exit_to_user_mode_prepare include/linux/irq-entry-common.h:207 [inline] syscall_exit_to_user_mode_prepare include/linux/irq-entry-common.h:238 [inline] syscall_exit_to_user_mode include/linux/entry-common.h:318 [inline] __do_fast_syscall_32+0x2c7/0x460 arch/x86/entry/syscall_32.c:310 do_fast_syscall_32+0x37/0x80 arch/x86/entry/syscall_32.c:332 do_SYSENTER_32+0x1f/0x30 arch/x86/entry/syscall_32.c:370 entry_SYSENTER_compat_after_hwframe+0x84/0x8e Local variable opts created at: __tcp_transmit_skb+0x4d/0x5fe0 net/ipv4/tcp_output.c:1536 __tcp_send_ack+0x967/0xad0 net/ipv4/tcp_output.c:4499 The output path currently omits initializing the mptcp extension `use_map` flag in a few corner cases. Address the issue always zeroing all the extensions flags before eventually initializing the individual bits. To that extent, introduce and use a struct_group to avoid multiple bitwise operations. Fixes: cfcceb7a39fc ("tcp: shrink per-packet memset in __tcp_transmit_skb()") Cc: stable@vger.kernel.org Reported-by: syzbot+ff020673c5e3d94d9478@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=ff020673c5e3d94d9478 Signed-off-by: Paolo Abeni Reviewed-by: Matthieu Baerts (NGI0) Signed-off-by: Matthieu Baerts (NGI0) Link: https://patch.msgid.link/20260602-net-mptcp-misc-fixes-7-1-rc7-v2-10-856831229976@kernel.org Signed-off-by: Jakub Kicinski --- include/net/mptcp.h | 7 +++++-- net/mptcp/options.c | 6 +++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/include/net/mptcp.h b/include/net/mptcp.h index f7263fe2a2e4..ee70f597a4de 100644 --- a/include/net/mptcp.h +++ b/include/net/mptcp.h @@ -27,7 +27,9 @@ struct mptcp_ext { u32 subflow_seq; u16 data_len; __sum16 csum; - u8 use_map:1, + + struct_group(flags, + u8 use_map:1, dsn64:1, data_fin:1, use_ack:1, @@ -35,9 +37,10 @@ struct mptcp_ext { mpc_map:1, frozen:1, reset_transient:1; - u8 reset_reason:4, + u8 reset_reason:4, csum_reqd:1, infinite_map:1; + ); /* end of flags group */ }; #define MPTCPOPT_HMAC_LEN 20 diff --git a/net/mptcp/options.c b/net/mptcp/options.c index 51ca334678b4..f9f587203c35 100644 --- a/net/mptcp/options.c +++ b/net/mptcp/options.c @@ -572,6 +572,11 @@ static bool mptcp_established_options_dss(struct sock *sk, struct sk_buff *skb, unsigned int ack_size; bool ret = false; + /* Zero `use_ack` and `use_map` flags with one shot. */ + BUILD_BUG_ON(sizeof_field(struct mptcp_ext, flags) != sizeof(u16)); + BUILD_BUG_ON(!IS_ALIGNED(offsetof(struct mptcp_ext, flags), + sizeof(u16))); + *(u16 *)&opts->ext_copy.flags = 0; opts->csum_reqd = READ_ONCE(msk->csum_enabled); mpext = skb ? mptcp_get_ext(skb) : NULL; @@ -595,7 +600,6 @@ static bool mptcp_established_options_dss(struct sock *sk, struct sk_buff *skb, /* passive sockets msk will set the 'can_ack' after accept(), even * if the first subflow may have the already the remote key handy */ - opts->ext_copy.use_ack = 0; if (!READ_ONCE(msk->can_ack)) { *size = ALIGN(dss_size, 4); return ret; From bd34fa0257261b76964df1c98f44b3cb4ee14620 Mon Sep 17 00:00:00 2001 From: "Matthieu Baerts (NGI0)" Date: Tue, 2 Jun 2026 22:14:18 +1000 Subject: [PATCH 315/337] mptcp: add-addr: always drop other suboptions When an ADD_ADDR needs to be sent, it could be prepared if there is enough remaining space and even if the packet is not a pure ACK. But it would be dropped soon after. Indeed, in mptcp_pm_add_addr_signal(), there is enough space to fit a DSS of 20 octets and an ADD_ADDR echo containing an IPv4 address on 8 octets for example. In this case, the packet would be prepared, the MPTCP_ADD_ADDR_ECHO bit would be removed from pm->addr_signal, but the option would be silently dropped in mptcp_established_options_add_addr() not to override DSS info in the union from 'struct mptcp_out_options', and also because mptcp_write_options() will enforce mutually exclusion with DSS. Instead, don't even try to send an ADD_ADDR if it is not a pure ACK. Retry for each new packet until a pure-ACK is emitted. That's fine to do that, because each time an ADD_ADDR (echo) is scheduled, a pure ACK is queued. This also simplifies the code, and the skb checks can be done earlier, before the lock. Note: also, since commit 6d0060f600ad ("mptcp: Write MPTCP DSS headers to outgoing data packets"), opts->ahmac would not have been set to 0 when other suboptions were not dropped, and when sending an ADD_ADDR echo. That would have resulted in sending an ADD_ADDR using garbage info, where there was not enough space, instead of an echo one without the ADD_ADDR HMAC. Fixes: 1bff1e43a30e ("mptcp: optimize out option generation") Cc: stable@vger.kernel.org Signed-off-by: Matthieu Baerts (NGI0) Link: https://patch.msgid.link/20260602-net-mptcp-misc-fixes-7-1-rc7-v2-11-856831229976@kernel.org Signed-off-by: Jakub Kicinski --- net/mptcp/options.c | 30 +++++++----------------------- net/mptcp/pm.c | 15 ++++----------- net/mptcp/protocol.h | 7 +++---- 3 files changed, 14 insertions(+), 38 deletions(-) diff --git a/net/mptcp/options.c b/net/mptcp/options.c index f9f587203c35..b3ea7854818f 100644 --- a/net/mptcp/options.c +++ b/net/mptcp/options.c @@ -665,7 +665,6 @@ static bool mptcp_established_options_add_addr(struct sock *sk, struct sk_buff * { struct mptcp_subflow_context *subflow = mptcp_subflow_ctx(sk); struct mptcp_sock *msk = mptcp_sk(subflow->conn); - bool drop_other_suboptions = false; unsigned int opt_size = *size; struct mptcp_addr_info addr; bool echo; @@ -676,36 +675,20 @@ static bool mptcp_established_options_add_addr(struct sock *sk, struct sk_buff * */ if (!mptcp_pm_should_add_signal(msk) || (opts->suboptions & (OPTION_MPTCP_MPJ_ACK | OPTION_MPTCP_MPC_ACK)) || - !mptcp_pm_add_addr_signal(msk, skb, opt_size, remaining, &addr, - &echo, &drop_other_suboptions)) + !skb || !skb_is_tcp_pure_ack(skb) || + !mptcp_pm_add_addr_signal(msk, opt_size, remaining, &addr, &echo)) return false; - /* - * Later on, mptcp_write_options() will enforce mutually exclusion with - * DSS, bail out if such option is set and we can't drop it. - */ - if (drop_other_suboptions) - remaining += opt_size; - else if (opts->suboptions & OPTION_MPTCP_DSS) - return false; + remaining += opt_size; len = mptcp_add_addr_len(addr.family, echo, !!addr.port); if (remaining < len) return false; *size = len; - if (drop_other_suboptions) { - pr_debug("drop other suboptions\n"); - opts->suboptions = 0; - - /* note that e.g. DSS could have written into the memory - * aliased by ahmac, we must reset the field here - * to avoid appending the hmac even for ADD_ADDR echo - * options - */ - opts->ahmac = 0; - *size -= opt_size; - } + pr_debug("drop other suboptions\n"); + opts->suboptions = 0; + *size -= opt_size; opts->addr = addr; opts->suboptions |= OPTION_MPTCP_ADD_ADDR; if (!echo) { @@ -715,6 +698,7 @@ static bool mptcp_established_options_add_addr(struct sock *sk, struct sk_buff * &opts->addr); } else { MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_ECHOADDTX); + opts->ahmac = 0; } pr_debug("addr_id=%d, ahmac=%llu, echo=%d, port=%d\n", opts->addr.id, opts->ahmac, echo, ntohs(opts->addr.port)); diff --git a/net/mptcp/pm.c b/net/mptcp/pm.c index 3e770c7407e1..470501470fe5 100644 --- a/net/mptcp/pm.c +++ b/net/mptcp/pm.c @@ -887,10 +887,9 @@ void mptcp_pm_mp_fail_received(struct sock *sk, u64 fail_seq) } } -bool mptcp_pm_add_addr_signal(struct mptcp_sock *msk, const struct sk_buff *skb, - unsigned int opt_size, unsigned int remaining, - struct mptcp_addr_info *addr, bool *echo, - bool *drop_other_suboptions) +bool mptcp_pm_add_addr_signal(struct mptcp_sock *msk, unsigned int opt_size, + unsigned int remaining, + struct mptcp_addr_info *addr, bool *echo) { bool skip_add_addr = false; int ret = false; @@ -908,10 +907,7 @@ bool mptcp_pm_add_addr_signal(struct mptcp_sock *msk, const struct sk_buff *skb, * plain dup-ack from TCP perspective. The other MPTCP-relevant info, * if any, will be carried by the 'original' TCP ack */ - if (skb && skb_is_tcp_pure_ack(skb)) { - remaining += opt_size; - *drop_other_suboptions = true; - } + remaining += opt_size; *echo = mptcp_pm_should_add_signal_echo(msk); if (*echo) { @@ -929,9 +925,6 @@ bool mptcp_pm_add_addr_signal(struct mptcp_sock *msk, const struct sk_buff *skb, if (remaining < mptcp_add_addr_len(family, *echo, port)) { struct net *net = sock_net((struct sock *)msk); - if (!*drop_other_suboptions) - goto out_unlock; - if (*echo) { MPTCP_INC_STATS(net, MPTCP_MIB_ECHOADDTXDROP); } else { diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h index e4f5aba24da7..b93b878478d2 100644 --- a/net/mptcp/protocol.h +++ b/net/mptcp/protocol.h @@ -1229,10 +1229,9 @@ static inline int mptcp_rm_addr_len(const struct mptcp_rm_list *rm_list) return TCPOLEN_MPTCP_RM_ADDR_BASE + roundup(rm_list->nr - 1, 4) + 1; } -bool mptcp_pm_add_addr_signal(struct mptcp_sock *msk, const struct sk_buff *skb, - unsigned int opt_size, unsigned int remaining, - struct mptcp_addr_info *addr, bool *echo, - bool *drop_other_suboptions); +bool mptcp_pm_add_addr_signal(struct mptcp_sock *msk, unsigned int opt_size, + unsigned int remaining, + struct mptcp_addr_info *addr, bool *echo); bool mptcp_pm_rm_addr_signal(struct mptcp_sock *msk, unsigned int remaining, struct mptcp_rm_list *rm_list); int mptcp_pm_get_local_id(struct mptcp_sock *msk, struct sock_common *skc); From b38cae85d1c45ff189d7ecb6ac36f41cdc3d84d0 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Tue, 2 Jun 2026 11:21:04 +0200 Subject: [PATCH 316/337] net: airoha: Fix use-after-free in metadata dst teardown airoha_metadata_dst_free() runs metadata_dst_free() which frees the metadata_dst with kfree() immediately, bypassing the RCU grace period. In the RX path, skb_dst_set_noref() sets a non-refcounted pointer from the skb to the metadata_dst. This function requires RCU read-side protection and the dst must remain valid until all RCU readers complete. Since metadata_dst_free() calls kfree() directly, an use-after-free can occur if any skb still holds a noref pointer to the dst when the driver tears it down. Replace metadata_dst_free() with dst_release() which properly goes through the refcount path: when the refcount drops to zero, it schedules the actual free via call_rcu_hurry(), ensuring all RCU readers have completed before the memory is freed. Fixes: af3cf757d5c9 ("net: airoha: Move DSA tag in DMA descriptor") Signed-off-by: Lorenzo Bianconi Link: https://patch.msgid.link/20260602-airoha-mtk-metadata-uaf-fix-v1-1-3aaa99d83351@kernel.org Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/airoha/airoha_eth.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c index cecd66251dba..eab6a98d62b9 100644 --- a/drivers/net/ethernet/airoha/airoha_eth.c +++ b/drivers/net/ethernet/airoha/airoha_eth.c @@ -2936,7 +2936,7 @@ static void airoha_metadata_dst_free(struct airoha_gdm_port *port) if (!port->dsa_meta[i]) continue; - metadata_dst_free(port->dsa_meta[i]); + dst_release(&port->dsa_meta[i]->dst); } } From 80df409e1a483676826a6c66e693dba6ac507751 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Tue, 2 Jun 2026 11:21:05 +0200 Subject: [PATCH 317/337] net: ethernet: mtk_eth_soc: Fix use-after-free in metadata dst teardown mtk_free_dev() calls metadata_dst_free() which frees the metadata_dst with kfree() immediately, bypassing the RCU grace period. In the RX path, skb_dst_set_noref() sets a non-refcounted pointer from the skb to the metadata_dst. This function requires RCU read-side protection and the dst must remain valid until all RCU readers complete. Since metadata_dst_free() calls kfree() directly, a use-after-free can occur if any skb still holds a noref pointer to the dst when the driver tears it down. Replace metadata_dst_free() with dst_release() which properly goes through the refcount path: when the refcount drops to zero, it schedules the actual free via call_rcu_hurry(), ensuring all RCU readers have completed before the memory is freed. Fixes: 2d7605a72906 ("net: ethernet: mtk_eth_soc: enable hardware DSA untagging") Signed-off-by: Lorenzo Bianconi Link: https://patch.msgid.link/20260602-airoha-mtk-metadata-uaf-fix-v1-2-3aaa99d83351@kernel.org Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c index 8d225bc9f063..7d771168b990 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c @@ -4491,7 +4491,7 @@ static int mtk_free_dev(struct mtk_eth *eth) for (i = 0; i < ARRAY_SIZE(eth->dsa_meta); i++) { if (!eth->dsa_meta[i]) break; - metadata_dst_free(eth->dsa_meta[i]); + dst_release(ð->dsa_meta[i]->dst); } return 0; From 1231623fd3b5aa6b41cce799ffb0d82e10914be4 Mon Sep 17 00:00:00 2001 From: Antoine Tenart Date: Fri, 29 May 2026 16:47:00 +0200 Subject: [PATCH 318/337] geneve: fix length used in GRO hint UDP checksum adjustment In geneve_post_decap_hint the length used for adjusting the UDP checksum should be 'skb->len - gro_hint->nested_tp_offset' (UDP length) instead of 'skb->len - gro_hint->nested_nh_offset' (IP length). Fixes: fd0dd796576e ("geneve: use GRO hint option in the RX path") Cc: Paolo Abeni Reported-by: Sashiko Closes: https://sashiko.dev/#/patchset/20260521131436.748832-1-jhs%40mojatatu.com Signed-off-by: Antoine Tenart Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260529144713.780938-1-atenart@kernel.org Signed-off-by: Paolo Abeni --- drivers/net/geneve.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c index c6563367d382..715180c3a1b3 100644 --- a/drivers/net/geneve.c +++ b/drivers/net/geneve.c @@ -632,7 +632,7 @@ static int geneve_post_decap_hint(const struct sock *sk, struct sk_buff *skb, uh = udp_hdr(skb); uh->len = htons(skb->len - gro_hint->nested_tp_offset); if (uh->check) { - len = skb->len - gro_hint->nested_nh_offset; + len = skb->len - gro_hint->nested_tp_offset; skb_shinfo(skb)->gso_type |= SKB_GSO_UDP_TUNNEL_CSUM; if (gro_hint->nested_is_v6) uh->check = ~udp_v6_check(len, &ipv6h->saddr, From 0652a3daa78723f955b1ebeb621665ce72bec53e Mon Sep 17 00:00:00 2001 From: Eva Kurchatova Date: Wed, 3 Jun 2026 18:31:42 +0300 Subject: [PATCH 319/337] tracing: Fix CFI violation in probestub being called by tprobes The probestub is a function to allow tprobes to hook to a tracepoint to gain access to its parameters. The function itself is only referenced by the tracepoint structure which lives in the __tracepoint section. objtool explicitly ignores that section and when processing functions in the kernel, if it detects one that has no references it will seal it to have its ENDBR stripped on boot up. This means when a tprobe is attached to the sched_wakeup tracepoint, when it is triggered it will call __probestub_sched_wakeup and due to the missing ENDBR on a CFI-enabled machine it will take a #CP exception. Fix this by adding CFI_NOSEAL annotation to probestub declaration. Cc: stable@vger.kernel.org Acked-by: Masami Hiramatsu (Google) Link: https://patch.msgid.link/20260603153147.573589-1-eva.kurchatova@virtuozzo.com Fixes: d5173f753750 ("objtool: Exclude __tracepoints data from ENDBR checks") Signed-off-by: Eva Kurchatova [ Updated change log ] Signed-off-by: Steven Rostedt --- include/linux/tracepoint.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/include/linux/tracepoint.h b/include/linux/tracepoint.h index 763eea4d80d8..2d2b9f8cdda4 100644 --- a/include/linux/tracepoint.h +++ b/include/linux/tracepoint.h @@ -20,6 +20,7 @@ #include #include #include +#include struct module; struct tracepoint; @@ -389,6 +390,13 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p) void __probestub_##_name(void *__data, proto) \ { \ } \ + /* \ + * Annotate the probestub 'CFI_NOSEAL' to stop objtool from \ + * requesting the kernel remove the ENDBR, because the only \ + * references to the function are in the __tracepoint section, \ + * that objtool doesn't scan. \ + */ \ + CFI_NOSEAL(__probestub_##_name); \ DEFINE_STATIC_CALL(tp_func_##_name, __traceiter_##_name); \ DEFINE_RUST_DO_TRACE(_name, TP_PROTO(proto), TP_ARGS(args)) From a764b0e8317a863006e05732e1aefe821b9d8c2d Mon Sep 17 00:00:00 2001 From: ZhaoJinming Date: Mon, 1 Jun 2026 16:56:49 +0800 Subject: [PATCH 320/337] net: bonding: fix NULL pointer dereference in bond_do_ioctl() In bond_do_ioctl(), slave_dev is obtained via __dev_get_by_name() which can return NULL if the requested interface name does not exist. However, the subsequent slave_dbg() call is placed before the NULL check: slave_dev = __dev_get_by_name(net, ifr->ifr_slave); slave_dbg(bond_dev, slave_dev, "slave_dev=%p:\n", slave_dev); //here if (!slave_dev) return -ENODEV; The slave_dbg() macro expands to netdev_dbg(bond_dev, "(slave %s): " fmt, (slave_dev)->name, ...) which unconditionally dereferences slave_dev->name before the NULL check is performed. This results in a NULL pointer dereference kernel oops when a user calls bonding ioctl (e.g. SIOCBONDENSLAVE, SIOCBONDRELEASE, etc.) with a non-existent slave interface name. This is reachable from userspace via the bonding ioctl interface with CAP_NET_ADMIN capability, making it a potential local denial-of-service vector. Fix by moving the slave_dbg() call after the NULL check. Fixes: e2a7420df2e0 ("bonding/main: convert to using slave printk macros") Cc: stable@vger.kernel.org # v5.2+ Signed-off-by: ZhaoJinming Link: https://patch.msgid.link/20260601085649.4029067-1-zhaojinming@uniontech.com Signed-off-by: Paolo Abeni --- drivers/net/bonding/bond_main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 82e779f7916b..8e75453ce0ef 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -4621,11 +4621,11 @@ static int bond_do_ioctl(struct net_device *bond_dev, struct ifreq *ifr, int cmd slave_dev = __dev_get_by_name(net, ifr->ifr_slave); - slave_dbg(bond_dev, slave_dev, "slave_dev=%p:\n", slave_dev); - if (!slave_dev) return -ENODEV; + slave_dbg(bond_dev, slave_dev, "slave_dev=%p:\n", slave_dev); + switch (cmd) { case SIOCBONDENSLAVE: res = bond_enslave(bond_dev, slave_dev, NULL); From c05fa14db43ebef3bd862ca9d073981c0358b3f0 Mon Sep 17 00:00:00 2001 From: Raf Dickson Date: Tue, 26 May 2026 10:43:56 +0000 Subject: [PATCH 321/337] vsock/vmci: fix sk_ack_backlog leak on failed handshake When vmci_transport_recv_connecting_server() returns an error, vmci_transport_recv_listen() calls vsock_remove_pending() but never calls sk_acceptq_removed(). This leaves sk_ack_backlog incremented permanently. Repeated handshake failures (malformed packets, queue pair alloc failure, event subscribe failure) cause sk_ack_backlog to climb toward sk_max_ack_backlog. Once it reaches the limit the listener permanently refuses all new connections with -ECONNREFUSED, a silent denial of service requiring a process restart to recover. The two existing sk_acceptq_removed() calls in af_vsock.c do not cover this path: line 764 checks vsock_is_pending() which returns false after vsock_remove_pending(), and line 1889 is only reached on successful accept(). Fix by balancing sk_acceptq_added() with sk_acceptq_removed() on the error path. Fixes: d021c344051a ("VSOCK: Introduce VM Sockets") Cc: stable@vger.kernel.org Signed-off-by: Raf Dickson Acked-by: Stefano Garzarella Link: https://patch.msgid.link/20260526104356.469928-1-rafdog35@gmail.com Signed-off-by: Paolo Abeni --- net/vmw_vsock/vmci_transport.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/vmw_vsock/vmci_transport.c b/net/vmw_vsock/vmci_transport.c index 5c1ecd5bfdbc..91516488a742 100644 --- a/net/vmw_vsock/vmci_transport.c +++ b/net/vmw_vsock/vmci_transport.c @@ -980,8 +980,10 @@ static int vmci_transport_recv_listen(struct sock *sk, err = -EINVAL; } - if (err < 0) + if (err < 0) { vsock_remove_pending(sk, pending); + sk_acceptq_removed(sk); + } release_sock(pending); vmci_transport_release_pending(pending); From 899ee91156e57784090c5565e4f31bd7dbffbc5a Mon Sep 17 00:00:00 2001 From: Rajat Gupta Date: Sun, 31 May 2026 08:32:21 -0400 Subject: [PATCH 322/337] net/sched: fix pedit partial COW leading to page cache corruption MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit tcf_pedit_act() computes the COW range for skb_ensure_writable() once before the key loop using tcfp_off_max_hint, but the hint does not account for the runtime header offset added by typed keys. This can leave part of the write region un-COW'd. Fix by moving skb_ensure_writable() inside the per-key loop where the actual write offset is known, and add overflow checking on the offset arithmetic. For negative offsets (e.g. Ethernet header edits at ingress), use skb_cow() to COW the headroom instead. Guard offset_valid() against INT_MIN, where negation is undefined. Fixes: 8b796475fd78 ("net/sched: act_pedit: really ensure the skb is writable") Reported-by: Yiming Qian Reported-by: Keenan Dong Reported-by: Han Guidong <2045gemini@gmail.com> Reported-by: Zhang Cen Reviewed-by: Han Guidong <2045gemini@gmail.com> Tested-by: Han Guidong <2045gemini@gmail.com> Reviewed-by: Davide Caratti Tested-by: Davide Caratti Reviewed-by: Toke Høiland-Jørgensen Tested-by: Toke Høiland-Jørgensen Reviewed-by: Victor Nogueira Tested-by: Victor Nogueira Acked-by: Jamal Hadi Salim Signed-off-by: Rajat Gupta Link: https://patch.msgid.link/20260531123221.48732-1-jhs@mojatatu.com Signed-off-by: Jakub Kicinski --- include/net/tc_act/tc_pedit.h | 1 - net/sched/act_pedit.c | 77 +++++++++++++++++++---------------- 2 files changed, 41 insertions(+), 37 deletions(-) diff --git a/include/net/tc_act/tc_pedit.h b/include/net/tc_act/tc_pedit.h index f58ee15cd858..cb7b82f2cbc7 100644 --- a/include/net/tc_act/tc_pedit.h +++ b/include/net/tc_act/tc_pedit.h @@ -15,7 +15,6 @@ struct tcf_pedit_parms { struct tc_pedit_key *tcfp_keys; struct tcf_pedit_key_ex *tcfp_keys_ex; int action; - u32 tcfp_off_max_hint; unsigned char tcfp_nkeys; unsigned char tcfp_flags; struct rcu_head rcu; diff --git a/net/sched/act_pedit.c b/net/sched/act_pedit.c index bc20f08a2789..bd3b1da3cd63 100644 --- a/net/sched/act_pedit.c +++ b/net/sched/act_pedit.c @@ -16,6 +16,8 @@ #include #include #include +#include +#include #include #include #include @@ -242,7 +244,6 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla, goto out_free_ex; } - nparms->tcfp_off_max_hint = 0; nparms->tcfp_flags = parm->flags; nparms->tcfp_nkeys = parm->nkeys; @@ -268,14 +269,6 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla, BITS_PER_TYPE(int) - 1, nparms->tcfp_keys[i].shift); - /* The AT option can read a single byte, we can bound the actual - * value with uchar max. - */ - cur += (0xff & offmask) >> nparms->tcfp_keys[i].shift; - - /* Each key touches 4 bytes starting from the computed offset */ - nparms->tcfp_off_max_hint = - max(nparms->tcfp_off_max_hint, cur + 4); } p = to_pedit(*a); @@ -318,15 +311,12 @@ static void tcf_pedit_cleanup(struct tc_action *a) call_rcu(&parms->rcu, tcf_pedit_cleanup_rcu); } -static bool offset_valid(struct sk_buff *skb, int offset) +static bool offset_valid(struct sk_buff *skb, int offset, int len) { - if (offset > 0 && offset > skb->len) + if (offset < -(int)skb_headroom(skb)) return false; - if (offset < 0 && -offset > skb_headroom(skb)) - return false; - - return true; + return offset <= (int)skb->len - len; } static int pedit_l4_skb_offset(struct sk_buff *skb, int *hoffset, const int header_type) @@ -393,18 +383,10 @@ TC_INDIRECT_SCOPE int tcf_pedit_act(struct sk_buff *skb, struct tcf_pedit_key_ex *tkey_ex; struct tcf_pedit_parms *parms; struct tc_pedit_key *tkey; - u32 max_offset; int i; parms = rcu_dereference_bh(p->parms); - max_offset = (skb_transport_header_was_set(skb) ? - skb_transport_offset(skb) : - skb_network_offset(skb)) + - parms->tcfp_off_max_hint; - if (skb_ensure_writable(skb, min(skb->len, max_offset))) - goto done; - tcf_lastuse_update(&p->tcf_tm); tcf_action_update_bstats(&p->common, skb); @@ -412,10 +394,11 @@ TC_INDIRECT_SCOPE int tcf_pedit_act(struct sk_buff *skb, tkey_ex = parms->tcfp_keys_ex; for (i = parms->tcfp_nkeys; i > 0; i--, tkey++) { + int write_offset, write_len; int offset = tkey->off; int hoffset = 0; - u32 *ptr, hdata; - u32 val; + u32 cur_val, val; + u32 *ptr; int rc; if (tkey_ex) { @@ -433,13 +416,15 @@ TC_INDIRECT_SCOPE int tcf_pedit_act(struct sk_buff *skb, if (tkey->offmask) { u8 *d, _d; + int at_offset; - if (!offset_valid(skb, hoffset + tkey->at)) { + if (check_add_overflow(hoffset, (int)tkey->at, &at_offset) || + !offset_valid(skb, at_offset, sizeof(_d))) { pr_info_ratelimited("tc action pedit 'at' offset %d out of bounds\n", hoffset + tkey->at); goto bad; } - d = skb_header_pointer(skb, hoffset + tkey->at, + d = skb_header_pointer(skb, at_offset, sizeof(_d), &_d); if (!d) goto bad; @@ -451,31 +436,51 @@ TC_INDIRECT_SCOPE int tcf_pedit_act(struct sk_buff *skb, } } - if (!offset_valid(skb, hoffset + offset)) { - pr_info_ratelimited("tc action pedit offset %d out of bounds\n", hoffset + offset); + if (check_add_overflow(hoffset, offset, &write_offset)) { + pr_info_ratelimited("tc action pedit offset overflow\n"); goto bad; } - ptr = skb_header_pointer(skb, hoffset + offset, - sizeof(hdata), &hdata); - if (!ptr) + if (!offset_valid(skb, write_offset, sizeof(*ptr))) { + pr_info_ratelimited("tc action pedit offset %d out of bounds\n", + write_offset); goto bad; + } + + if (write_offset < 0) { + if (skb_cow(skb, -write_offset)) + goto bad; + if (write_offset + (int)sizeof(*ptr) > 0) { + if (skb_ensure_writable(skb, + min_t(int, skb->len, + write_offset + (int)sizeof(*ptr)))) + goto bad; + } + } else { + if (check_add_overflow(write_offset, (int)sizeof(*ptr), + &write_len)) + goto bad; + if (skb_ensure_writable(skb, min_t(int, skb->len, + write_len))) + goto bad; + } + + ptr = (u32 *)(skb->data + write_offset); + cur_val = get_unaligned(ptr); /* just do it, baby */ switch (cmd) { case TCA_PEDIT_KEY_EX_CMD_SET: val = tkey->val; break; case TCA_PEDIT_KEY_EX_CMD_ADD: - val = (*ptr + tkey->val) & ~tkey->mask; + val = (cur_val + tkey->val) & ~tkey->mask; break; default: pr_info_ratelimited("tc action pedit bad command (%d)\n", cmd); goto bad; } - *ptr = ((*ptr & tkey->mask) ^ val); - if (ptr == &hdata) - skb_store_bits(skb, hoffset + offset, ptr, 4); + put_unaligned((cur_val & tkey->mask) ^ val, ptr); } goto done; From 0861615c28de668669d748ef4eb913ea9262d13b Mon Sep 17 00:00:00 2001 From: Xin Long Date: Mon, 1 Jun 2026 21:06:06 -0400 Subject: [PATCH 323/337] sctp: validate cached peer INIT chunk length in COOKIE_ECHO processing When a listening SCTP server processes a COOKIE_ECHO chunk, the cached peer INIT chunk embedded after the cookie is parsed and its parameters are later walked by sctp_process_init() using sctp_walk_params(). However, the chunk header length of this cached INIT chunk was not validated against the remaining buffer in the COOKIE_ECHO payload. If the length field is inflated, the parameter walk can run beyond the actual received data, leading to out-of-bounds reads and potential memory corruption during later parameter handling (e.g. STATE_COOKIE processing and kmemdup() copies). Add a bounds check in sctp_unpack_cookie() to ensure the cached INIT chunk length does not exceed the available data in the COOKIE_ECHO buffer before it is used. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Reported-by: Brian Geffon Signed-off-by: Xin Long Link: https://patch.msgid.link/eb60825fa22d6f9e663c7d4dbb69f397b5d34d42.1780362366.git.lucien.xin@gmail.com Signed-off-by: Jakub Kicinski --- net/sctp/sm_make_chunk.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index de86ac088289..85264862fb6b 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -1730,6 +1730,7 @@ struct sctp_association *sctp_unpack_cookie( struct sctp_signed_cookie *cookie; struct sk_buff *skb = chunk->skb; struct sctp_cookie *bear_cookie; + struct sctp_chunkhdr *ch; enum sctp_scope scope; unsigned int len; ktime_t kt; @@ -1759,6 +1760,10 @@ struct sctp_association *sctp_unpack_cookie( cookie = chunk->subh.cookie_hdr; bear_cookie = &cookie->c; + ch = (struct sctp_chunkhdr *)(bear_cookie + 1); + if (ntohs(ch->length) > len - fixed_size) + goto malformed; + /* Verify the cookie's MAC, if cookie authentication is enabled. */ if (sctp_sk(ep->base.sk)->cookie_auth_enable) { u8 mac[SHA256_DIGEST_SIZE]; From 1232b3104b4b2c0267f31608fe0f8a8758428f28 Mon Sep 17 00:00:00 2001 From: Zhi Li Date: Tue, 2 Jun 2026 09:45:28 +0800 Subject: [PATCH 324/337] dt-bindings: ethernet: eswin: fix hsp-sp-csr backward compatibility Commit c36069c6f46c ("dt-bindings: ethernet: eswin: add optional TXD and RXD delay register offsets") added two optional cells to eswin,hsp-sp-csr but omitted minItems: 4. As a result, dt-schema implicitly required all 6 cells, which broke backward compatibility with existing 4-cell device trees. Add minItems: 4 to preserve backward compatibility. Fixes: c36069c6f46c ("dt-bindings: ethernet: eswin: add optional TXD and RXD delay register offsets") Reported-by: Sashiko AI Closes: https://lore.kernel.org/all/20260519022334.35742C2BCB7@smtp.kernel.org/ Reviewed-by: Krzysztof Kozlowski Signed-off-by: Zhi Li Link: https://patch.msgid.link/20260602014528.2076-1-lizhi2@eswincomputing.com Signed-off-by: Jakub Kicinski --- Documentation/devicetree/bindings/net/eswin,eic7700-eth.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/net/eswin,eic7700-eth.yaml b/Documentation/devicetree/bindings/net/eswin,eic7700-eth.yaml index b66ae6300faf..65882ff79d8d 100644 --- a/Documentation/devicetree/bindings/net/eswin,eic7700-eth.yaml +++ b/Documentation/devicetree/bindings/net/eswin,eic7700-eth.yaml @@ -84,7 +84,8 @@ properties: This reference is provided for background information only. $ref: /schemas/types.yaml#/definitions/phandle-array items: - - items: + - minItems: 4 + items: - description: Phandle to HSP(High-Speed Peripheral) device - description: Offset of phy control register for internal or external clock selection From 1d31eb27e570daa04f5373345f9ac98c95863be9 Mon Sep 17 00:00:00 2001 From: Nithin Dabilpuram Date: Tue, 2 Jun 2026 10:28:53 +0530 Subject: [PATCH 325/337] octeontx2-af: npc: Fix CPT channel mask in npc_install_flow Use the CPT-aware NIX channel mask in the npc_install_flow path so that when the host PF installs steering rules in kernel for a VF used from userspace (e.g. DPDK), MCAM entries see the same channel mask semantics as other RX paths. Fixes: 56bcef528bd8 ("octeontx2-af: Use npc_install_flow API for promisc and broadcast entries") Cc: Naveen Mamindlapalli Signed-off-by: Nithin Dabilpuram Signed-off-by: Ratheesh Kannoth Link: https://patch.msgid.link/20260602045853.1558530-1-rkannoth@marvell.com Signed-off-by: Jakub Kicinski --- .../net/ethernet/marvell/octeontx2/af/rvu.h | 1 + .../ethernet/marvell/octeontx2/af/rvu_npc.c | 32 +++++++++---------- .../marvell/octeontx2/af/rvu_npc_fs.c | 2 +- 3 files changed, 18 insertions(+), 17 deletions(-) diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu.h b/drivers/net/ethernet/marvell/octeontx2/af/rvu.h index de3fbd3d15d6..65397daae4c2 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu.h @@ -1145,6 +1145,7 @@ int rvu_cpt_lf_teardown(struct rvu *rvu, u16 pcifunc, int blkaddr, int lf, int slot); int rvu_cpt_ctx_flush(struct rvu *rvu, u16 pcifunc); int rvu_cpt_init(struct rvu *rvu); +u32 rvu_get_cpt_chan_mask(struct rvu *rvu); #define NDC_AF_BANK_MASK GENMASK_ULL(7, 0) #define NDC_AF_BANK_LINE_MASK GENMASK_ULL(31, 16) diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c index 6bbda0593fcd..d301a3f0f87a 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c @@ -701,6 +701,19 @@ void npc_set_mcam_action(struct rvu *rvu, struct npc_mcam *mcam, return rvu_write64(rvu, blkaddr, reg, cfg); } +u32 rvu_get_cpt_chan_mask(struct rvu *rvu) +{ + /* For cn10k the upper two bits of the channel number are + * cpt channel number. with masking out these bits in the + * mcam entry, same entry used for NIX will allow packets + * received from cpt for parsing. + */ + if (!is_rvu_otx2(rvu)) + return NIX_CHAN_CPT_X2P_MASK; + else + return 0xFFFu; +} + void rvu_npc_install_ucast_entry(struct rvu *rvu, u16 pcifunc, int nixlf, u64 chan, u8 *mac_addr) { @@ -750,7 +763,7 @@ void rvu_npc_install_ucast_entry(struct rvu *rvu, u16 pcifunc, eth_broadcast_addr((u8 *)&req.mask.dmac); req.features = BIT_ULL(NPC_DMAC); req.channel = chan; - req.chan_mask = 0xFFFU; + req.chan_mask = rvu_get_cpt_chan_mask(rvu); req.intf = pfvf->nix_rx_intf; req.op = action.op; req.hdr.pcifunc = 0; /* AF is requester */ @@ -845,11 +858,7 @@ void rvu_npc_install_promisc_entry(struct rvu *rvu, u16 pcifunc, * mcam entry, same entry used for NIX will allow packets * received from cpt for parsing. */ - if (!is_rvu_otx2(rvu)) { - req.chan_mask = NIX_CHAN_CPT_X2P_MASK; - } else { - req.chan_mask = 0xFFFU; - } + req.chan_mask = rvu_get_cpt_chan_mask(rvu); if (chan_cnt > 1) { if (!is_power_of_2(chan_cnt)) { @@ -1053,16 +1062,7 @@ void rvu_npc_install_allmulti_entry(struct rvu *rvu, u16 pcifunc, int nixlf, ether_addr_copy(req.mask.dmac, mac_addr); req.features = BIT_ULL(NPC_DMAC); - /* For cn10k the upper two bits of the channel number are - * cpt channel number. with masking out these bits in the - * mcam entry, same entry used for NIX will allow packets - * received from cpt for parsing. - */ - if (!is_rvu_otx2(rvu)) - req.chan_mask = NIX_CHAN_CPT_X2P_MASK; - else - req.chan_mask = 0xFFFU; - + req.chan_mask = rvu_get_cpt_chan_mask(rvu); req.channel = chan; req.intf = pfvf->nix_rx_intf; req.entry = index; diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c index 6ae9cdcb608b..34f1e066707b 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c @@ -1820,7 +1820,7 @@ int rvu_mbox_handler_npc_install_flow(struct rvu *rvu, /* ignore chan_mask in case pf func is not AF, revisit later */ if (!is_pffunc_af(req->hdr.pcifunc)) - req->chan_mask = 0xFFF; + req->chan_mask = rvu_get_cpt_chan_mask(rvu); err = npc_check_unsupported_flows(rvu, req->features, req->intf); if (err) { From ab1ecaabe74b7d86c38ab2ab44bd56cdcc33645a Mon Sep 17 00:00:00 2001 From: Justin Lai Date: Tue, 2 Jun 2026 19:46:59 +0800 Subject: [PATCH 326/337] rtase: Reset TX subqueue when clearing TX ring rtase_tx_clear() clears the TX ring and resets the ring indexes. However, the TX queue state and BQL accounting are not reset at the same time. This may leave __QUEUE_STATE_STACK_XOFF asserted after rtase_sw_reset(), preventing new TX packets from being scheduled. Reset the TX subqueue when clearing the TX ring so the TX queue state and BQL accounting are restored together. Fixes: 5a2a2f15244c ("rtase: Implement the rtase_down function") Cc: stable@vger.kernel.org Signed-off-by: Justin Lai Reviewed-by: Alexander Lobakin Link: https://patch.msgid.link/20260602114659.12335-1-justinlai0215@realtek.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/realtek/rtase/rtase_main.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/ethernet/realtek/rtase/rtase_main.c b/drivers/net/ethernet/realtek/rtase/rtase_main.c index ef13109c49cf..6ccbefb5acf2 100644 --- a/drivers/net/ethernet/realtek/rtase/rtase_main.c +++ b/drivers/net/ethernet/realtek/rtase/rtase_main.c @@ -239,6 +239,8 @@ static void rtase_tx_clear(struct rtase_private *tp) rtase_tx_clear_range(ring, ring->dirty_idx, RTASE_NUM_DESC); ring->cur_idx = 0; ring->dirty_idx = 0; + + netdev_tx_reset_subqueue(tp->dev, i); } } From aa6ca1c5c338907817374b59f7551fd855a88754 Mon Sep 17 00:00:00 2001 From: Andy Roulin Date: Tue, 2 Jun 2026 11:51:36 -0700 Subject: [PATCH 327/337] vxlan: vnifilter: send notification on VNI add When a new VNI is added to a vxlan device with vnifilter enabled, no RTM_NEWTUNNEL notification is sent to userspace. This means 'bridge monitor vni' never shows VNI add events, even though VNI delete events are reported correctly. The bug is in vxlan_vni_add(), where the notification is guarded by 'if (changed)'. The 'changed' flag is set by vxlan_vni_update_group() only when the multicast group or remote IP is modified, but for a new VNI added without a group (e.g. in L3 VxLAN interface scenarios), the function returns early without setting changed=true. Since this is a new VNI, the notification should be sent unconditionally. The notification is not guarded by the return value of vxlan_vni_update_group() because, at this point, the VNI has already been inserted into the hash table and list with no rollback on error. The VNI will be visible in 'bridge vni show' regardless, so userspace should be informed. This is consistent with vxlan_vni_del() which also notifies unconditionally. The 'if (changed)' guard remains correct in vxlan_vni_update(), which handles the case where a VNI already exists and is being re-added -- there, we only want to notify if the group/remote actually changed. Reproducer: # ip link add vxlan100 type vxlan dstport 4789 local 10.0.0.1 \ nolearning external vnifilter # ip link set vxlan100 up # bridge monitor vni & # bridge vni add vni 1000 dev vxlan100 # no notification # bridge vni delete vni 1000 dev vxlan100 # notification received Fixes: f9c4bb0b245c ("vxlan: vni filtering support on collect metadata device") Reported-by: Chirag Shah Signed-off-by: Andy Roulin Reviewed-by: Petr Machata Link: https://patch.msgid.link/20260602185138.253265-2-aroulin@nvidia.com Signed-off-by: Jakub Kicinski --- drivers/net/vxlan/vxlan_vnifilter.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/vxlan/vxlan_vnifilter.c b/drivers/net/vxlan/vxlan_vnifilter.c index 2042369379ff..f2a202d46892 100644 --- a/drivers/net/vxlan/vxlan_vnifilter.c +++ b/drivers/net/vxlan/vxlan_vnifilter.c @@ -759,8 +759,7 @@ static int vxlan_vni_add(struct vxlan_dev *vxlan, err = vxlan_vni_update_group(vxlan, vninode, group, true, &changed, extack); - if (changed) - vxlan_vnifilter_notify(vxlan, vninode, RTM_NEWTUNNEL); + vxlan_vnifilter_notify(vxlan, vninode, RTM_NEWTUNNEL); return err; } From 84683b5b60c7274e2c8f7f413d39d78d3db5540f Mon Sep 17 00:00:00 2001 From: Andy Roulin Date: Tue, 2 Jun 2026 11:51:37 -0700 Subject: [PATCH 328/337] vxlan: vnifilter: fix spurious notification on VNI update When a VNI is re-added with the same attributes (e.g. same group or no group), vxlan_vni_update() sends a spurious RTM_NEWTUNNEL notification even though nothing changed. The bug is that 'if (changed)' tests whether the pointer is non-NULL, not the bool value it points to. Since every caller passes a valid pointer, the condition is always true and the notification fires unconditionally. Fix by dereferencing the pointer: 'if (*changed)'. Reproducer: # ip link add vxlan100 type vxlan dstport 4789 local 10.0.0.1 \ nolearning external vnifilter # ip link set vxlan100 up # bridge monitor vni & # bridge vni add vni 1000 dev vxlan100 # bridge vni add vni 1000 dev vxlan100 # spurious notification Fixes: f9c4bb0b245c ("vxlan: vni filtering support on collect metadata device") Signed-off-by: Andy Roulin Reviewed-by: Petr Machata Link: https://patch.msgid.link/20260602185138.253265-3-aroulin@nvidia.com Signed-off-by: Jakub Kicinski --- drivers/net/vxlan/vxlan_vnifilter.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/vxlan/vxlan_vnifilter.c b/drivers/net/vxlan/vxlan_vnifilter.c index f2a202d46892..3e76f4e21094 100644 --- a/drivers/net/vxlan/vxlan_vnifilter.c +++ b/drivers/net/vxlan/vxlan_vnifilter.c @@ -661,7 +661,7 @@ static int vxlan_vni_update(struct vxlan_dev *vxlan, if (ret) return ret; - if (changed) + if (*changed) vxlan_vnifilter_notify(vxlan, vninode, RTM_NEWTUNNEL); return 0; From 8df1c84e7272a5e24b563df7e50111dc81014d4a Mon Sep 17 00:00:00 2001 From: Andy Roulin Date: Tue, 2 Jun 2026 11:51:38 -0700 Subject: [PATCH 329/337] selftests: net: add vxlan vnifilter notification test Add a selftest for VXLAN vnifilter netlink notifications that verifies RTM_NEWTUNNEL and RTM_DELTUNNEL are sent correctly when VNIs are added, deleted, or updated, and that no spurious notifications are sent when a VNI is re-added with the same attributes. Signed-off-by: Andy Roulin Acked-by: Petr Machata Link: https://patch.msgid.link/20260602185138.253265-4-aroulin@nvidia.com Signed-off-by: Jakub Kicinski --- tools/testing/selftests/net/Makefile | 1 + .../net/test_vxlan_vnifilter_notify.sh | 184 ++++++++++++++++++ 2 files changed, 185 insertions(+) create mode 100755 tools/testing/selftests/net/test_vxlan_vnifilter_notify.sh diff --git a/tools/testing/selftests/net/Makefile b/tools/testing/selftests/net/Makefile index f3da38c54d27..2ed7d803eb54 100644 --- a/tools/testing/selftests/net/Makefile +++ b/tools/testing/selftests/net/Makefile @@ -109,6 +109,7 @@ TEST_PROGS := \ test_vxlan_nh.sh \ test_vxlan_nolocalbypass.sh \ test_vxlan_under_vrf.sh \ + test_vxlan_vnifilter_notify.sh \ test_vxlan_vnifiltering.sh \ tfo_passive.sh \ traceroute.sh \ diff --git a/tools/testing/selftests/net/test_vxlan_vnifilter_notify.sh b/tools/testing/selftests/net/test_vxlan_vnifilter_notify.sh new file mode 100755 index 000000000000..9d51a9e02ae0 --- /dev/null +++ b/tools/testing/selftests/net/test_vxlan_vnifilter_notify.sh @@ -0,0 +1,184 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 +# shellcheck disable=SC2034,SC2154,SC2317,SC2329 +# +# Test for VXLAN vnifilter netlink notifications (RTM_NEWTUNNEL / +# RTM_DELTUNNEL). +# +# Verifies that: +# - Adding a new VNI sends a notification +# - Adding a new VNI with a remote sends a notification +# - Deleting a VNI sends a notification +# - Re-adding an existing VNI with the same attributes does not send +# a spurious notification +# - Updating an existing VNI's remote sends a notification +# - Deleting a non-existent VNI does not send a notification + +source lib.sh + +require_command bridge + +VXLAN_DEV=vxlan100 + +ALL_TESTS=" + test_vni_add_notify + test_vni_add_remote_notify + test_vni_del_notify + test_vni_readd_no_notify + test_vni_update_remote_notify + test_vni_del_nonexistent_no_notify +" + +setup_prepare() +{ + setup_ns NS1 + defer cleanup_all_ns + + ip -n "$NS1" link add $VXLAN_DEV type vxlan dstport 4789 \ + local 10.0.0.1 nolearning external vnifilter + ip -n "$NS1" link set $VXLAN_DEV up +} + +# Run bridge monitor in the background, execute a command, then count +# the notification lines. +# Usage: vni_notify_check [args...] +# Sets: NOTIFY_COUNT with the number of notifications observed. +vni_notify_check() +{ + local tmpf cmd_ret monitor_pid + + tmpf=$(mktemp) + defer rm "$tmpf" + + defer_scope_push + ip netns exec "$NS1" bridge monitor vni > "$tmpf" 2>/dev/null & + monitor_pid=$! + defer kill_process "$monitor_pid" + + sleep 0.5 + if [ ! -e "/proc/$monitor_pid" ]; then + RET=$ksft_skip + log_test "iproute2 'bridge monitor vni' not supported" + return "$RET" + fi + + "$@" + cmd_ret=$? + sleep 0.2 + defer_scope_pop + + NOTIFY_COUNT=$(grep -c "$VXLAN_DEV" "$tmpf") + NOTIFY_COUNT=${NOTIFY_COUNT:-0} + return "$cmd_ret" +} + +# Adding a brand new VNI should produce a notification. +test_vni_add_notify() +{ + RET=0 + + vni_notify_check \ + bridge -n "$NS1" vni add vni 1000 dev "$VXLAN_DEV" + check_err $? "Failed to add VNI" + + [ "$NOTIFY_COUNT" -eq 1 ] + check_err $? "Expected 1 notification for VNI add, got $NOTIFY_COUNT" + + bridge -n "$NS1" vni delete vni 1000 dev "$VXLAN_DEV" 2>/dev/null + + log_test "VNI add sends notification" +} + +# Adding a VNI with a remote should produce a notification. +test_vni_add_remote_notify() +{ + RET=0 + + vni_notify_check \ + bridge -n "$NS1" vni add vni 4000 remote 10.0.0.2 dev "$VXLAN_DEV" + check_err $? "Failed to add VNI with remote" + + [ "$NOTIFY_COUNT" -eq 1 ] + check_err $? "Expected 1 notification for VNI add with remote, got $NOTIFY_COUNT" + + bridge -n "$NS1" vni delete vni 4000 dev "$VXLAN_DEV" + + log_test "VNI add with remote sends notification" +} + +# Deleting a VNI should produce a notification. +test_vni_del_notify() +{ + RET=0 + + bridge -n "$NS1" vni add vni 2000 dev "$VXLAN_DEV" + + vni_notify_check \ + bridge -n "$NS1" vni delete vni 2000 dev "$VXLAN_DEV" + check_err $? "Failed to delete VNI" + + [ "$NOTIFY_COUNT" -eq 1 ] + check_err $? "Expected 1 notification for VNI del, got $NOTIFY_COUNT" + + log_test "VNI delete sends notification" +} + +# Re-adding an existing VNI with the same attributes should not produce +# a notification. +test_vni_readd_no_notify() +{ + RET=0 + + bridge -n "$NS1" vni add vni 3000 dev "$VXLAN_DEV" + + vni_notify_check \ + bridge -n "$NS1" vni add vni 3000 dev "$VXLAN_DEV" + check_err $? "Failed to re-add VNI" + + [ "$NOTIFY_COUNT" -eq 0 ] + check_err $? "Expected 0 notifications for VNI re-add, got $NOTIFY_COUNT" + + bridge -n "$NS1" vni delete vni 3000 dev "$VXLAN_DEV" + + log_test "VNI re-add does not send spurious notification" +} + +# Updating an existing VNI's remote should produce a notification. +test_vni_update_remote_notify() +{ + RET=0 + + bridge -n "$NS1" vni add vni 5000 remote 10.0.0.2 dev "$VXLAN_DEV" + + vni_notify_check \ + bridge -n "$NS1" vni add vni 5000 remote 10.0.0.3 dev "$VXLAN_DEV" + check_err $? "Failed to update VNI remote" + + [ "$NOTIFY_COUNT" -eq 1 ] + check_err $? "Expected 1 notification for VNI remote update, got $NOTIFY_COUNT" + + bridge -n "$NS1" vni delete vni 5000 dev "$VXLAN_DEV" + + log_test "VNI remote update sends notification" +} + +# Deleting a non-existent VNI should not produce a notification. +test_vni_del_nonexistent_no_notify() +{ + RET=0 + + vni_notify_check \ + bridge -n "$NS1" vni delete vni 9999 dev "$VXLAN_DEV" 2>/dev/null + + [ "$NOTIFY_COUNT" -eq 0 ] + check_err $? "Expected 0 notifications for non-existent VNI del, got $NOTIFY_COUNT" + + log_test "Non-existent VNI delete does not send notification" +} + +trap defer_scopes_cleanup EXIT + +setup_prepare +tests_run + +exit "$EXIT_STATUS" From 791c91dc7a9dfb2457d5e29b8216a6484b9c4b40 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Wed, 3 Jun 2026 13:18:11 +0300 Subject: [PATCH 330/337] ipv6: mcast: Fix use-after-free when processing MLD queries When processing an MLD query, a pointer to the multicast group address is retrieved when initially parsing the packet. This pointer is later dereferenced without being reloaded despite the fact that the skb header might have been reallocated following the pskb_may_pull() calls, leading to a use-after-free [1]. Fix by copying the multicast group address when the packet is initially parsed. [1] BUG: KASAN: slab-use-after-free in __mld_query_work (net/ipv6/mcast.c:1512) Read of size 8 at addr ffff8881154b8e90 by task kworker/4:1/118 Workqueue: mld mld_query_work Call Trace: dump_stack_lvl (lib/dump_stack.c:94 lib/dump_stack.c:120) print_address_description.constprop.0 (mm/kasan/report.c:378) print_report (mm/kasan/report.c:482) kasan_report (mm/kasan/report.c:595) __mld_query_work (net/ipv6/mcast.c:1512) mld_query_work (net/ipv6/mcast.c:1563) process_one_work (kernel/workqueue.c:3314) worker_thread (kernel/workqueue.c:3397 kernel/workqueue.c:3478) kthread (kernel/kthread.c:436) ret_from_fork (arch/x86/kernel/process.c:158) ret_from_fork_asm (arch/x86/entry/entry_64.S:245) [...] Freed by task 118: kasan_save_stack (mm/kasan/common.c:57) kasan_save_track (mm/kasan/common.c:78) kasan_save_free_info (mm/kasan/generic.c:584) __kasan_slab_free (mm/kasan/common.c:253 mm/kasan/common.c:285) kfree (./include/linux/kasan.h:235 mm/slub.c:2689 mm/slub.c:6251 mm/slub.c:6566) pskb_expand_head (net/core/skbuff.c:2335) __pskb_pull_tail (net/core/skbuff.c:2878 (discriminator 4)) __mld_query_work (net/ipv6/mcast.c:1495 (discriminator 1)) mld_query_work (net/ipv6/mcast.c:1563) process_one_work (kernel/workqueue.c:3314) worker_thread (kernel/workqueue.c:3397 kernel/workqueue.c:3478) kthread (kernel/kthread.c:436) ret_from_fork (arch/x86/kernel/process.c:158) ret_from_fork_asm (arch/x86/entry/entry_64.S:245) Fixes: 97300b5fdfe2 ("[MCAST] IPv6: Check packet size when process Multicast") Reported-by: Leo Lin Reviewed-by: David Ahern Signed-off-by: Ido Schimmel Reviewed-by: Eric Dumazet Reviewed-by: Jiayuan Chen Link: https://patch.msgid.link/20260603101811.612594-1-idosch@nvidia.com Signed-off-by: Jakub Kicinski --- net/ipv6/mcast.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c index 3330adcf26db..d9b855d5191b 100644 --- a/net/ipv6/mcast.c +++ b/net/ipv6/mcast.c @@ -1424,9 +1424,9 @@ void igmp6_event_query(struct sk_buff *skb) static void __mld_query_work(struct sk_buff *skb) { struct mld2_query *mlh2 = NULL; - const struct in6_addr *group; unsigned long max_delay; struct inet6_dev *idev; + struct in6_addr group; struct ifmcaddr6 *ma; struct mld_msg *mld; int group_type; @@ -1458,8 +1458,8 @@ static void __mld_query_work(struct sk_buff *skb) goto kfree_skb; mld = (struct mld_msg *)icmp6_hdr(skb); - group = &mld->mld_mca; - group_type = ipv6_addr_type(group); + group = mld->mld_mca; + group_type = ipv6_addr_type(&group); if (group_type != IPV6_ADDR_ANY && !(group_type&IPV6_ADDR_MULTICAST)) @@ -1509,7 +1509,7 @@ static void __mld_query_work(struct sk_buff *skb) } } else { for_each_mc_mclock(idev, ma) { - if (!ipv6_addr_equal(group, &ma->mca_addr)) + if (!ipv6_addr_equal(&group, &ma->mca_addr)) continue; if (ma->mca_flags & MAF_TIMER_RUNNING) { /* gsquery <- gsquery && mark */ From 3a5f3f7aff18bcc36a57839cf50cf0cc8de707f3 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 3 Jun 2026 07:29:55 +0000 Subject: [PATCH 331/337] ieee802154: 6lowpan: only accept IPv6 packets in lowpan_xmit() The aoe driver (or similar) generates a non-IPv6 packet (e.g., ETH_P_AOE) and queues it for transmission via dev_queue_xmit() on a 6LoWPAN interface (configured by the user or test case). Since the packet is not IPv6, the 6LoWPAN header_ops->create function (lowpan_header_create or header_create) returns early without initializing the lowpan_addr_info structure in the skb headroom. In the transmit function (lowpan_xmit), the driver calls lowpan_header (or setup_header) which unconditionally copies and uses the lowpan_addr_info from the headroom, which contains uninitialized data. Fix this by dropping non IPv6 packets. A similar fix is needed in net/bluetooth/6lowpan.c bt_xmit(). Fixes: 4dc315e267fe ("ieee802154: 6lowpan: move transmit functionality") Reported-by: syzbot+f13c19f75e1097abd116@syzkaller.appspotmail.com Closes: https://lore.kernel.org/netdev/6a1fd763.278b5b03.2bcf39.0049.GAE@google.com/T/#u Signed-off-by: Eric Dumazet Reviewed-by: Miquel Raynal Link: https://patch.msgid.link/20260603072955.4032221-1-edumazet@google.com Signed-off-by: Jakub Kicinski --- net/ieee802154/6lowpan/tx.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/net/ieee802154/6lowpan/tx.c b/net/ieee802154/6lowpan/tx.c index 0c07662b44c0..4df76ff50699 100644 --- a/net/ieee802154/6lowpan/tx.c +++ b/net/ieee802154/6lowpan/tx.c @@ -255,6 +255,11 @@ netdev_tx_t lowpan_xmit(struct sk_buff *skb, struct net_device *ldev) pr_debug("package xmit\n"); + if (skb->protocol != htons(ETH_P_IPV6)) { + kfree_skb(skb); + return NET_XMIT_DROP; + } + WARN_ON_ONCE(skb->len > IPV6_MIN_MTU); /* We must take a copy of the skb before we modify/replace the ipv6 From 9fc237f8d49f06d05f0f8e80361047b718894e81 Mon Sep 17 00:00:00 2001 From: Justin Lai Date: Wed, 3 Jun 2026 14:18:16 +0800 Subject: [PATCH 332/337] rtase: Avoid sleeping in get_stats64() The .ndo_get_stats64 callback must not sleep because it can be called when reading /proc/net/dev. rtase_get_stats64() calls rtase_dump_tally_counter(), which polls the tally counter dump bit with read_poll_timeout(). This may sleep while waiting for the hardware counter dump to complete. Use read_poll_timeout_atomic() instead to avoid sleeping in the get_stats64() path. Fixes: 079600489960 ("rtase: Implement net_device_ops") Cc: stable@vger.kernel.org Signed-off-by: Justin Lai Link: https://patch.msgid.link/20260603061816.31356-1-justinlai0215@realtek.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/realtek/rtase/rtase_main.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/realtek/rtase/rtase_main.c b/drivers/net/ethernet/realtek/rtase/rtase_main.c index 6ccbefb5acf2..55105d34bc79 100644 --- a/drivers/net/ethernet/realtek/rtase/rtase_main.c +++ b/drivers/net/ethernet/realtek/rtase/rtase_main.c @@ -1565,8 +1565,9 @@ static void rtase_dump_tally_counter(const struct rtase_private *tp) rtase_w32(tp, RTASE_DTCCR0, cmd); rtase_w32(tp, RTASE_DTCCR0, cmd | RTASE_COUNTER_DUMP); - err = read_poll_timeout(rtase_r32, val, !(val & RTASE_COUNTER_DUMP), - 10, 250, false, tp, RTASE_DTCCR0); + err = read_poll_timeout_atomic(rtase_r32, val, + !(val & RTASE_COUNTER_DUMP), + 10, 250, false, tp, RTASE_DTCCR0); if (err == -ETIMEDOUT) netdev_err(tp->dev, "error occurred in dump tally counter\n"); From 7561c7fbc694308da73300f036719e63e42bf0b4 Mon Sep 17 00:00:00 2001 From: Yizhou Zhao Date: Wed, 3 Jun 2026 14:00:13 +0800 Subject: [PATCH 333/337] net/802/mrp: fix vector attribute parsing in mrp_pdu_parse_vecattr In mrp_pdu_parse_vecattr(), vector attribute events are encoded three per byte and valen tracks the number of events left to process. The parser decrements valen after processing the first and second events from each event byte, but not after processing the third one. When valen is exactly a multiple of three, the loop continues after the last valid event and consumes the next byte as a new event byte, applying a spurious event to the MRP applicant state. Additionally, when valen is zero the parser unconditionally consumes attrlen bytes as FirstValue and advances the offset, even though per IEEE 802.1ak a VectorAttribute with only a LeaveAllEvent has valen of zero and no FirstValue or Vector fields. This corrupts the offset for subsequent PDU parsing. Also, when valen exceeds three the loop crosses byte boundaries but the attribute value is not incremented between the last event of one byte and the first event of the next. This causes the first event of the next byte to use the same attribute value as the third event rather than the next consecutive value. Decrement valen after processing the third event, skip FirstValue consumption when valen is zero, and increment the attribute value at the end of each loop iteration. Fixes: febf018d2234 ("net/802: Implement Multiple Registration Protocol (MRP)") Reported-by: Yizhou Zhao Reported-by: Yuxiang Yang Reported-by: Ao Wang Reported-by: Xuewei Feng Reported-by: Qi Li Reported-by: Ke Xu Signed-off-by: Yizhou Zhao Link: https://patch.msgid.link/20260603060016.21522-1-zhaoyz24@mails.tsinghua.edu.cn Signed-off-by: Jakub Kicinski --- net/802/mrp.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/net/802/mrp.c b/net/802/mrp.c index ff0e80574e6b..160a3b14569c 100644 --- a/net/802/mrp.c +++ b/net/802/mrp.c @@ -703,6 +703,12 @@ static int mrp_pdu_parse_vecattr(struct mrp_applicant *app, valen = be16_to_cpu(get_unaligned(&mrp_cb(skb)->vah->lenflags) & MRP_VECATTR_HDR_LEN_MASK); + /* If valen is 0, only a LeaveAllEvent is present; FirstValue and + * Vector fields are absent per IEEE 802.1ak. + */ + if (valen == 0) + return 0; + /* The VectorAttribute structure in a PDU carries event information * about one or more attributes having consecutive values. Only the * value for the first attribute is contained in the structure. So @@ -753,6 +759,9 @@ static int mrp_pdu_parse_vecattr(struct mrp_applicant *app, vaevents %= __MRP_VECATTR_EVENT_MAX; vaevent = vaevents; mrp_pdu_parse_vecattr_event(app, skb, vaevent); + valen--; + mrp_attrvalue_inc(mrp_cb(skb)->attrvalue, + mrp_cb(skb)->mh->attrlen); } return 0; } From b47ff80f280e18ad2310f44293cc057d9b64ff11 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 3 Jun 2026 12:35:14 +0000 Subject: [PATCH 334/337] bonding: annotate data-races arcound churn variables These fields are updated asynchronously by the bonding state machine in ad_churn_machine() while holding bond->mode_lock. bond_info_show_slave() and bond_fill_slave_info() read them without bond->mode_lock being held, we need to add READ_ONCE() and WRITE_ONCE() annotations. Note that AD_CHURN_MONITOR, AD_CHURN, and AD_NO_CHURN are defined exclusively in (kernel private) include/net/bond_3ad.h header. They should be moved to include/uapi/linux/if_bonding.h or userspace tools will have to hardcode their values. Fixes: 4916f2e2f3fc ("bonding: print churn state via netlink") Fixes: 14c9551a32eb ("bonding: Implement port churn-machine (AD standard 43.4.17).") Signed-off-by: Eric Dumazet Link: https://patch.msgid.link/20260603123514.388226-1-edumazet@google.com Signed-off-by: Jakub Kicinski --- drivers/net/bonding/bond_3ad.c | 18 ++++++++++-------- drivers/net/bonding/bond_netlink.c | 4 ++-- drivers/net/bonding/bond_procfs.c | 8 ++++---- 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/drivers/net/bonding/bond_3ad.c b/drivers/net/bonding/bond_3ad.c index f0aa7d2f2171..985ef66dc333 100644 --- a/drivers/net/bonding/bond_3ad.c +++ b/drivers/net/bonding/bond_3ad.c @@ -1386,8 +1386,8 @@ static void ad_churn_machine(struct port *port) { if (port->sm_vars & AD_PORT_CHURNED) { port->sm_vars &= ~AD_PORT_CHURNED; - port->sm_churn_actor_state = AD_CHURN_MONITOR; - port->sm_churn_partner_state = AD_CHURN_MONITOR; + WRITE_ONCE(port->sm_churn_actor_state, AD_CHURN_MONITOR); + WRITE_ONCE(port->sm_churn_partner_state, AD_CHURN_MONITOR); port->sm_churn_actor_timer_counter = __ad_timer_to_ticks(AD_ACTOR_CHURN_TIMER, 0); port->sm_churn_partner_timer_counter = @@ -1398,20 +1398,22 @@ static void ad_churn_machine(struct port *port) !(--port->sm_churn_actor_timer_counter) && port->sm_churn_actor_state == AD_CHURN_MONITOR) { if (port->actor_oper_port_state & LACP_STATE_SYNCHRONIZATION) { - port->sm_churn_actor_state = AD_NO_CHURN; + WRITE_ONCE(port->sm_churn_actor_state, AD_NO_CHURN); } else { - port->churn_actor_count++; - port->sm_churn_actor_state = AD_CHURN; + WRITE_ONCE(port->churn_actor_count, + port->churn_actor_count + 1); + WRITE_ONCE(port->sm_churn_actor_state, AD_CHURN); } } if (port->sm_churn_partner_timer_counter && !(--port->sm_churn_partner_timer_counter) && port->sm_churn_partner_state == AD_CHURN_MONITOR) { if (port->partner_oper.port_state & LACP_STATE_SYNCHRONIZATION) { - port->sm_churn_partner_state = AD_NO_CHURN; + WRITE_ONCE(port->sm_churn_partner_state, AD_NO_CHURN); } else { - port->churn_partner_count++; - port->sm_churn_partner_state = AD_CHURN; + WRITE_ONCE(port->churn_partner_count, + port->churn_partner_count + 1); + WRITE_ONCE(port->sm_churn_partner_state, AD_CHURN); } } } diff --git a/drivers/net/bonding/bond_netlink.c b/drivers/net/bonding/bond_netlink.c index c7d3e0602c83..90365d3f7ebf 100644 --- a/drivers/net/bonding/bond_netlink.c +++ b/drivers/net/bonding/bond_netlink.c @@ -82,10 +82,10 @@ static int bond_fill_slave_info(struct sk_buff *skb, goto nla_put_failure_rcu; if (nla_put_u8(skb, IFLA_BOND_SLAVE_AD_CHURN_ACTOR_STATE, - ad_port->sm_churn_actor_state)) + READ_ONCE(ad_port->sm_churn_actor_state))) goto nla_put_failure_rcu; if (nla_put_u8(skb, IFLA_BOND_SLAVE_AD_CHURN_PARTNER_STATE, - ad_port->sm_churn_partner_state)) + READ_ONCE(ad_port->sm_churn_partner_state))) goto nla_put_failure_rcu; } rcu_read_unlock(); diff --git a/drivers/net/bonding/bond_procfs.c b/drivers/net/bonding/bond_procfs.c index 3714aab1a3d9..3607b62f9b63 100644 --- a/drivers/net/bonding/bond_procfs.c +++ b/drivers/net/bonding/bond_procfs.c @@ -221,13 +221,13 @@ static void bond_info_show_slave(struct seq_file *seq, seq_printf(seq, "Aggregator ID: %d\n", agg->aggregator_identifier); seq_printf(seq, "Actor Churn State: %s\n", - bond_3ad_churn_desc(port->sm_churn_actor_state)); + bond_3ad_churn_desc(READ_ONCE(port->sm_churn_actor_state))); seq_printf(seq, "Partner Churn State: %s\n", - bond_3ad_churn_desc(port->sm_churn_partner_state)); + bond_3ad_churn_desc(READ_ONCE(port->sm_churn_partner_state))); seq_printf(seq, "Actor Churned Count: %d\n", - port->churn_actor_count); + READ_ONCE(port->churn_actor_count)); seq_printf(seq, "Partner Churned Count: %d\n", - port->churn_partner_count); + READ_ONCE(port->churn_partner_count)); if (capable(CAP_NET_ADMIN)) { seq_puts(seq, "details actor lacp pdu:\n"); From e374b22e9b07b72a25909621464ff74096151bfb Mon Sep 17 00:00:00 2001 From: Xin Long Date: Wed, 3 Jun 2026 14:11:44 -0400 Subject: [PATCH 335/337] sctp: purge outqueue on stale COOKIE-ECHO handling sctp_stream_update() is only invoked when the association is moved into COOKIE_WAIT during association setup/reconfiguration. In this path, the outbound stream scheduler state (stream->out_curr) is expected to be clean, since no user data should have been transmitted yet unless the state machine has already partially progressed. However, a corner case exists in sctp_sf_do_5_2_6_stale(): when a Stale Cookie ERROR is received, the association is rolled back from COOKIE_ECHOED to COOKIE_WAIT. In this scenario, user data may already have been queued and even bundled with the COOKIE-ECHO chunk. During the rollback, sctp_stream_update() frees the old stream table and installs a new one, but it does not invalidate stream->out_curr. As a result, out_curr may still point to a freed sctp_stream_out entry from the previous stream state. Later, SCTP scheduler dequeue paths (FCFS, RR, PRIO, etc.) rely on stream->out_curr->ext, which can lead to use-after-free once the old stream state has been released via sctp_stream_free(). This results in crashes such as (reported by Yuqi): BUG: KASAN: slab-use-after-free in sctp_sched_fcfs_dequeue+0x13a/0x140 Read of size 8 at addr ff1100004d4d3208 by task mini_poc/9312 CPU: 1 UID: 1001 PID: 9312 Comm: mini_poc Not tainted 7.1.0-rc1-00305-gbd3a4795d574 #5 PREEMPT(full) sctp_sched_fcfs_dequeue+0x13a/0x140 sctp_outq_flush+0x1603/0x33e0 sctp_do_sm+0x31c9/0x5d30 sctp_assoc_bh_rcv+0x392/0x6f0 sctp_inq_push+0x1db/0x270 sctp_rcv+0x138d/0x3c10 Fix this by fully purging the association outqueue when handling the Stale Cookie case. This ensures all pending transmit and retransmit state is dropped, and any scheduler cached pointers are invalidated, making it safe to rebuild stream state during COOKIE_WAIT restart. Updating only stream->out_curr would be insufficient, since queued and retransmittable data would still reference the old stream state and trigger later use-after-free in dequeue paths. Fixes: 5bbbbe32a431 ("sctp: introduce stream scheduler foundations") Reported-by: Yuan Tan Reported-by: Yifan Wu Reported-by: Juefei Pu Reported-by: Zhengchuan Liang Reported-by: Xin Liu Reported-by: Yuqi Xu Reported-by: Ren Wei Signed-off-by: Xin Long Link: https://patch.msgid.link/94318159b9052907a6cbb7256aee8b5f8dfbfccb.1780510304.git.lucien.xin@gmail.com Signed-off-by: Jakub Kicinski --- net/sctp/sm_statefuns.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index 8e89a870780c..9b23c11cbb9e 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -2598,11 +2598,7 @@ static enum sctp_disposition sctp_sf_do_5_2_6_stale( */ sctp_add_cmd_sf(commands, SCTP_CMD_DEL_NON_PRIMARY, SCTP_NULL()); - /* If we've sent any data bundled with COOKIE-ECHO we will need to - * resend - */ - sctp_add_cmd_sf(commands, SCTP_CMD_T1_RETRAN, - SCTP_TRANSPORT(asoc->peer.primary_path)); + sctp_add_cmd_sf(commands, SCTP_CMD_PURGE_OUTQUEUE, SCTP_NULL()); /* Cast away the const modifier, as we want to just * rerun it through as a sideffect. From 3c94f241f776562c489876ff506f366224565c21 Mon Sep 17 00:00:00 2001 From: Sechang Lim Date: Wed, 3 Jun 2026 16:27:33 +0000 Subject: [PATCH 336/337] udp: clear skb->dev before running a sockmap verdict On the UDP receive path skb->dev is repurposed as dev_scratch (the truesize/state cache set by udp_set_dev_scratch()), through the union { struct net_device *dev; unsigned long dev_scratch; } in sk_buff. When a UDP socket is in a sockmap, sk_data_ready is sk_psock_verdict_data_ready(), which calls udp_read_skb() -> recv_actor() (sk_psock_verdict_recv) to run the attached SK_SKB verdict program in softirq. If that program calls a socket-lookup helper (bpf_sk_lookup_tcp/udp, bpf_skc_lookup_tcp), bpf_skc_lookup() does: if (skb->dev) caller_net = dev_net(skb->dev); skb->dev still holds the dev_scratch value (a non-NULL integer), so dev_net() dereferences it as a struct net_device * and the kernel takes a general protection fault on a non-canonical address in softirq: Oops: general protection fault, probably for non-canonical address 0x1010000800004a0 CPU: 1 UID: 0 PID: 1406 Comm: syz.2.19 Not tainted 7.1.0-rc6 #1 PREEMPT(full) RIP: 0010:bpf_skc_lookup net/core/filter.c:7033 [inline] RIP: 0010:bpf_sk_lookup+0x45/0x160 net/core/filter.c:7047 Call Trace: bpf_prog_4675cb904b7071f8+0x12e/0x14e bpf_prog_run_pin_on_cpu+0xc6/0x1f0 sk_psock_verdict_recv+0x1ba/0x350 udp_read_skb+0x31a/0x370 sk_psock_verdict_data_ready+0x2e3/0x600 __udp_enqueue_schedule_skb+0x4c8/0x650 udpv6_queue_rcv_one_skb+0x3ec/0x740 udp6_unicast_rcv_skb+0x11d/0x140 ip6_protocol_deliver_rcu+0x61e/0x950 ip6_input_finish+0xa9/0x150 NF_HOOK+0x286/0x2f0 ip6_input+0x117/0x220 NF_HOOK+0x286/0x2f0 __netif_receive_skb+0x85/0x200 process_backlog+0x374/0x9a0 __napi_poll+0x4f/0x1c0 net_rx_action+0x3b0/0x770 handle_softirqs+0x15a/0x460 do_softirq+0x57/0x80 The rmem charge that dev_scratch accounted for is released by skb_recv_udp() on dequeue, just above, so the scratch is dead by the time recv_actor() runs. Clear skb->dev so bpf_skc_lookup() falls back to sock_net(skb->sk), which skb_set_owner_sk_safe() set just above. Fixes: 965b57b469a5 ("net: Introduce a new proto_ops ->read_skb()") Cc: stable@vger.kernel.org Signed-off-by: Sechang Lim Reviewed-by: Jiayuan Chen Reviewed-by: Eric Dumazet Link: https://patch.msgid.link/20260603162737.697215-1-rhkrqnwk98@gmail.com Signed-off-by: Jakub Kicinski --- net/ipv4/udp.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 0ac2bf4f8759..70f6cbd4ef73 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -2011,6 +2011,14 @@ int udp_read_skb(struct sock *sk, skb_read_actor_t recv_actor) } WARN_ON_ONCE(!skb_set_owner_sk_safe(skb, sk)); + + /* + * skb->dev still aliases the UDP rx dev_scratch (its charge was freed + * on dequeue above); a sockmap verdict program may deref it via + * bpf_sk_lookup_*(), so clear it -> bpf_skc_lookup() uses skb->sk + */ + skb->dev = NULL; + return recv_actor(sk, skb); } From b6197b386677ae5268d4702e23849d9ad53051ad Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Wed, 3 Jun 2026 12:58:45 -0700 Subject: [PATCH 337/337] Reapply "bnxt_en: bring back rtnl_lock() in the bnxt_open() path" This reverts commit 850d9248d2eac662f869c766a598c877690c74e5. This reapplies commit 325eb217e41f ("bnxt_en: bring back rtnl_lock() in the bnxt_open() path"). Breno reports a lockdep warning in bnxt. During FW reset the driver may end up calling netif_set_real_num_tx_queues() (if queue count changes), so calls to bnxt_open() still require rtnl_lock. net/sched/sch_generic.c:1416 suspicious rcu_dereference_protected() usage! dev_qdisc_change_real_num_tx+0x54/0xe0 netif_set_real_num_tx_queues+0x4ed/0xa80 __bnxt_open_nic+0x9cb/0x3490 bnxt_open+0x1cb/0x370 bnxt_fw_reset_task+0x80d/0x1e80 process_scheduled_works+0x9c1/0x13b0 The reverted commit was just an optimization / experiment so let's go back to taking the lock. Reported-by: Breno Leitao Link: https://lore.kernel.org/ah726OtFX-Qw3U-R@gmail.com Fixes: 850d9248d2ea ("Revert "bnxt_en: bring back rtnl_lock() in the bnxt_open() path"") Acked-by: Stanislav Fomichev Reviewed-by: Michael Chan Reviewed-by: Breno Leitao Link: https://patch.msgid.link/20260603195845.2574426-1-kuba@kernel.org Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 36 ++++++++++++++++++----- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 008c34cff7b4..35e1f8f663c7 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -14388,13 +14388,28 @@ static void bnxt_unlock_sp(struct bnxt *bp) netdev_unlock(bp->dev); } +/* Same as bnxt_lock_sp() with additional rtnl_lock */ +static void bnxt_rtnl_lock_sp(struct bnxt *bp) +{ + clear_bit(BNXT_STATE_IN_SP_TASK, &bp->state); + rtnl_lock(); + netdev_lock(bp->dev); +} + +static void bnxt_rtnl_unlock_sp(struct bnxt *bp) +{ + set_bit(BNXT_STATE_IN_SP_TASK, &bp->state); + netdev_unlock(bp->dev); + rtnl_unlock(); +} + /* Only called from bnxt_sp_task() */ static void bnxt_reset(struct bnxt *bp, bool silent) { - bnxt_lock_sp(bp); + bnxt_rtnl_lock_sp(bp); if (test_bit(BNXT_STATE_OPEN, &bp->state)) bnxt_reset_task(bp, silent); - bnxt_unlock_sp(bp); + bnxt_rtnl_unlock_sp(bp); } /* Only called from bnxt_sp_task() */ @@ -14402,9 +14417,9 @@ static void bnxt_rx_ring_reset(struct bnxt *bp) { int i; - bnxt_lock_sp(bp); + bnxt_rtnl_lock_sp(bp); if (!test_bit(BNXT_STATE_OPEN, &bp->state)) { - bnxt_unlock_sp(bp); + bnxt_rtnl_unlock_sp(bp); return; } /* Disable and flush TPA before resetting the RX ring */ @@ -14443,7 +14458,7 @@ static void bnxt_rx_ring_reset(struct bnxt *bp) } if (bp->flags & BNXT_FLAG_TPA) bnxt_set_tpa(bp, true); - bnxt_unlock_sp(bp); + bnxt_rtnl_unlock_sp(bp); } static void bnxt_fw_fatal_close(struct bnxt *bp) @@ -15358,15 +15373,17 @@ static void bnxt_fw_reset_task(struct work_struct *work) bp->fw_reset_state = BNXT_FW_RESET_STATE_OPENING; fallthrough; case BNXT_FW_RESET_STATE_OPENING: - while (!netdev_trylock(bp->dev)) { + while (!rtnl_trylock()) { bnxt_queue_fw_reset_work(bp, HZ / 10); return; } + netdev_lock(bp->dev); rc = bnxt_open(bp->dev); if (rc) { netdev_err(bp->dev, "bnxt_open() failed during FW reset\n"); bnxt_fw_reset_abort(bp, rc); netdev_unlock(bp->dev); + rtnl_unlock(); goto ulp_start; } @@ -15386,6 +15403,7 @@ static void bnxt_fw_reset_task(struct work_struct *work) bnxt_dl_health_fw_status_update(bp, true); } netdev_unlock(bp->dev); + rtnl_unlock(); bnxt_ulp_start(bp); bnxt_reenable_sriov(bp); netdev_lock(bp->dev); @@ -16379,7 +16397,7 @@ static int bnxt_queue_start(struct net_device *dev, rc); napi_enable_locked(&bnapi->napi); bnxt_db_nq_arm(bp, &cpr->cp_db, cpr->cp_raw_cons); - bnxt_reset_task(bp, true); + netif_close(dev); return rc; } @@ -17230,6 +17248,7 @@ static int bnxt_resume(struct device *device) struct bnxt *bp = netdev_priv(dev); int rc = 0; + rtnl_lock(); netdev_lock(dev); rc = pci_enable_device(bp->pdev); if (rc) { @@ -17274,6 +17293,7 @@ static int bnxt_resume(struct device *device) resume_exit: netdev_unlock(bp->dev); + rtnl_unlock(); if (!rc) { bnxt_ulp_start(bp); bnxt_reenable_sriov(bp); @@ -17445,6 +17465,7 @@ static void bnxt_io_resume(struct pci_dev *pdev) int err; netdev_info(bp->dev, "PCI Slot Resume\n"); + rtnl_lock(); netdev_lock(netdev); err = bnxt_hwrm_func_qcaps(bp); @@ -17462,6 +17483,7 @@ static void bnxt_io_resume(struct pci_dev *pdev) netif_device_attach(netdev); netdev_unlock(netdev); + rtnl_unlock(); if (!err) { bnxt_ulp_start(bp); bnxt_reenable_sriov(bp);