6.6.143-rc1 review
Subsystems (4)
Greg Kroah-Hartman
maintainerPATCH 116/452 iio: magnetometer: st_magn: fix default DRDY pin selection for LIS2MDL
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Advait Dhamorikar <[email protected]>
commit 49f79cd28f1e3333cbe0d616ce59ead0b24bf34e upstream.
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.
--- drivers/iio/magnetometer/st_magn_core.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-)
drivers/iio/magnetometer/st_magn_core.c +11 -2
@@ -506,6 +506,11 @@ static const struct st_sensors_platform_ .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 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 )
Greg Kroah-Hartman
maintainerPATCH 117/452 iio: light: cm3323: fix reg_conf not being initialized correctly
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Aldo Conte <[email protected]>
commit 1f4f0bcc5255dec5c4c3a1551bf49d8c33b69b20 upstream.
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 usingi2cset -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.
--- drivers/iio/light/cm3323.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-)
drivers/iio/light/cm3323.c +2 -3
@@ -89,15 +89,14 @@ static int cm3323_init(struct iio_dev *i/* 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 ; }
Greg Kroah-Hartman
maintainerPATCH 118/452 iio: buffer: hw-consumer: fix use-after-free in error path
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Felix Gu <[email protected]>
commit 6f5ed4f2c7c83f33344e0ba179f72a12e5dad4a4 upstream.
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().
--- drivers/iio/buffer/industrialio-hw-consumer.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/iio/buffer/industrialio-hw-consumer.c +2 -2
@@ -82,7 +82,7 @@ static struct hw_consumer_buffer *iio_hw*/ 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;
@@ -113,7 +113,7 @@ struct iio_hw_consumer *iio_hw_consumer_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:
Greg Kroah-Hartman
maintainerPATCH 119/452 USB: serial: omninet: fix memory corruption with small endpoint
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Johan Hovold <[email protected]>
commit 60df93d30f9bdd27db17c4d80ed80ef718d7226b upstream.
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.
--- drivers/usb/serial/omninet.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-)
drivers/usb/serial/omninet.c +5 -4
@@ -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,
@@ -55,6 +59,7 @@ static struct usb_serial_driver zyxel_om .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,
@@ -131,10 +136,6 @@ static void omninet_port_remove(struct ukfree (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;
Greg Kroah-Hartman
maintainerPATCH 120/452 usb: cdns3: gadget: fix request skipping after clearing halt
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yongchao Wu <[email protected]>
commit c8778ff817a7047d6848fefba99dcb27b1bf01fe upstream.
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.
--- drivers/usb/cdns3/cdns3-gadget.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-)
drivers/usb/cdns3/cdns3-gadget.c +11 -1
@@ -2816,9 +2816,19 @@ int __cdns3_gadget_ep_clear_halt(struct 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 ); }
Greg Kroah-Hartman
maintainerPATCH 121/452 usb: cdns3: plat: fix unbalanced pm_runtime_forbid() call permanently leaks the runtime PM usage counter across bind/unbind cycles
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Peter Chen <[email protected]>
commit ae6f3b82324e4f39ad8443c9020787e6fc889637 upstream.
Call pm_runtime_allow(dev) conditionally at cdns3_plat_remove.
--- drivers/usb/cdns3/cdns3-plat.c | 3 +++ 1 file changed, 3 insertions(+)
drivers/usb/cdns3/cdns3-plat.c +3 -0
@@ -182,6 +182,9 @@ static void cdns3_plat_remove(struct plastruct 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);
Greg Kroah-Hartman
maintainerPATCH 122/452 usb: dwc2: Fix use after free in debug code
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Dan Carpenter <[email protected]>
commit 9ea06a3fbf9f16e0d98c52cb3b99642be15ec281 upstream.
We're not allowed to dereference "urb" after calling usb_hcd_giveback_urb() so save the urb->status ahead of time.
--- drivers/usb/dwc2/hcd.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/usb/dwc2/hcd.c +3 -1
@@ -4804,6 +4804,7 @@ static int _dwc2_hcd_urb_dequeue(structstruct 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/* 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);
Greg Kroah-Hartman
maintainerPATCH 123/452 Input: elan_i2c - validate firmware size before use
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Dmitry Torokhov <[email protected]>
commit 76b0d0baa9ae9c60e726bbe1b6ff0bec2c993634 upstream.
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.
--- drivers/input/mouse/elan_i2c_core.c | 5 +++++ 1 file changed, 5 insertions(+)
drivers/input/mouse/elan_i2c_core.c +5 -0
@@ -645,6 +645,11 @@ static ssize_t elan_sysfs_update_fw(strureturn 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 ) {
Greg Kroah-Hartman
maintainerPATCH 124/452 bpf: sockmap: fix tail fragment offset in bpf_msg_push_data
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yuqi Xu <[email protected]>
commit f72eed9b84fb771019a955908132410a9ba9ea3f upstream.
When bpf_msg_push_data() inserts data in the middle of a scatterlist entry, it splits the original entry into a left fragment and a right fragment.
The right fragment offset is page-local, but the code advances it with start, which is the message-global insertion point. For inserts into a non-first SG entry, this over-advances the offset and leaves the split layout inconsistent.
Advance the right fragment offset by the fragment-local delta, start - offset, which matches the length removed from the front of the original entry.
--- net/core/filter.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/core/filter.c +1 -1
@@ -2848,7 +2848,7 @@ BPF_CALL_4(bpf_msg_push_data, struct sk_ psge->length = start - offset; rsge.length -= psge->length; - rsge.offset += start; + rsge.offset += start - offset;sk_msg_iter_var_next (i);sg_unmark_end (psge);
Greg Kroah-Hartman
maintainerPATCH 125/452 macsec: fix replay protection at XPN lower-PN wrap
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Junrui Luo <[email protected]>
commit e68842b3356471ba56c882209f324613dac47f64 upstream.
In macsec_post_decrypt(), when pn is U32_MAX, pn + 1 overflows u32 to 0 and the first branch never fires. If next_pn_halves.lower is also in the upper half, pn_same_half(pn, lower) is true and the XPN else-if does not fire either, leaving next_pn_halves unchanged. An attacker that captures the legitimate frame carrying pn == 0xFFFFFFFF on an XPN association can then replay it indefinitely, since lowest_pn never rises above the captured pn and macsec_decrypt() reconstructs the same IV.
Extend the XPN else-if to also fire when pn + 1 wraps to 0, so receipt of pn == U32_MAX advances next_pn_halves to (upper + 1, 0).
--- drivers/net/macsec.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/net/macsec.c +2 -1
@@ -800,7 +800,8 @@ static bool macsec_post_decrypt(struct sif (pn +1 > rx_sa->next_pn_halves.lower) { rx_sa->next_pn_halves.lower = pn +1 ; }else if (secy->xpn && - !pn_same_half (pn, rx_sa->next_pn_halves.lower)) { + (pn +1 ==0 || + !pn_same_half (pn, rx_sa->next_pn_halves.lower))) { rx_sa->next_pn_halves.upper++; rx_sa->next_pn_halves.lower = pn +1 ; }
Greg Kroah-Hartman
maintainerPATCH 126/452 ipv6: exthdrs: refresh nh pointer after ipv6_hop_jumbo()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Justin Iurman <[email protected]>
commit d47548a36639095939f4747d4c43f2271366f565 upstream.
ipv6_hop_jumbo() calls pskb_trim_rcsum(), which can change skb pointers. Let's recompute nh pointer to make sure any change won't mess things up.
--- net/ipv6/exthdrs.c | 2 ++ 1 file changed, 2 insertions(+)
net/ipv6/exthdrs.c +2 -0
@@ -183,6 +183,8 @@ static bool ip6_parse_tlv(bool hopbyhop,case IPV6_TLV_JUMBO :if (!ipv6_hop_jumbo (skb, off))return false; + + nh =skb_network_header (skb);break ;case IPV6_TLV_CALIPSO :if (!ipv6_hop_calipso (skb, off))
Greg Kroah-Hartman
maintainerPATCH 127/452 ASoC: qcom: q6asm-dai: fix error handling in prepare and set_params
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Srinivas Kandagatla <[email protected]>
commit 4b4db09f283df65d780bc7cee66cb4a7e9bf4770 upstream.
Fix error handling in q6asm_dai_compr_set_params() and q6asm_dai_prepare() for both CMD_CLOSE and q6asm_unmap_memory_regions().
In both the functions, we are doing q6asm_audio_client_free in failure cases, which means if prepare or set_params fail, we can never recover. Now open and close are done in respective dai_open/close functions.
--- sound/soc/qcom/qdsp6/q6asm-dai.c | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-)
sound/soc/qcom/qdsp6/q6asm-dai.c +16 -8
@@ -239,9 +239,19 @@ static int q6asm_dai_prepare(struct snd_/* rate and channels are sent to audio driver */ if (prtd->state ==Q6ASM_STREAM_RUNNING ) {/* clear the previous setup if any */ -q6asm_cmd (prtd->audio_client, prtd->stream_id,CMD_CLOSE ); -q6asm_unmap_memory_regions (substream->stream, - prtd->audio_client); + ret =q6asm_cmd (prtd->audio_client, prtd->stream_id,CMD_CLOSE ); +if (ret <0 ) { +dev_err (dev,"Failed to close q6asm stream %d\n" , prtd->stream_id); +return ret; + } + + ret =q6asm_unmap_memory_regions (substream->stream, prtd->audio_client); +if (ret <0 ) { +dev_err (dev,"Failed to unmap memory regions for q6asm stream %d\n" , + prtd->stream_id); +return ret; + } +q6routing_stream_close (soc_prtd->dai_link->id, substream->stream); prtd->state =Q6ASM_STREAM_STOPPED ;
@@ -309,8 +319,6 @@ routing_err:q6asm_cmd (prtd->audio_client, prtd->stream_id,CMD_CLOSE ); open_err:q6asm_unmap_memory_regions (substream->stream, prtd->audio_client); -q6asm_audio_client_free (prtd->audio_client); - prtd->audio_client =NULL ;return ret; }
@@ -912,7 +920,7 @@ static int q6asm_dai_compr_set_params(st prtd->session_id, dir);if (ret) {dev_err (dev,"Stream reg failed ret:%d\n" , ret); - goto q6_err; + goto routing_err; } ret = __q6asm_dai_compr_set_codec_params(component, stream,
@@ -938,11 +946,11 @@ static int q6asm_dai_compr_set_params(streturn 0 ; q6_err: +q6routing_stream_close (rtd->dai_link->id, dir); +routing_err:q6asm_cmd (prtd->audio_client, prtd->stream_id,CMD_CLOSE ); open_err: -q6asm_audio_client_free (prtd->audio_client); - prtd->audio_client =NULL ;return ret; }
Greg Kroah-Hartman
maintainerPATCH 128/452 ipv6: exthdrs: refresh nh after handling HAO option
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zhengchuan Liang <[email protected]>
commit f7b52afe3592eae66e160586b45a3f2242972c63 upstream.
ip6_parse_tlv() caches skb_network_header(skb) in nh while walking IPv6 TLVs.
ipv6_dest_hao() may call pskb_expand_head() for a cloned skb, which can move the skb head and invalidate the cached network header pointer. Refresh nh after ipv6_dest_hao() returns so any trailing padding or TLVs are parsed from the current skb head.
This matches the existing pattern used in ip6_parse_tlv() after helpers that can modify skb header storage.
--- net/ipv6/exthdrs.c | 2 ++ 1 file changed, 2 insertions(+)
net/ipv6/exthdrs.c +2 -0
@@ -202,6 +202,8 @@ static bool ip6_parse_tlv(bool hopbyhop,case IPV6_TLV_HAO :if (!ipv6_dest_hao (skb, off))return false; + + nh =skb_network_header (skb);break ;#endif default :
Greg Kroah-Hartman
maintainerPATCH 129/452 ip6: vti: Use ip6_tnl.net in vti6_siocdevprivate().
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Maoyi Xie <[email protected]>
commit 8b484efd5cb4eeef9021a661e198edc5349dacf6 upstream.
After patch 1/2 in this series, vti6_update() unlinks and relinks the tunnel through t->net. vti6_siocdevprivate() still uses dev_net(dev) for the collision lookup. For a tunnel moved through IFLA_NET_NS_FD, dev_net(dev) is the new netns, not t->net.
SIOCCHGTUNNEL on a migrated tunnel then runs:
net = dev_net(dev) /* migrated netns */
t = vti6_locate(net, &p1, false) /* misses target in t->net */
...
t = netdev_priv(dev)
vti6_update(t, &p1, false) /* mutates t->net's hash */
A caller in the migrated netns picks params that match a tunnel in the creation netns. The lookup in dev_net(dev) finds nothing. vti6_update() prepends the migrated tunnel at the head of the creation netns hash bucket for those params. Later lookups in the creation netns resolve to the migrated device. xfrm receive delivers the matched packets through a device the caller controls.
Reachable from an unprivileged user namespace (unshare --user --map-root-user --net). Cross tenant scope on container hosts.
Switch the SIOCCHGTUNNEL path on a non fallback device to use t->net for the lookup. The lookup now matches the netns vti6_update() operates on.
Also add ns_capable(self->net->user_ns, CAP_NET_ADMIN) before the lookup. The check at the top of the case is against dev_net(dev)->user_ns, which after migration is the attacker's netns. A caller there can pick params absent from self->net, the lookup returns NULL, t becomes self, and vti6_update() inserts the device into the creation netns hash. The new check requires CAP_NET_ADMIN in the creation netns user_ns too.
SIOCADDTUNNEL and SIOCCHGTUNNEL on the fallback device keep dev_net(dev), which equals init_net there.
--- net/ipv6/ip6_vti.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-)
net/ipv6/ip6_vti.c +9 -2
@@ -838,17 +838,24 @@ vti6_siocdevprivate(struct net_device *dif (p.proto !=IPPROTO_IPV6 && p.proto !=0 )break ;vti6_parm_from_user (&p1, &p); - t =vti6_locate (net, &p1, cmd ==SIOCADDTUNNEL );if (dev != ip6n->fb_tnl_dev && cmd ==SIOCCHGTUNNEL ) { +struct ip6_tnl *self =netdev_priv (dev); + + err = -EPERM ; +if (!ns_capable (self->net->user_ns,CAP_NET_ADMIN )) +break ; + t =vti6_locate (self->net, &p1, false);if (t) {if (t->dev != dev) { err = -EEXIST ;break ; } }else - t =netdev_priv (dev); + t = self; err =vti6_update (t, &p1, false); + }else { + t =vti6_locate (net, &p1, cmd ==SIOCADDTUNNEL ); }if (t) { err =0 ;
Greg Kroah-Hartman
maintainerPATCH 130/452 ipv6: validate extension header length before copying to cmsg
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Qi Tang <[email protected]>
commit dd433671fef381fdaf7b530c631e6b782d66e224 upstream.
ip6_datagram_recv_specific_ctl() builds IPV6_{HOPOPTS,DSTOPTS,RTHDR} cmsgs (and their IPV6_2292* legacy counterparts) by trusting the on-wire hdrlen byte (ptr[1]) when computing the put_cmsg() length. The length was validated only at parse time (ipv6_parse_hopopts(), etc.). An nftables payload-write expression can rewrite hdrlen after parsing and before the skb reaches recvmsg; the write itself is in-bounds but put_cmsg() then reads up to ((hdrlen+1) << 3) = 2040 bytes from an 8-byte header. nftables is reachable from an unprivileged user namespace, so this is an unprivileged slab-out-of-bounds read:
BUG: KASAN: slab-out-of-bounds in put_cmsg+0x3ac/0x540 put_cmsg+0x3ac/0x540 udpv6_recvmsg+0xca0/0x1250 sock_recvmsg+0xdf/0x190 ____sys_recvmsg+0x1b1/0x620
Add ipv6_get_exthdr_len() which validates that at least two bytes are accessible before reading the hdrlen field, then checks the computed length against skb_tail_pointer(skb), returning 0 on failure. Extension headers are kept in the linear skb area by pskb_may_pull() during input, so skb_tail_pointer() is the correct bound.
Use ipv6_get_exthdr_len() at all non-AH call sites: the five standalone cmsg blocks (HbH, 2292HbH, 2292DSTOPTS x2, 2292RTHDR) and the three standard cases in the extension-header walk loop (DSTOPTS, ROUTING, default). AH retains an inline bounds check because its length formula differs ((ptr[1]+2)<<2).
The walk loop also gets a pre-read bounds check at the top to validate ptr before any case accesses ptr[0] or ptr[1].
When the walk loop detects a corrupted header, return from the function instead of continuing to process later socket options.
--- net/ipv6/datagram.c | 54 ++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 46 insertions(+), 8 deletions(-)
net/ipv6/datagram.c +46 -8
@@ -618,6 +618,18 @@ void ip6_datagram_recv_common_ctl(struct } } +static u16 ipv6_get_exthdr_len (const struct sk_buff *skb,const u8 *ptr) +{ +u16 len; + +if (ptr +2 >skb_tail_pointer (skb)) +return 0 ; + + len = (ptr[1 ] +1 ) <<3 ; + +return (len <=skb_tail_pointer (skb) - ptr) ? len :0 ; +} +void ip6_datagram_recv_specific_ctl (struct sock *sk,struct msghdr *msg,struct sk_buff *skb) {
@@ -644,7 +656,10 @@ void ip6_datagram_recv_specific_ctl(stru/* HbH is allowed only once */ if (np->rxopt.bits.hopopts && (opt->flags &IP6SKB_HOPBYHOP )) {u8 *ptr = nh +sizeof (struct ipv6hdr ); -put_cmsg (msg,SOL_IPV6 ,IPV6_HOPOPTS , (ptr[1 ]+1 )<<3 , ptr); +u16 len =ipv6_get_exthdr_len (skb, ptr); + +if (len) +put_cmsg (msg,SOL_IPV6 ,IPV6_HOPOPTS , len, ptr); } if (opt->lastopt &&
@@ -665,26 +680,37 @@ void ip6_datagram_recv_specific_ctl(struunsigned int len;u8 *ptr = nh + off; +if (ptr +2 >skb_tail_pointer (skb)) +return ; +switch (nexthdr) {case IPPROTO_DSTOPTS : nexthdr = ptr[0 ]; - len = (ptr[1 ] +1 ) <<3 ; + len =ipv6_get_exthdr_len (skb, ptr); +if (!len) +return ;if (np->rxopt.bits.dstopts)put_cmsg (msg,SOL_IPV6 ,IPV6_DSTOPTS , len, ptr);break ;case IPPROTO_ROUTING : nexthdr = ptr[0 ]; - len = (ptr[1 ] +1 ) <<3 ; + len =ipv6_get_exthdr_len (skb, ptr); +if (!len) +return ;if (np->rxopt.bits.srcrt)put_cmsg (msg,SOL_IPV6 ,IPV6_RTHDR , len, ptr);break ;case IPPROTO_AH : nexthdr = ptr[0 ]; len = (ptr[1 ] +2 ) <<2 ; +if (ptr + len >skb_tail_pointer (skb)) +return ;break ;default : nexthdr = ptr[0 ]; - len = (ptr[1 ] +1 ) <<3 ; + len =ipv6_get_exthdr_len (skb, ptr); +if (!len) +return ;break ; }
@@ -706,19 +732,31 @@ void ip6_datagram_recv_specific_ctl(stru }if (np->rxopt.bits.ohopopts && (opt->flags &IP6SKB_HOPBYHOP )) {u8 *ptr = nh +sizeof (struct ipv6hdr ); -put_cmsg (msg,SOL_IPV6 ,IPV6_2292HOPOPTS , (ptr[1 ]+1 )<<3 , ptr); +u16 len =ipv6_get_exthdr_len (skb, ptr); + +if (len) +put_cmsg (msg,SOL_IPV6 ,IPV6_2292HOPOPTS , len, ptr); }if (np->rxopt.bits.odstopts && opt->dst0) {u8 *ptr = nh + opt->dst0; -put_cmsg (msg,SOL_IPV6 ,IPV6_2292DSTOPTS , (ptr[1 ]+1 )<<3 , ptr); +u16 len =ipv6_get_exthdr_len (skb, ptr); + +if (len) +put_cmsg (msg,SOL_IPV6 ,IPV6_2292DSTOPTS , len, ptr); }if (np->rxopt.bits.osrcrt && opt->srcrt) {struct ipv6_rt_hdr *rthdr = (struct ipv6_rt_hdr *)(nh + opt->srcrt); -put_cmsg (msg,SOL_IPV6 ,IPV6_2292RTHDR , (rthdr->hdrlen+1 ) <<3 , rthdr); +u16 len =ipv6_get_exthdr_len (skb, (u8 *)rthdr); + +if (len) +put_cmsg (msg,SOL_IPV6 ,IPV6_2292RTHDR , len, rthdr); }if (np->rxopt.bits.odstopts && opt->dst1) {u8 *ptr = nh + opt->dst1; -put_cmsg (msg,SOL_IPV6 ,IPV6_2292DSTOPTS , (ptr[1 ]+1 )<<3 , ptr); +u16 len =ipv6_get_exthdr_len (skb, ptr); + +if (len) +put_cmsg (msg,SOL_IPV6 ,IPV6_2292DSTOPTS , len, ptr); }if (np->rxopt.bits.rxorigdstaddr) {struct sockaddr_in6 sin6;
Greg Kroah-Hartman
maintainerPATCH 131/452 xfrm: input: hold netns during deferred transport reinjection
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zhengchuan Liang <[email protected]>
commit c16f74dc1d75d0e2e7670076d5375deda110ebeb upstream.
Transport-mode reinjection stores a struct net pointer in skb->cb and uses it later from xfrm_trans_reinject(). That pointer must stay valid until the deferred callback runs.
Take a netns reference when queueing deferred reinjection work and drop it after the callback completes. Use maybe_get_net() so the queueing path does not revive a namespace that is already being torn down.
This keeps the existing workqueue design and fixes the netns lifetime handling in one place for all users of xfrm_trans_queue_net().
--- net/xfrm/xfrm_input.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-)
net/xfrm/xfrm_input.c +12 -4
@@ -760,9 +760,12 @@ static void xfrm_trans_reinject(struct wspin_unlock_bh (&trans->queue_lock);local_bh_disable (); -while ((skb =__skb_dequeue (&queue))) -XFRM_TRANS_SKB_CB (skb)->finish (XFRM_TRANS_SKB_CB (skb)->net, -NULL , skb); +while ((skb =__skb_dequeue (&queue))) { +struct net *net =XFRM_TRANS_SKB_CB (skb)->net; + +XFRM_TRANS_SKB_CB (skb)->finish (net,NULL , skb); +put_net (net); + }local_bh_enable (); }
@@ -771,6 +774,7 @@ int xfrm_trans_queue_net(struct net *net struct sk_buff *)) {struct xfrm_trans_tasklet *trans; +struct net *hold_net; trans =this_cpu_ptr (&xfrm_trans_tasklet);
@@ -779,8 +783,12 @@ int xfrm_trans_queue_net(struct net *netBUILD_BUG_ON (sizeof (struct xfrm_trans_cb ) >sizeof (skb->cb)); + hold_net =maybe_get_net (net); +if (!hold_net) +return -ENODEV ; +XFRM_TRANS_SKB_CB (skb)->finish = finish; -XFRM_TRANS_SKB_CB (skb)->net = net; +XFRM_TRANS_SKB_CB (skb)->net = hold_net;spin_lock_bh (&trans->queue_lock);__skb_queue_tail (&trans->queue, skb);spin_unlock_bh (&trans->queue_lock);
Greg Kroah-Hartman
maintainerPATCH 132/452 ip6: vti: Use ip6_tnl.net in vti6_changelink().
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kuniyuki Iwashima <[email protected]>
commit 11b326fb0a374f4654f9be22d0f0f7abd9f7d3fe upstream.
ip netns add ns1
ip netns add ns2
ip -n ns1 link add vti6_test type vti6 remote ::1 local ::2 key 7 ip -n ns1 link set vti6_test netns ns2
ip -n ns2 link set vti6_test type vti6 remote ::3 local ::4 key 9 ip netns del ns2
ip netns del ns1
[ 132.495484] ------------[ cut here ]------------ [ 132.497609] kernel BUG at net/core/dev.c:12376!
Commit 61220ab34948 ("vti6: Enable namespace changing") dropped NETIF_F_NETNS_LOCAL from vti6 devices. A vti6 tunnel can then move through IFLA_NET_NS_FD. After the move dev_net(dev) points at the new netns while t->net stays at the creation netns.
vti6_changelink() and vti6_update() still use dev_net(dev) and dev_net(t->dev). They unlink from one per netns hash and relink into another. The creation netns is left with a stale entry. cleanup_net() of that netns later walks freed memory.
Reachable from an unprivileged user namespace (unshare --user --map-root-user --net). Cross tenant scope on container hosts.
--- net/ipv6/ip6_vti.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-)
net/ipv6/ip6_vti.c +7 -5
@@ -726,10 +726,11 @@ vti6_tnl_change(struct ip6_tnl *t, conststatic int vti6_update (struct ip6_tnl *t,struct __ip6_tnl_parm *p,bool keep_mtu) { -struct net *net =dev_net (t->dev); -struct vti6_net *ip6n =net_generic (net, vti6_net_id); +struct net *net = t->net; +struct vti6_net *ip6n;int err; + ip6n =net_generic (net, vti6_net_id);vti6_tnl_unlink (ip6n, t);synchronize_net (); err =vti6_tnl_change (t, p, keep_mtu);
@@ -1043,11 +1044,12 @@ static int vti6_changelink(struct net_destruct nlattr *data[], struct netlink_ext_ack *extack) { - struct ip6_tnl *t; + struct ip6_tnl *t = netdev_priv(dev); +struct net *net = t->net;struct __ip6_tnl_parm p; -struct net *net =dev_net (dev); -struct vti6_net *ip6n =net_generic (net, vti6_net_id); +struct vti6_net *ip6n; + ip6n =net_generic (net, vti6_net_id);if (dev == ip6n->fb_tnl_dev)return -EINVAL ;
Greg Kroah-Hartman
maintainerPATCH 133/452 HID: wacom: Fix OOB write in wacom_hid_set_device_mode()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Lee Jones <[email protected]>
commit c0a8899e02ddebd51e2589835182c239c2e224ae upstream.
wacom_hid_set_device_mode() currently assumes that the HID_DG_INPUTMODE usage is always located in the first field (field[0]) of the feature report. However, a device can specify HID_DG_INPUTMODE in a different field.
If HID_DG_INPUTMODE is in a field other than the first one and the first field has a report_count smaller than the usage_index of HID_DG_INPUTMODE, this leads to an out-of-bounds write to r->field[0]->value.
Fix this by storing the field index of HID_DG_INPUTMODE in 'struct hid_data' during feature mapping. In wacom_hid_set_device_mode(), use this stored field index to access the correct field and add bounds checks to ensure both the field index and the value index are within valid ranges before writing.
--- drivers/hid/wacom_sys.c | 13 ++++++++++--- drivers/hid/wacom_wac.h | 1 + 2 files changed, 11 insertions(+), 3 deletions(-)
drivers/hid/wacom_sys.c +10 -3
@@ -341,6 +341,7 @@ static void wacom_feature_mapping(struct hid_data->inputmode = field->report->id; hid_data->inputmode_index = usage->usage_index; + hid_data->inputmode_field_index = field->index;break ;case HID_UP_DIGITIZER :
@@ -556,9 +557,14 @@ static int wacom_hid_set_device_mode(str re = &(hdev->report_enum[HID_FEATURE_REPORT ]); r = re->report_id_hash[hid_data->inputmode]; -if (r) { - r->field[0 ]->value[hid_data->inputmode_index] =2 ; -hid_hw_request (hdev, r,HID_REQ_SET_REPORT ); +if (r && hid_data->inputmode_field_index >=0 && + hid_data->inputmode_field_index < r->maxfield) { +struct hid_field *field = r->field[hid_data->inputmode_field_index]; + +if (field && hid_data->inputmode_index < field->report_count) { + field->value[hid_data->inputmode_index] =2 ; +hid_hw_request (hdev, r,HID_REQ_SET_REPORT ); + } }return 0 ; }
@@ -2810,6 +2816,7 @@ static int wacom_probe(struct hid_devicereturn error; wacom_wac->hid_data.inputmode =-1 ; + wacom_wac->hid_data.inputmode_field_index =-1 ; wacom_wac->mode_report =-1 ;if (hid_is_usb (hdev)) {
drivers/hid/wacom_wac.h +1 -0
@@ -297,6 +297,7 @@ struct wacom_shared {struct hid_data {__s16 inputmode;/* InputMode HID feature, -1 if non-existent */ __s16 inputmode_index;/* InputMode HID feature index in the report */ +__s16 inputmode_field_index;/* InputMode HID feature field index in the report */ bool sense_state;bool inrange_state;bool invert_state;
Greg Kroah-Hartman
maintainerPATCH 134/452 iommu, debugobjects: avoid gcc-16.1 section mismatch warnings
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Arnd Bergmann <[email protected]>
commit 4c9ad387aa2d6785299722e54224d34764edaeb3 upstream.
gcc-16 has gained some more advanced inter-procedual optimization techniques that enable it to inline the dummy_tlb_add_page() and dummy_tlb_flush() function pointers into a specialized version of __arm_v7s_unmap:
WARNING: modpost: vmlinux: section mismatch in reference: __arm_v7s_unmap+0x2cc (section: .text) -> dummy_tlb_add_page (section: .init.text)
ERROR: modpost: Section mismatches detected.
Quoted 1 line
From what I can tell, the transformation is correct, as this is only
called when __arm_v7s_unmap() is called from arm_v7s_do_selftests(), which is also __init. Since __arm_v7s_unmap() however is not __init, gcc cannot inline the inner function calls directly.
In debug_objects_selftest(), the same thing happens. Both the caller and the leaf function are __init, but the IPA pulls it into a non-init one:
WARNING: modpost: vmlinux: section mismatch in reference: lookup_object_or_alloc+0x7c (section: .text.lookup_object_or_alloc) -> is_static_object (section: .init.text)
Marking the affected functions as not "__init" would reliably avoid this issue but is not a good solution because it removes an otherwise correct annotation. I tried marking the functions as 'noinline', but that ended up not covering all the affected configurations.
With some more experimenting, I found that marking these functions as __attribute__((noipa)) is both logical and reliable.
In order to keep the syntax readable, add a custom macro for this in include/linux/compiler_attributes.h next to other related macros and use it to annotate both files.
--- drivers/iommu/io-pgtable-arm-v7s.c | 18 ++++++++++++------ include/linux/compiler_attributes.h | 11 +++++++++++ lib/debugobjects.c | 2 +- 3 files changed, 24 insertions(+), 7 deletions(-)
drivers/iommu/io-pgtable-arm-v7s.c +12 -6
@@ -894,21 +894,27 @@ struct io_pgtable_init_fns io_pgtable_arstatic struct io_pgtable_cfg *cfg_cookie__initdata ; -static void __init dummy_tlb_flush_all (void *cookie) +/* +* __noipa prevents gcc from turning indirect iommu_flush_ops calls +* into direct calls from a specialized __arm_v7s_unmap() that triggers +* a build time section mismatch assertion. +*/ +static __noipa void __init dummy_tlb_flush_all(void *cookie) {WARN_ON (cookie != cfg_cookie); } -static void __init dummy_tlb_flush (unsigned long iova,size_t size, -size_t granule,void *cookie) +static __noipa void __init dummy_tlb_flush(unsigned long iova, size_t size, +size_t granule,void *cookie) {WARN_ON (cookie != cfg_cookie);WARN_ON (!(size & cfg_cookie->pgsize_bitmap)); } -static void __init dummy_tlb_add_page (struct iommu_iotlb_gather *gather, -unsigned long iova,size_t granule, -void *cookie) +static __noipa void __init dummy_tlb_add_page(struct iommu_iotlb_gather *gather, +unsigned long iova, +size_t granule, +void *cookie) {dummy_tlb_flush (iova, granule, granule, cookie); }
include/linux/compiler_attributes.h +11 -0
@@ -384,6 +384,17 @@#endif /* +* Optional: not supported by clang +* +* gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Attributes.html#index-noipa +*/ +#if __has_attribute (noipa) +# define __noipa __attribute__((noipa)) +#else +# define __noipa +#endif + +/* * gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-weak-function-attribute * gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Variable-Attributes.html#index-weak-variable-attribute */
lib/debugobjects.c +1 -1
@@ -1076,7 +1076,7 @@ struct self_test {static __initconst const struct debug_obj_descr descr_type_test; -static bool __init is_static_object (void *addr) +static __noipa bool __init is_static_object(void *addr) {struct self_test *obj = addr;
Greg Kroah-Hartman
maintainerPATCH 135/452 nfc: hci: fix out-of-bounds read in HCP header parsing
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ashutosh Desai <[email protected]>
commit f040e590c035bfd9553fe79ee9585caf1b14d67b upstream.
Both nfc_hci_recv_from_llc() and nci_hci_data_received_cb() read packet->header from skb->data at function entry without first checking that the buffer holds at least one byte. A malicious NFC peer can send a 0-byte HCP frame that passes through the SHDLC layer and reaches these functions, causing an out-of-bounds heap read of packet->header. The same 0-byte frame, if queued as a non-final fragment, also causes the reassembly loop to underflow msg_len to UINT_MAX, triggering skb_over_panic() when the reassembled skb is written.
Fix this by adding a pskb_may_pull() check at the entry of each function before packet->header is first accessed. The existing pskb_may_pull() checks before the reassembled hcp_skb is cast to struct hcp_packet remain in place to guard the 2-byte HCP message header.
--- net/nfc/hci/core.c | 10 ++++++++++ net/nfc/nci/hci.c | 10 ++++++++++ 2 files changed, 20 insertions(+)
net/nfc/hci/core.c +10 -0
@@ -861,6 +861,11 @@ static void nfc_hci_recv_from_llc(structstruct sk_buff *frag_skb;int msg_len; +if (!pskb_may_pull (skb,NFC_HCI_HCP_PACKET_HEADER_LEN )) { +kfree_skb (skb); +return ; + } + packet = (struct hcp_packet *)skb->data;if ((packet->header & ~NFC_HCI_FRAGMENT ) ==0 ) {skb_queue_tail (&hdev->rx_hcp_frags, skb);
@@ -904,6 +909,11 @@ static void nfc_hci_recv_from_llc(struct* unblock waiting cmd context. Otherwise, enqueue to dispatch * in separate context where handler can also execute command. */ +if (!pskb_may_pull (hcp_skb,NFC_HCI_HCP_HEADER_LEN )) { +kfree_skb (hcp_skb); +return ; + } + packet = (struct hcp_packet *)hcp_skb->data; type =HCP_MSG_GET_TYPE (packet->message.header);if (type ==NFC_HCI_HCP_RESPONSE ) {
net/nfc/nci/hci.c +10 -0
@@ -439,6 +439,11 @@ void nci_hci_data_received_cb(void *contreturn ; } +if (!pskb_may_pull (skb,NCI_HCI_HCP_PACKET_HEADER_LEN )) { +kfree_skb (skb); +return ; + } + packet = (struct nci_hcp_packet *)skb->data;if ((packet->header & ~NCI_HCI_FRAGMENT ) ==0 ) {skb_queue_tail (&ndev->hci_dev->rx_hcp_frags, skb);
@@ -482,6 +487,11 @@ void nci_hci_data_received_cb(void *cont* unblock waiting cmd context. Otherwise, enqueue to dispatch * in separate context where handler can also execute command. */ +if (!pskb_may_pull (hcp_skb,NCI_HCI_HCP_HEADER_LEN )) { +kfree_skb (hcp_skb); +return ; + } + packet = (struct nci_hcp_packet *)hcp_skb->data; type =NCI_HCP_MSG_GET_TYPE (packet->message.header);if (type ==NCI_HCI_HCP_RESPONSE ) {
Greg Kroah-Hartman
maintainerPATCH 136/452 xfrm: route MIGRATE notifications to callers netns
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Maoyi Xie <[email protected]>
commit 7e2a4f7ca0952820731ef7bdadfc9a9e9d3571b4 upstream.
xfrm_send_migrate() in net/xfrm/xfrm_user.c and pfkey_send_migrate() in net/key/af_key.c both hardcode &init_net for the multicast that announces a successful XFRM_MSG_MIGRATE / SADB_X_MIGRATE.
XFRM_MSG_MIGRATE arrives on a per-netns NETLINK_XFRM socket, and the rest of the xfrm/af_key netlink path was made netns-aware in 2008. The other 14 multicast paths in xfrm_user.c route their event using xs_net(x), xp_net(xp) or sock_net(skb->sk); only the migrate path was missed.
Two consequences of the init_net hardcoding:
1. The notification (selector, old/new endpoint addresses, and the km_address) is delivered to listeners on init_net's XFRMNLGRP_MIGRATE / pfkey BROADCAST_ALL groups rather than on the issuing netns. An IKE daemon running in init_net therefore receives migration notifications originating from any other netns on the host.
2. An IKE daemon running inside a non-init netns and subscribed to its own XFRMNLGRP_MIGRATE / pfkey groups never receives the notification of its own migration. IKEv2 MOBIKE / address-update handling inside a netns is silently broken.
Thread struct net through km_migrate() and the xfrm_mgr.migrate function pointer, drop the &init_net override in xfrm_send_migrate() and pfkey_send_migrate(), and pass the caller's net (already in scope in xfrm_migrate() via sock_net(skb->sk)) all the way down. struct xfrm_mgr is in-tree only and not exported as a stable API, so the function-pointer signature change is internal.
pfkey_broadcast() is already netns-aware via net_generic(net, pfkey_net_id) since the pernet conversion. The five other pfkey_broadcast() callers in af_key.c already pass xs_net(x), sock_net(sk) or a per-netns net, so this only removes the &init_net outlier.
--- include/net/xfrm.h | 3 ++- net/key/af_key.c | 6 +++--- net/xfrm/xfrm_policy.c | 2 +- net/xfrm/xfrm_state.c | 4 ++-- net/xfrm/xfrm_user.c | 5 ++--- 5 files changed, 10 insertions(+), 10 deletions(-)
include/net/xfrm.h +2 -1
@@ -608,6 +608,7 @@ struct xfrm_mgr {const struct xfrm_migrate *m, int num_bundles, const struct xfrm_kmaddress *k, + struct net *net, const struct xfrm_encap_tmpl *encap);bool (*is_alive)(const struct km_event *c); };
@@ -1772,7 +1773,7 @@ int xfrm_sk_policy_insert(struct sock *s#ifdef CONFIG_XFRM_MIGRATE int km_migrate (const struct xfrm_selector *sel,u8 dir,u8 type,const struct xfrm_migrate *m,int num_bundles, -const struct xfrm_kmaddress *k, +const struct xfrm_kmaddress *k,struct net *net,const struct xfrm_encap_tmpl *encap);struct xfrm_state *xfrm_migrate_state_find (struct xfrm_migrate *m,struct net *net,u32 if_id);
net/key/af_key.c +3 -3
@@ -3563,7 +3563,7 @@ static int set_ipsecrequest(struct sk_bu#ifdef CONFIG_NET_KEY_MIGRATE static int pfkey_send_migrate (const struct xfrm_selector *sel,u8 dir,u8 type,const struct xfrm_migrate *m,int num_bundles, -const struct xfrm_kmaddress *k, +const struct xfrm_kmaddress *k,struct net *net,const struct xfrm_encap_tmpl *encap) {int i;
@@ -3668,7 +3668,7 @@ static int pfkey_send_migrate(const stru }/* broadcast migrate message to sockets */ -pfkey_broadcast (skb,GFP_ATOMIC ,BROADCAST_ALL ,NULL , &init_net); +pfkey_broadcast (skb,GFP_ATOMIC ,BROADCAST_ALL ,NULL , net);return 0 ;
@@ -3679,7 +3679,7 @@ err:#else static int pfkey_send_migrate (const struct xfrm_selector *sel,u8 dir,u8 type,const struct xfrm_migrate *m,int num_bundles, -const struct xfrm_kmaddress *k, +const struct xfrm_kmaddress *k,struct net *net,const struct xfrm_encap_tmpl *encap) {return -ENOPROTOOPT ;
net/xfrm/xfrm_policy.c +1 -1
@@ -4595,7 +4595,7 @@ int xfrm_migrate(const struct xfrm_selec }/* Stage 5 - announce */ -km_migrate (sel, dir, type, m, num_migrate, k, encap); +km_migrate (sel, dir, type, m, num_migrate, k, net, encap);xfrm_pol_put (pol);
net/xfrm/xfrm_state.c +2 -2
@@ -2583,7 +2583,7 @@ EXPORT_SYMBOL(km_policy_expired);#ifdef CONFIG_XFRM_MIGRATE int km_migrate (const struct xfrm_selector *sel,u8 dir,u8 type,const struct xfrm_migrate *m,int num_migrate, -const struct xfrm_kmaddress *k, +const struct xfrm_kmaddress *k,struct net *net,const struct xfrm_encap_tmpl *encap) {int err = -EINVAL ;
@@ -2594,7 +2594,7 @@ int km_migrate(const struct xfrm_selectolist_for_each_entry_rcu (km, &xfrm_km_list, list) {if (km->migrate) { ret = km->migrate (sel, dir, type, m, num_migrate, k, - encap); + net, encap);if (!ret) err = ret; }
net/xfrm/xfrm_user.c +2 -3
@@ -2963,10 +2963,9 @@ out_cancel:static int xfrm_send_migrate (const struct xfrm_selector *sel,u8 dir,u8 type,const struct xfrm_migrate *m,int num_migrate, -const struct xfrm_kmaddress *k, +const struct xfrm_kmaddress *k,struct net *net,const struct xfrm_encap_tmpl *encap) { -struct net *net = &init_net;struct sk_buff *skb;int err;
@@ -2984,7 +2983,7 @@ static int xfrm_send_migrate(const struc#else static int xfrm_send_migrate (const struct xfrm_selector *sel,u8 dir,u8 type,const struct xfrm_migrate *m,int num_migrate, -const struct xfrm_kmaddress *k, +const struct xfrm_kmaddress *k,struct net *net,const struct xfrm_encap_tmpl *encap) {return -ENOPROTOOPT ;
Greg Kroah-Hartman
maintainerPATCH 137/452 xfrm: ah: use skb_to_full_sk in async output callbacks
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Michael Bommarito <[email protected]>
commit 79d8be262377f7112cfa3088dfc4142d5a2533f3 upstream.
When AH output is offloaded to an asynchronous crypto provider (hardware accelerators such as AMD CCP, or a forced-async software shim used for testing), the digest completion fires ah_output_done() / ah6_output_done() on a workqueue. The egress skb at that point may have been originated by a TCP listener sending a SYN-ACK, which sets skb->sk to a request_sock via skb_set_owner_edemux(); it may also have been originated by an inet_timewait_sock retransmit. Neither is a full struct sock, and passing the raw skb->sk to xfrm_output_resume() then forwards a non-full socket through the rest of the xfrm output chain.
xfrm_output_resume() and its downstream consumers expect a full sk where they dereference at all. The natural egress path through ah_output_done() does not crash today because the consumers that read past sock_common are either gated by sk_fullsock() or short-circuit on flags that are clear on a fresh request_sock; an exhaustive walk of the 50 most plausible consumers under sch_fq, dev_queue_xmit, netfilter, tc-egress and cgroup-egress BPF found no current unguarded deref. The bug is still a real type confusion that future consumer changes could turn into a memory-corruption primitive.
This is the same bug class fixed for ESP in commit 1620c88887b1 ("xfrm: Fix the usage of skb->sk"). Apply the analogous fix to
AH: convert skb->sk to a full socket pointer (or NULL) via skb_to_full_sk() before handing it to xfrm_output_resume().
The same async AH callbacks were touched recently for an independent ESN-related ICV layout bug in commit ec54093e6a8f ("xfrm: ah: account for ESN high bits in async callbacks"); the sk type-confusion addressed here is orthogonal. This patch is part of an ongoing audit of the AH callback paths; an ah_output ihl-validation hardening series is also currently under review on netdev.
Reproduced under UML + KASAN + lockdep with a forced-async hmac(sha1) shim that registers at priority 9999 and wraps the sync in-tree hmac-sha1-lib. With the shim loaded, ah_output_done runs on every SYN-ACK egress through a transport-mode AH SA and skb->sk arrives as a request_sock (TCP_NEW_SYN_RECV); after this patch, xfrm_output_resume() receives the listener (the result of sk_to_full_sk()) and consumer derefs land on full-sock fields as intended.
--- net/ipv4/ah4.c | 2 +- net/ipv6/ah6.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-)
net/ipv4/ah4.c +1 -1
@@ -143,7 +143,7 @@ static void ah_output_done(void *data, i }kfree (AH_SKB_CB (skb)->tmp); -xfrm_output_resume (skb->sk, skb, err); +xfrm_output_resume (skb_to_full_sk (skb), skb, err); }static int ah_output (struct xfrm_state *x,struct sk_buff *skb)
net/ipv6/ah6.c +1 -1
@@ -337,7 +337,7 @@ static void ah6_output_done(void *data,ah6_restore_hdrs (top_iph, iph_ext, extlen);kfree (AH_SKB_CB (skb)->tmp); -xfrm_output_resume (skb->sk, skb, err); +xfrm_output_resume (skb_to_full_sk (skb), skb, err); }static int ah6_output (struct xfrm_state *x,struct sk_buff *skb)
Greg Kroah-Hartman
maintainerPATCH 138/452 netfilter: conntrack: tcp: do not force CLOSE on invalid-seq RST without direction check
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Hamza Mahfooz <[email protected]>
commit bed6e04be8e6b9133d8b16d5a42d0e0ce674fa9a upstream.
An unintended behavior in the TCP conntrack state machine allows a connection to be forced into the CLOSE state using an RST packet with an invalid sequence number.
Specifically, after a SYN packet is observed, an RST with an invalid SEQ can transition the conntrack entry to TCP_CONNTRACK_CLOSE, regardless of whether the RST corresponds to the expected reply direction. The relevant code path assumes the RST is a response to an outgoing SYN, but does not validate packet direction or ensure that a matching SYN was actually sent in the opposite direction.
As a result, a crafted packet sequence consisting of a SYN followed by an invalid-sequence RST can prematurely terminate an active NAT entry. This makes connection teardown easier than intended.
So, tighten the state transition logic to ensure that RST-triggered CLOSE transitions only occur when the RST is a valid response to a previously observed SYN in the correct direction.
--- net/netfilter/nf_conntrack_proto_tcp.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
net/netfilter/nf_conntrack_proto_tcp.c +2 -1
@@ -1220,7 +1220,8 @@ int nf_conntrack_tcp_packet(struct nf_co new_state = old_state; }if (((test_bit (IPS_SEEN_REPLY_BIT , &ct->status) - && ct->proto.tcp.last_index ==TCP_SYN_SET ) + && ct->proto.tcp.last_index ==TCP_SYN_SET + && ct->proto.tcp.last_dir != dir) || (!test_bit (IPS_ASSURED_BIT , &ct->status) && ct->proto.tcp.last_index ==TCP_ACK_SET )) &&ntohl (th->ack_seq) == ct->proto.tcp.last_end) {
Greg Kroah-Hartman
maintainerPATCH 139/452 ASoC: qcom: q6asm-dai: close stream only when running
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Srinivas Kandagatla <[email protected]>
commit 048c540ee76ded666bda74f9dae1ca3254e0633c upstream.
q6asm_dai_close() and q6asm_dai_compr_free() currently issue CMD_CLOSE whenever prtd->state is non-zero.
After prepare() closes an existing stream, the state is updated to Q6ASM_STREAM_STOPPED. Since this state is also non-zero, the close and free paths can send CMD_CLOSE again for a stream that has already been closed.
Restrict CMD_CLOSE to the Q6ASM_STREAM_RUNNING state so the command is sent only when the ASM stream is still active.
--- sound/soc/qcom/qdsp6/q6asm-dai.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-)
sound/soc/qcom/qdsp6/q6asm-dai.c +7 -7
@@ -456,12 +456,12 @@ static int q6asm_dai_close(struct snd_sostruct q6asm_dai_rtd *prtd = runtime->private_data;if (prtd->audio_client) { -if (prtd->state) +if (prtd->state ==Q6ASM_STREAM_RUNNING ) {q6asm_cmd (prtd->audio_client, prtd->stream_id,CMD_CLOSE ); - -q6asm_unmap_memory_regions (substream->stream, +q6asm_unmap_memory_regions (substream->stream, prtd->audio_client); + }q6asm_audio_client_free (prtd->audio_client); prtd->audio_client =NULL ; }
@@ -678,7 +678,7 @@ static int q6asm_dai_compr_free(struct sstruct snd_soc_pcm_runtime *rtd = stream->private_data;if (prtd->audio_client) { -if (prtd->state) { +if (prtd->state ==Q6ASM_STREAM_RUNNING ) {q6asm_cmd (prtd->audio_client, prtd->stream_id,CMD_CLOSE );if (prtd->next_track_stream_id) {
@@ -686,11 +686,11 @@ static int q6asm_dai_compr_free(struct s prtd->next_track_stream_id, CMD_CLOSE); } - } -snd_dma_free_pages (&prtd->dma_buffer); -q6asm_unmap_memory_regions (stream->direction, +q6asm_unmap_memory_regions (stream->direction, prtd->audio_client); + } +snd_dma_free_pages (&prtd->dma_buffer);q6asm_audio_client_free (prtd->audio_client); prtd->audio_client =NULL ; }
Greg Kroah-Hartman
maintainerPATCH 140/452 ASoC: qcom: q6asm-dai: do not set stream state in event and trigger callbacks
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Srinivas Kandagatla <[email protected]>
commit cee3e63e7106c3c81b2053371fdf14240bfba2fc upstream.
The q6asm-dai stream state is used by prepare() to decide whether an existing stream setup needs to be closed before opening/configuring a new one. Updating the state from trigger or asynchronous DSP callbacks can make that state stale or incorrect relative to the actual setup lifetime.
In particular, setting Q6ASM_STREAM_STOPPED on STOP or EOS completion can make prepare() believe there is no active setup to close, which can result in opening/configuring the same stream more than once.
Keep stream state updates tied to prepare(), where the stream is actually closed and reopened, and stop changing it from trigger and EOS callbacks.
--- sound/soc/qcom/qdsp6/q6asm-dai.c | 5 ----- 1 file changed, 5 deletions(-)
sound/soc/qcom/qdsp6/q6asm-dai.c +0 -5
@@ -191,7 +191,6 @@ static void event_handler(uint32_t opcod prtd->pcm_count, 0, 0, 0);break ;case ASM_CLIENT_EVENT_CMD_EOS_DONE : - prtd->state =Q6ASM_STREAM_STOPPED ;break ;case ASM_CLIENT_EVENT_DATA_WRITE_DONE : { prtd->pcm_irq_pos += prtd->pcm_count;
@@ -338,7 +337,6 @@ static int q6asm_dai_trigger(struct snd_ 0, 0, 0);break ;case SNDRV_PCM_TRIGGER_STOP : - prtd->state =Q6ASM_STREAM_STOPPED ; ret =q6asm_cmd_nowait (prtd->audio_client, prtd->stream_id,CMD_EOS );break ;
@@ -554,8 +552,6 @@ static void compress_event_handler(uint3snd_compr_drain_notify (prtd->cstream); prtd->notify_on_drain = false; - }else { - prtd->state =Q6ASM_STREAM_STOPPED ; }spin_unlock_irqrestore (&prtd->lock, flags);break ;
@@ -1018,7 +1014,6 @@ static int q6asm_dai_compr_trigger(struc 0, 0, 0);break ;case SNDRV_PCM_TRIGGER_STOP : - prtd->state =Q6ASM_STREAM_STOPPED ; ret =q6asm_cmd_nowait (prtd->audio_client, prtd->stream_id,CMD_EOS );break ;
Greg Kroah-Hartman
maintainerPATCH 141/452 xfrm: esp: restore combined single-frag length gate
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jingguo Tan <[email protected]>
commit dfa0d7b0ff1eb6b2c416b8fdb9b4f2cefba57a40 upstream.
The ESP out-of-place fast path appends the trailer in esp_output_head() before esp_output_tail() allocates the destination page frag. The head-side gate currently checks skb->data_len and tailen separately, but the tail code allocates a single destination frag from the combined post-trailer skb->data_len.
Reject the page-frag fast path when the combined aligned length exceeds a page. Otherwise skb_page_frag_refill() may fall back to a single page while the destination sg still spans the combined skb->data_len.
Restore this combined-length page gate for both IPv4 and IPv6.
--- net/ipv4/esp4.c | 4 ++-- net/ipv6/esp6.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-)
net/ipv4/esp4.c +2 -2
@@ -419,8 +419,8 @@ int esp_output_head(struct xfrm_state *xreturn err; } -if (ALIGN (tailen,L1_CACHE_BYTES ) >PAGE_SIZE || -ALIGN (skb->data_len,L1_CACHE_BYTES ) >PAGE_SIZE ) +if (ALIGN (skb->data_len + tailen,L1_CACHE_BYTES ) > +PAGE_SIZE ) goto cow;if (!skb_cloned (skb)) {
net/ipv6/esp6.c +2 -2
@@ -454,8 +454,8 @@ int esp6_output_head(struct xfrm_state *return err; } -if (ALIGN (tailen,L1_CACHE_BYTES ) >PAGE_SIZE || -ALIGN (skb->data_len,L1_CACHE_BYTES ) >PAGE_SIZE ) +if (ALIGN (skb->data_len + tailen,L1_CACHE_BYTES ) > +PAGE_SIZE ) goto cow;if (!skb_cloned (skb)) {
Greg Kroah-Hartman
maintainerPATCH 142/452 Input: xpad - add "Nova 2 Lite" from GameSir
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Qbeliw Tanaka <[email protected]>
commit 1f6ac0f8441c48c4cc250141e1da8486c13512ba upstream.
Add support for the gamepad "Nova 2 Lite" from GameSir, compatible with the Xbox 360 gamepad.
--- drivers/input/joystick/xpad.c | 1 + 1 file changed, 1 insertion(+)
drivers/input/joystick/xpad.c +1 -0
@@ -414,6 +414,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 }, { 0x3767, 0x0101, "Fanatec Speedster 3 Forceshock Wheel", 0, XTYPE_XBOX }, { 0x413d, 0x2104, "Black Shark Green Ghost Gamepad", 0, XTYPE_XBOX360 },
Greg Kroah-Hartman
maintainerPATCH 143/452 Input: xpad - add support for ASUS ROG RAIKIRI II
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Dmitriy Zharov <[email protected]>
commit c897cf120696b94f56ed0f3197ba9a77071a59ec upstream.
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.
--- drivers/input/joystick/xpad.c | 5 +++++ 1 file changed, 5 insertions(+)
drivers/input/joystick/xpad.c +5 -0
@@ -215,6 +215,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 },
@@ -527,6 +531,7 @@ static const struct usb_device_id xpad_t { 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 */
Greg Kroah-Hartman
maintainerPATCH 144/452 ksmbd: OOB read regression in smb_check_perm_dacl() ACE-walk loops
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ali Ganiyev <[email protected]>
commit 0e60dafe97eca61721f3db456f97d97a80c6c8ae upstream.
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().
--- fs/smb/server/smbacl.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
fs/smb/server/smbacl.c +4 -4
@@ -1342,8 +1342,8 @@ int smb_check_perm_dacl(struct ksmbd_con 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 ||
@@ -1366,8 +1366,8 @@ int smb_check_perm_dacl(struct ksmbd_con 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 ||
Greg Kroah-Hartman
maintainerPATCH 145/452 Input: atmel_mxt_ts - fix boundary check in mxt_prepare_cfg_mem
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Dmitry Torokhov <[email protected]>
commit baa0210fb6a9dc3882509a9411b6d284d88fe30e upstream.
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 >=.
--- drivers/input/touchscreen/atmel_mxt_ts.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/input/touchscreen/atmel_mxt_ts.c +1 -1
@@ -1476,7 +1476,7 @@ static int mxt_prepare_cfg_mem(struct mx } cfg->raw_pos += offset; -if (i >mxt_obj_size (object)) +if (i >=mxt_obj_size (object))continue ; byte_offset = reg + i - cfg->start_ofs;
Greg Kroah-Hartman
maintainerPATCH 146/452 Input: synaptics - add LEN2058 to SMBus passlist for ThinkPad E490
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Nicolás Bazaes <[email protected]>
commit 16ca52bc209fa4bf9239cd9e5643e95533476b58 upstream.
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.
--- drivers/input/mouse/synaptics.c | 1 + 1 file changed, 1 insertion(+)
drivers/input/mouse/synaptics.c +1 -0
@@ -189,6 +189,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 */
Greg Kroah-Hartman
maintainerPATCH 147/452 comedi: comedi_test: fix check for valid scan_begin_src in waveform_ai_cmdtest()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ian Abbott <[email protected]>
commit 542f5248cb481073203e0dadab5bcbd28aeae308 upstream.
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.
--- drivers/comedi/drivers/comedi_test.c | 1 + 1 file changed, 1 insertion(+)
drivers/comedi/drivers/comedi_test.c +1 -0
@@ -273,6 +273,7 @@ static int waveform_ai_cmdtest(struct co/* 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 */
Greg Kroah-Hartman
maintainerPATCH 148/452 comedi: comedi_test: Fix limiting of convert_arg in waveform_ai_cmdtest()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ian Abbott <[email protected]>
commit 8a3bee801d420be8a7a0bae4a26547b353b8fe22 upstream.
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.
--- drivers/comedi/drivers/comedi_test.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/comedi/drivers/comedi_test.c +2 -2
@@ -324,10 +324,10 @@ static int waveform_ai_cmdtest(struct co 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);
Greg Kroah-Hartman
maintainerPATCH 149/452 counter: Fix refcount leak in counter_alloc() error path
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Guangshuo Li <[email protected]>
commit d9eeb0ea0d2de658663bfaa9c26eccdd8fd64440 upstream.
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.
--- drivers/counter/counter-core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/counter/counter-core.c +2 -1
@@ -123,7 +123,8 @@ struct counter_device *counter_alloc(siz err_dev_set_name: -counter_chrdev_remove (counter); +put_device (dev); +return NULL ; err_chrdev_add:ida_free (&counter_ida, dev->id);
Greg Kroah-Hartman
maintainerPATCH 150/452 tty: serial: pch_uart: add check for dma_alloc_coherent()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zhaoyang Yu <[email protected]>
commit 6fe472c1bbbe238e91141f7cabc1226e96a60d43 upstream.
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.
--- drivers/tty/serial/pch_uart.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-)
drivers/tty/serial/pch_uart.c +13 -6
@@ -694,8 +694,7 @@ static void pch_request_dma(struct uart_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;
@@ -709,18 +708,26 @@ static void pch_request_dma(struct uart_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)
Greg Kroah-Hartman
maintainerPATCH 151/452 usb: chipidea: core: convert ci_role_switch to local variable
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xu Yang <[email protected]>
commit 8f6aa392653e52a45858cff5c063df550028836b upstream.
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.
--- drivers/usb/chipidea/core.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-)
drivers/usb/chipidea/core.c +6 -10
@@ -669,12 +669,6 @@ static enum ci_role ci_get_role(struct creturn 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) {
@@ -801,9 +795,6 @@ static int ci_get_platdata(struct device 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;
@@ -1045,6 +1036,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;
@@ -1191,7 +1183,11 @@ static int ci_hdrc_probe(struct platform } } -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);
Greg Kroah-Hartman
maintainerPATCH 152/452 usb: core: Fix up Interrupt IN endpoints with bogus wBytesPerInterval
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Michal Pecio <[email protected]>
commit 727d045d064b7c9a24db3bce9c0485a382cb768b upstream.
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.
--- drivers/usb/core/config.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-)
drivers/usb/core/config.c +8 -1
@@ -165,7 +165,14 @@ static void usb_parse_ss_endpoint_compan (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",
Greg Kroah-Hartman
maintainerPATCH 153/452 USB: quirks: add NO_LPM for Lenovo ThinkPad USB-C Dock Gen2 hub controllers
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Stephen J. Fuhry <[email protected]>
commit 9ddb9c0deca48d2c2a22ebf4d2f35c925a520328 upstream.
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.
--- drivers/usb/core/quirks.c | 4 ++++ 1 file changed, 4 insertions(+)
drivers/usb/core/quirks.c +4 -0
@@ -511,6 +511,10 @@ static const struct usb_device_id usb_qu/* 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 },
Greg Kroah-Hartman
maintainerPATCH 154/452 usb: storage: Add quirks for PNY Elite Portable SSD
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sam Burkels <[email protected]>
commit b53ebb811e00be50a779ce4e7aee604178b4a825 upstream.
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.
--- drivers/usb/storage/unusual_uas.h | 7 +++++++ 1 file changed, 7 insertions(+)
drivers/usb/storage/unusual_uas.h +7 -0
@@ -132,6 +132,13 @@ UNUSUAL_DEV(0x152d, 0x0583, 0x0000, 0x99 USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_NO_REPORT_OPCODES), +/* Reported-by: Sam Burkels <[email protected]> */ +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 <[email protected]> */ UNUSUAL_DEV(0x154b, 0xf00b, 0x0000, 0x9999, "PNY",
Greg Kroah-Hartman
maintainerPATCH 155/452 usbip: vudc: Fix use after free bug in vudc_remove due to race condition
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Michael Bommarito <[email protected]>
commit d96209626a29ea64666be98c30b30ac82e5f1be6 upstream.
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/0x100This 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.
--- drivers/usb/usbip/vudc_dev.c | 1 + drivers/usb/usbip/vudc_transfer.c | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-)
drivers/usb/usbip/vudc_dev.c +1 -0
@@ -632,6 +632,7 @@ int vudc_remove(struct platform_device * {struct vudc *udc =platform_get_drvdata (pdev); +v_stop_timer (udc);usb_del_gadget_udc (&udc->gadget);cleanup_vudc_hw (udc);kfree (udc);
drivers/usb/usbip/vudc_transfer.c +2 -1
@@ -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 ; }
Greg Kroah-Hartman
maintainerPATCH 156/452 usb: usbtmc: check URB actual_length for interrupt-IN notifications
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Heitor Alves de Siqueira <[email protected]>
commit 52f2ad3f7e5eb3b5908e1d685d4342519dc9cfcd upstream.
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.
--- drivers/usb/class/usbtmc.c | 8 ++++++++ 1 file changed, 8 insertions(+)
drivers/usb/class/usbtmc.c +8 -0
@@ -2310,6 +2310,14 @@ static void usbtmc_interrupt(struct urbswitch (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 ];
Greg Kroah-Hartman
maintainerPATCH 157/452 usb: usbtmc: reject interrupt endpoints with small wMaxPacketSize
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Heitor Alves de Siqueira <[email protected]>
commit 121d2f682ba912b1427cddca7cf84840f41cc620 upstream.
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.
--- drivers/usb/class/usbtmc.c | 6 ++++++ 1 file changed, 6 insertions(+)
drivers/usb/class/usbtmc.c +6 -0
@@ -2444,6 +2444,12 @@ static int usbtmc_probe(struct usb_inter 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); }
Greg Kroah-Hartman
maintainerPATCH 158/452 USB: serial: option: add MeiG SRM813Q
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jan Volckaert <[email protected]>
commit 7d2b37d3e42d19071b62f4ddbee6e16e905efbf1 upstream.
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
--- drivers/usb/serial/option.c | 6 ++++++ 1 file changed, 6 insertions(+)
drivers/usb/serial/option.c +6 -0
@@ -2450,6 +2450,12 @@ static const struct usb_device_id option { 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 */
Greg Kroah-Hartman
maintainerPATCH 159/452 USB: serial: option: add missing RSVD(5) flag for Rolling RW135R-GL
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Wanquan Zhong <[email protected]>
commit 689f2facc689c8add11d7ff69fbbad17d65ee596 upstream.
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
--- drivers/usb/serial/option.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/usb/serial/option.c +2 -1
@@ -2476,7 +2476,8 @@ static const struct usb_device_id option { 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) },
Greg Kroah-Hartman
maintainerPATCH 160/452 USB: serial: belkin_sa: validate interrupt status length
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zhang Cen <[email protected]>
commit 4ce058df2ee02cc2a0f0fd5cd64ce6f1482a0b65 upstream.
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() (?:?)
--- drivers/usb/serial/belkin_sa.c | 3 +++ 1 file changed, 3 insertions(+)
drivers/usb/serial/belkin_sa.c +3 -0
@@ -195,6 +195,9 @@ static void belkin_sa_read_int_callback(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] */
Greg Kroah-Hartman
maintainerPATCH 161/452 USB: serial: cypress_m8: validate interrupt packet headers
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zhang Cen <[email protected]>
commit 9f9bfc80c67f35a275820da7e83a35dface08281 upstream.
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()
--- drivers/usb/serial/cypress_m8.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-)
drivers/usb/serial/cypress_m8.c +11 -1
@@ -1020,8 +1020,8 @@ static void cypress_read_int_callback(stchar tty_flag =TTY_NORMAL ;int bytes =0 ;int result; -int i =0 ;int status = urb->status; +int i;switch (status) {case 0 :/* success */
@@ -1059,22 +1059,32 @@ static void cypress_read_int_callback(stspin_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",
Greg Kroah-Hartman
maintainerPATCH 162/452 USB: serial: keyspan: fix missing indat transfer sanity check
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Johan Hovold <[email protected]>
commit ab8336a7e414f018430aa1af3a46944032f7ff96 upstream.
Add the missing sanity check on the size of usa49wg indat transfers to avoid parsing stale or uninitialised slab data.
--- drivers/usb/serial/keyspan.c | 4 ++++ 1 file changed, 4 insertions(+)
drivers/usb/serial/keyspan.c +4 -0
@@ -1188,6 +1188,10 @@ static void usa49wg_indat_callback(struc 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) {
Greg Kroah-Hartman
maintainerPATCH 163/452 USB: serial: mxuport: fix memory corruption with small endpoint
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Johan Hovold <[email protected]>
commit 4085f0dbb1ce2251c9a5938d693de6593f0ab2bd upstream.
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.
--- drivers/usb/serial/mxuport.c | 8 ++++++++ 1 file changed, 8 insertions(+)
drivers/usb/serial/mxuport.c +8 -0
@@ -962,6 +962,14 @@ static int mxuport_calc_num_ports(struct*/ 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 ];
Greg Kroah-Hartman
maintainerPATCH 164/452 USB: serial: mct_u232: fix missing interrupt-in transfer sanity check
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Johan Hovold <[email protected]>
commit 245aba83e3c288e176ed037a1f6b618b09e92ed8 upstream.
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).
--- drivers/usb/serial/mct_u232.c | 5 +++++ 1 file changed, 5 insertions(+)
drivers/usb/serial/mct_u232.c +5 -0
@@ -544,6 +544,11 @@ static void mct_u232_read_int_callback(s 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.
Greg Kroah-Hartman
maintainerPATCH 165/452 usb: gadget: net2280: Fix double free in probe error path
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Guangshuo Li <[email protected]>
commit c8547c74988e0b5f4cbb1b895e2a57aae084f070 upstream.
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.
--- drivers/usb/gadget/udc/net2280.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-)
drivers/usb/gadget/udc/net2280.c +1 -3
@@ -3790,10 +3790,8 @@ static int net2280_probe(struct pci_devreturn 0 ; done: -if (dev) { +if (dev)net2280_remove (pdev); -kfree (dev); - }return retval; }
Greg Kroah-Hartman
maintainerPATCH 166/452 usb: gadget: f_hid: fix device reference leak in hidg_alloc()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Guangshuo Li <[email protected]>
commit 4f88d65def6f3c90121601b4f62a4c967f3063a6 upstream.
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.
--- drivers/usb/gadget/function/f_hid.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
drivers/usb/gadget/function/f_hid.c +1 -2
@@ -1282,7 +1282,7 @@ static struct usb_function *hidg_alloc(s 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;
@@ -1317,7 +1317,6 @@ static struct usb_function *hidg_alloc(s err_put_device:put_device (&hidg->dev); -err_unlock:mutex_unlock (&opts->lock);return ERR_PTR (ret); }
Greg Kroah-Hartman
maintainerPATCH 167/452 usb: gadget: composite: fix integer underflow in WebUSB GET_URL handling
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jeremy Erazo <[email protected]>
commit 6c5dbc104dadd79fc2923497c20bae759a18758c upstream.
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.
--- drivers/usb/gadget/composite.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
drivers/usb/gadget/composite.c +4 -1
@@ -2196,7 +2196,10 @@ unknown: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;
Greg Kroah-Hartman
maintainerPATCH 168/452 usb: gadget: dummy_hcd: Reject hub port requests for non-existent ports
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Seungjin Bae <[email protected]>
commit 7d9633528dd40e33964d2dc74a5abbf5c4d116ce upstream.
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.
--- drivers/usb/gadget/udc/dummy_hcd.c | 4 ++++ 1 file changed, 4 insertions(+)
drivers/usb/gadget/udc/dummy_hcd.c +4 -0
@@ -2132,6 +2132,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 ) {
@@ -2246,6 +2248,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 ) {
Greg Kroah-Hartman
maintainerPATCH 169/452 usb: gadget: f_fs: copy only received bytes on short ep0 read
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Michael Bommarito <[email protected]>
commit 4e036c10e7f4df5d951c69cc3697bc8e209c6d02 upstream.
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.
--- drivers/usb/gadget/function/f_fs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/usb/gadget/function/f_fs.c +1 -1
@@ -588,7 +588,7 @@ static ssize_t ffs_ep0_read(struct file/* 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;
Greg Kroah-Hartman
maintainerPATCH 170/452 thunderbolt: property: Reject u32 wrap in tb_property_entry_valid()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Michael Bommarito <[email protected]>
commit 01deda0152066c6c955f0619114ea6afa070aaec upstream.
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.
--- drivers/thunderbolt/property.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-)
drivers/thunderbolt/property.c +5 -1
@@ -8,6 +8,7 @@*/ #include <linux/err.h> +#include <linux/overflow.h> #include <linux/slab.h> #include <linux/string.h> #include <linux/uuid.h>
@@ -52,13 +53,16 @@ static inline void format_dwdata(void *dstatic 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 ;
Greg Kroah-Hartman
maintainerPATCH 171/452 thunderbolt: property: Reject dir_len < 4 to prevent size_t underflow
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Michael Bommarito <[email protected]>
commit de21b59c29e31c5108ddc04210631bbfab81b997 upstream.
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.
--- drivers/thunderbolt/property.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-)
drivers/thunderbolt/property.c +6 -2
@@ -174,10 +174,16 @@ static struct tb_property_dir *__tb_propif (!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_prop 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;
Greg Kroah-Hartman
maintainerPATCH 172/452 scsi: fcoe: Reject FIP descriptors with zero fip_dlen in CVL walker
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Michael Bommarito <[email protected]>
commit 9eed1bd59937e6828b00d2f2dfef631d964f3636 upstream.
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.
--- drivers/scsi/fcoe/fcoe_ctlr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/scsi/fcoe/fcoe_ctlr.c +1 -1
@@ -1386,7 +1386,7 @@ static void fcoe_ctlr_recv_clr_vlink(strwhile (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) &&
Greg Kroah-Hartman
maintainerPATCH 173/452 scsi: scsi_transport_fc: Widen FPIN pname walker counter to u32
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Michael Bommarito <[email protected]>
commit a9a39233ec1fc9f97ea1340a4d09bb7ec2be5153 upstream.
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.
--- drivers/scsi/scsi_transport_fc.c | 77 ++++++++++++++++++++------------------- 1 file changed, 41 insertions(+), 36 deletions(-)
drivers/scsi/scsi_transport_fc.c +41 -36
@@ -747,6 +747,37 @@ fc_cn_stats_update(u16 event_type, struc } } +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.
@@ -757,13 +788,11 @@ fc_cn_stats_update(u16 event_type, strucstatic 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));
@@ -774,22 +803,11 @@ fc_fpin_li_stats_update(struct Scsi_Hostfc_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);
@@ -837,13 +855,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));
@@ -854,22 +870,11 @@ fc_fpin_peer_congn_stats_update(struct Sfc_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); }/*
Greg Kroah-Hartman
maintainerPATCH 174/452 scsi: target: iscsi: Bound iscsi_encode_text_output() appends to rsp_buf
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Michael Bommarito <[email protected]>
commit bf33e01f88388c43e285492a63e539df6ffed64c upstream.
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.)
--- drivers/target/iscsi/iscsi_target_nego.c | 7 ++ drivers/target/iscsi/iscsi_target_parameters.c | 62 +++++++++++++++++++------ drivers/target/iscsi/iscsi_target_parameters.h | 2 3 files changed, 55 insertions(+), 16 deletions(-)
drivers/target/iscsi/iscsi_target_nego.c +6 -1
@@ -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(sSENDER_TARGET , login->rsp_buf, &login->rsp_length, +MAX_KEY_VALUE_PAIRS , conn->param_list, conn->tpg->tpg_attrib.login_keys_workaround);if (ret <0 ) {
drivers/target/iscsi/iscsi_target_parameters.c +48 -14
101 lines not on this page · raw message
drivers/target/iscsi/iscsi_target_parameters.h +1 -1
@@ -46,7 +46,7 @@ extern struct iscsi_param *iscsi_find_paextern 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 *,
Greg Kroah-Hartman
maintainerPATCH 175/452 scsi: target: iscsi: Validate CHAP_R length before base64 decode
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alexandru Hossu <[email protected]>
commit 85db7391310b1304d2dc8ae3b0b12105a9567147 upstream.
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.
--- drivers/target/iscsi/iscsi_target_auth.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-)
drivers/target/iscsi/iscsi_target_auth.c +18 -1
@@ -339,13 +339,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;
@@ -472,6 +481,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));
Greg Kroah-Hartman
maintainerPATCH 176/452 drm/hyperv: validate resolution_count and fix WIN8 fallback
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Berkant Koc <[email protected]>
commit 13d33b9ef67066c77c84273fac5a1d3fde3533d1 upstream.
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.
--- drivers/gpu/drm/hyperv/hyperv_drm_proto.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-)
drivers/gpu/drm/hyperv/hyperv_drm_proto.c +10 -3
@@ -396,8 +396,11 @@ static int hyperv_get_supported_resolutireturn -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 ; }
@@ -513,9 +516,13 @@ int hyperv_connect_vsp(struct hv_device 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;
Greg Kroah-Hartman
maintainerPATCH 177/452 drm/hyperv: validate VMBus packet size in receive callback
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Berkant Koc <[email protected]>
commit 7f87763f47a3c22fb50265a00619ef10f2394b18 upstream.
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().
--- drivers/gpu/drm/hyperv/hyperv_drm_proto.c | 100 ++++++++++++++++++++++++++---- 1 file changed, 87 insertions(+), 13 deletions(-)
drivers/gpu/drm/hyperv/hyperv_drm_proto.c +87 -13
@@ -425,30 +425,92 @@ static int hyperv_get_supported_resolutireturn 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)
@@ -469,9 +531,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 ); }
Greg Kroah-Hartman
maintainerPATCH 178/452 drm/i915: Fix potential UAF in TTM object purge
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Janusz Krzysztofik <[email protected]>
commit 5c4063c87a619e4df954c179d24628636f5db15f upstream.
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] <TASK> [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).
--- drivers/gpu/drm/i915/gem/i915_gem_ttm.c | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-)
drivers/gpu/drm/i915/gem/i915_gem_ttm.c +16 -12
@@ -423,8 +423,6 @@ void i915_ttm_free_cached_io_rsgt(structint 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,
@@ -439,16 +437,22 @@ int i915_ttm_purge(struct drm_i915_gem_oif (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 ;
Greg Kroah-Hartman
maintainerPATCH 179/452 drm/amd/pm/si: Disregard vblank time when no displays are connected
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Timur Kristóf <[email protected]>
commit dd4f3ee535b3b0ac027f75dbf9dc5fc88733c765 upstream.
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.
--- drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.c | 4 ++++ 1 file changed, 4 insertions(+)
drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.c +4 -0
@@ -3062,6 +3062,10 @@ static bool si_dpm_vblank_too_short(void/* 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.
Greg Kroah-Hartman
maintainerPATCH 180/452 serial: altera_jtaguart: handle uart_add_one_port() failures
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Myeonghun Pak <[email protected]>
commit ea66be25f0e934f49d24cd0c5845d13cdba3520b upstream.
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.
--- drivers/tty/serial/altera_jtaguart.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-)
drivers/tty/serial/altera_jtaguart.c +6 -1
@@ -381,6 +381,7 @@ static int altera_jtaguart_probe(structstruct 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 )
@@ -420,7 +421,11 @@ static int altera_jtaguart_probe(struct 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 ; }
Greg Kroah-Hartman
maintainerPATCH 181/452 serial: qcom-geni: fix UART_RX_PAR_EN bit position
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Prasanna S <[email protected]>
commit ca2584d841b69391ffc4144840563d2e1a0018df upstream.
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.
--- drivers/tty/serial/qcom_geni_serial.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/tty/serial/qcom_geni_serial.c +1 -1
@@ -46,7 +46,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)
Greg Kroah-Hartman
maintainerPATCH 182/452 serial: sh-sci: fix memory region release in error path
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Hongling Zeng <[email protected]>
commit 92b1ea22454b08a39baef3a7290fb3ec50366616 upstream.
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().
--- drivers/tty/serial/sh-sci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/tty/serial/sh-sci.c +1 -1
@@ -2818,7 +2818,7 @@ static int sci_request_port(struct uart_ ret =sci_remap_port (port);if (unlikely (ret !=0 )) { -release_resource (res); +release_mem_region (port->mapbase, sport->reg_size);return ret; }
Greg Kroah-Hartman
maintainerPATCH 183/452 serial: zs: Fix swapped RI/DSR modem line transition counting
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Maciej W. Rozycki <[email protected]>
commit d15cd40cb1858f75846eaafa9a6bca841b790a92 upstream.
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.
--- drivers/tty/serial/zs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/tty/serial/zs.c +2 -2
@@ -679,9 +679,9 @@ static void zs_status_handle(struct zs_puart_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);
Greg Kroah-Hartman
maintainerPATCH 184/452 serial: fsl_lpuart: fix rx buffer and DMA map leaks in start_rx_dma
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Shitalkumar Gandhi <[email protected]>
commit 9a9254c4a2a3ca2b3da16d173f3b0dd01f397ff6 upstream.
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.
--- drivers/tty/serial/fsl_lpuart.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-)
drivers/tty/serial/fsl_lpuart.c +12 -3
@@ -1383,7 +1383,8 @@ static inline int lpuart_start_rx_dma(stif (!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);
@@ -1395,7 +1396,7 @@ static inline int lpuart_start_rx_dma(stif (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,
@@ -1406,7 +1407,8 @@ static inline int lpuart_start_rx_dma(st 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;
@@ -1430,6 +1432,13 @@ static inline int lpuart_start_rx_dma(st }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)
Greg Kroah-Hartman
maintainerPATCH 185/452 drm/amdkfd: fix NULL pointer bug in svm_range_set_attr
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Huang <[email protected]>
commit e984d61d92e702096058f0f828f4b2b8563b88ce upstream.
The process_info could be NULL if user doesn't call kfd_ioctl_acquire_vm before calling kfd_ioctl_svm.
--- drivers/gpu/drm/amd/amdkfd/kfd_svm.c | 3 +++ 1 file changed, 3 insertions(+)
drivers/gpu/drm/amd/amdkfd/kfd_svm.c +3 -0
@@ -3567,6 +3567,9 @@ svm_range_set_attr(struct kfd_process *p svms = &p->svms; +if (!process_info) +return -EINVAL ; +mutex_lock (&process_info->lock);svm_range_list_lock_and_flush_work (svms, mm);
Greg Kroah-Hartman
maintainerPATCH 186/452 drm/amdkfd: fix a vulnerability of integer overflow in kfd debugger
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Huang <[email protected]>
commit 93f5534b35a05ef8a0109c1eefa800062fee810a upstream.
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.
--- drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-)
drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c +5 -3
@@ -2786,12 +2786,14 @@ static void copy_context_work_handler (sstatic 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,
Greg Kroah-Hartman
maintainerPATCH 187/452 drm/amdkfd: Check for pdd drm file first in CRIU restore path
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Francis <[email protected]>
commit 6842b6a4b72da9b2906ffc5ca9d846ace2c54c14 upstream.
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.
--- drivers/gpu/drm/amd/amdkfd/kfd_chardev.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-)
drivers/gpu/drm/amd/amdkfd/kfd_chardev.c +5 -5
@@ -2265,6 +2265,11 @@ static int criu_restore_devices(struct k 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);
@@ -2274,11 +2279,6 @@ static int criu_restore_devices(struct k ret = -EINVAL ; 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)) {
Greg Kroah-Hartman
maintainerPATCH 188/452 serial: dz: Fix bootconsole message clobbering at chip reset
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Maciej W. Rozycki <[email protected]>
commit ca904f4b42355287bc5ce8b7550ebe909cda4c2c upstream.
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.
--- drivers/tty/serial/dz.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+)
drivers/tty/serial/dz.c +21 -0
@@ -543,10 +543,31 @@ static int dz_encode_baud_rate(unsignedstatic 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 ();
Greg Kroah-Hartman
maintainerPATCH 189/452 serial: zs: Fix bootconsole handover lockup
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Maciej W. Rozycki <[email protected]>
commit 6c05cf72e13314ce9b770b5951695dc5a2152920 upstream.
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.
--- drivers/tty/serial/zs.c | 29 ++++++++--------------------- 1 file changed, 8 insertions(+), 21 deletions(-)
drivers/tty/serial/zs.c +8 -21
@@ -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 */ };
@@ -955,23 +961,6 @@ static void zs_set_termios(struct uart_pspin_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) {
@@ -1054,7 +1043,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,
@@ -1209,7 +1197,6 @@ static int __init zs_console_setup(strucreturn ret;zs_reset (zport); -zs_pm (uport,0 ,-1 );if (options)uart_parse_options (options, &baud, &parity, &bits, &flow);
Greg Kroah-Hartman
maintainerPATCH 190/452 serial: zs: Switch to using channel reset
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Maciej W. Rozycki <[email protected]>
commit 8572955630f30948837088aa98bcbe0532d1ceac upstream.
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.
--- drivers/tty/serial/zs.c | 7 ++++--- drivers/tty/serial/zs.h | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-)
drivers/tty/serial/zs.c +4 -3
@@ -831,21 +831,22 @@ static void zs_shutdown(struct uart_portstatic 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);
drivers/tty/serial/zs.h +1 -1
@@ -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__ */
Greg Kroah-Hartman
maintainerPATCH 191/452 USB: serial: cypress_m8: fix memory corruption with small endpoint
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Johan Hovold <[email protected]>
commit e1a9d791fd66ab2431b9e6f6f835823809869047 upstream.
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.
--- drivers/usb/serial/cypress_m8.c | 8 ++++++++ 1 file changed, 8 insertions(+)
drivers/usb/serial/cypress_m8.c +8 -0
@@ -448,6 +448,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 (sizeof (struct cypress_private ),GFP_KERNEL );if (!priv)return -ENOMEM ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 192/452 Bluetooth: hci_sync: fix UAF in hci_le_create_cis_sync
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Doruk Tan Ozturk <[email protected]>
[ Upstream commit bfea6091e0fffb270c20e74384b660910277eb6c ]
hci_le_create_cis_sync() dereferences conn->conn_timeout after releasing both rcu_read_lock() and hci_dev_lock(hdev). The conn pointer was obtained from an RCU-protected iteration over hdev->conn_hash.list and is not valid once these locks are dropped. A concurrent disconnect can free the hci_conn between the unlock and the dereference, causing a use-after-free read.
The cancellation mechanism in hci_conn_del() cannot prevent this because hci_le_create_cis_pending() queues hci_create_cis_sync with data=NULL:
hci_cmd_sync_queue(hdev, hci_create_cis_sync, NULL, NULL);
While hci_conn_del() dequeues with data=conn:
hci_cmd_sync_dequeue(hdev, NULL, conn, NULL);
Since NULL != conn, the lookup in _hci_cmd_sync_lookup_entry() never matches, and the pending work item is not cancelled.
Fix this by saving conn->conn_timeout into a local variable while the locks are still held, so the stale conn pointer is never dereferenced after unlock.
This is the same class of bug as the one fixed by commit 035c25007c9e ("Bluetooth: hci_sync: Fix UAF on le_read_features_complete") which addressed the identical pattern in a different function.
This vulnerability was identified using 0sec.ai, an open-source automated security auditing platform (https://github.com/0sec-labs).
[doruk: adjust context for 6.6 \u2014 open-coded cmd struct instead of
DEFINE_FLEX, num_cis tracked via cmd.cp.num_cis]
--- net/bluetooth/hci_sync.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
net/bluetooth/hci_sync.c +3 -1
@@ -6631,6 +6631,7 @@ int hci_le_create_cis_sync(struct hci_dev *hdev)struct hci_cis cis[0x1f ]; } cmd;struct hci_conn *conn; +u16 timeout =0 ;u8 cig =BT_ISO_QOS_CIG_UNSET ;/* The spec allows only one pending LE Create CIS command at a time. If
@@ -6703,6 +6704,7 @@ int hci_le_create_cis_sync(struct hci_dev *hdev)set_bit (HCI_CONN_CREATE_CIS , &conn->flags); cis->acl_handle =cpu_to_le16 (conn->parent->handle); cis->cis_handle =cpu_to_le16 (conn->handle); + timeout = conn->conn_timeout; cmd.cp.num_cis++;if (cmd.cp.num_cis >=ARRAY_SIZE (cmd.cis))
@@ -6722,7 +6724,7 @@ int hci_le_create_cis_sync(struct hci_dev *hdev)sizeof (cmd.cp) +sizeof (cmd.cis[0 ]) * cmd.cp.num_cis, &cmd,HCI_EVT_LE_CIS_ESTABLISHED , - conn->conn_timeout, NULL); + timeout, NULL); }int hci_le_remove_cig_sync (struct hci_dev *hdev,u8 handle)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 193/452 x86/kexec: Disable KCOV instrumentation after load_segments()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Aleksandr Nogikh <[email protected]>
[ Upstream commit 917e3ad3321e75ca0223d5ccf26ceda116aa51e1 ]
The load_segments() function changes segment registers, invalidating GS base (which KCOV relies on for per-cpu data). When CONFIG_KCOV is enabled, any subsequent instrumented C code call (e.g. native_gdt_invalidate()) begins crashing the kernel in an endless loop.
To reproduce the problem, it's sufficient to do kexec on a KCOV-instrumented kernel:
$ kexec -l /boot/otherKernel $ kexec -e
The real-world context for this problem is enabling crash dump collection in syzkaller. For this, the tool loads a panic kernel before fuzzing and then calls makedumpfile after the panic. This workflow requires both CONFIG_KEXEC and CONFIG_KCOV to be enabled simultaneously.
Adding safeguards directly to the KCOV fast-path (__sanitizer_cov_trace_pc()) is also undesirable as it would introduce an extra performance overhead.
Disabling instrumentation for the individual functions would be too fragile, so disable KCOV instrumentation for the entire machine_kexec_64.c and physaddr.c. If coverage-guided fuzzing ever needs these components in the future, other approaches should be considered.
The problem is not relevant for 32 bit kernels as CONFIG_KCOV is not supported there.
[ bp: Space out comment for better readability. ]
--- arch/x86/kernel/Makefile | 14 ++++++++++++++ arch/x86/mm/Makefile | 2 ++ 2 files changed, 16 insertions(+)
arch/x86/kernel/Makefile +14 -0
@@ -39,6 +39,20 @@ KMSAN_SANITIZE_nmi.o := n KCOV_INSTRUMENT_head$(BITS).o := n KCOV_INSTRUMENT_sev.o := n +# Disable KCOV to prevent crashes during kexec: load_segments() invalidates +# the GS base, which KCOV relies on for per-CPU data. +# +# As KCOV and KEXEC compatibility should be preserved (e.g. syzkaller is +# using it to collect crash dumps during kernel fuzzing), disabling +# KCOV for KEXEC kernels is not an option. Selectively disabling KCOV +# instrumentation for individual affected functions can be fragile, while +# adding more checks to KCOV would slow it down. +# +# As a compromise solution, disable KCOV instrumentation for the whole +# source code file. If its coverage is ever needed, other approaches +# should be considered. +KCOV_INSTRUMENT_machine_kexec_64.o := n + CFLAGS_irq.o := -I $(srctree)/$(src)/../include/asm/trace obj-y += head_$(BITS).o
arch/x86/mm/Makefile +2 -0
@@ -5,6 +5,8 @@ KCOV_INSTRUMENT_mem_encrypt.o := n KCOV_INSTRUMENT_mem_encrypt_amd.o := n KCOV_INSTRUMENT_mem_encrypt_identity.o := n KCOV_INSTRUMENT_pgprot.o := n +# See the "Disable KCOV" comment in arch/x86/kernel/Makefile. +KCOV_INSTRUMENT_physaddr.o := n KASAN_SANITIZE_mem_encrypt.o := n KASAN_SANITIZE_mem_encrypt_amd.o := n
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 194/452 landlock: Fix handling of disconnected directories
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mickaël Salaün <[email protected]>
[ Upstream commit 49c9e09d961025b22e61ef9ad56aa1c21b6ce2f1 ]
Disconnected files or directories can appear when they are visible and opened from a bind mount, but have been renamed or moved from the source of the bind mount in a way that makes them inaccessible from the mount point (i.e. out of scope).
Previously, access rights tied to files or directories opened through a disconnected directory were collected by walking the related hierarchy down to the root of the filesystem, without taking into account the mount point because it couldn't be found. This could lead to inconsistent access results, potential access right widening, and hard-to-debug renames, especially since such paths cannot be printed.
For a sandboxed task to create a disconnected directory, it needs to have write access (i.e. FS_MAKE_REG, FS_REMOVE_FILE, and FS_REFER) to the underlying source of the bind mount, and read access to the related
mount point. Because a sandboxed task cannot acquire more access
rights than those defined by its Landlock domain, this could lead to inconsistent access rights due to missing permissions that should be inherited from the mount point hierarchy, while inheriting permissions from the filesystem hierarchy hidden by this mount point instead.
Landlock now handles files and directories opened from disconnected directories by taking into account the filesystem hierarchy when the mount point is not found in the hierarchy walk, and also always taking into account the mount point from which these disconnected directories were opened. This ensures that a rename is not allowed if it would widen access rights [1].
The rationale is that, even if disconnected hierarchies might not be visible or accessible to a sandboxed task, relying on the collected access rights from them improves the guarantee that access rights will not be widened during a rename because of the access right comparison between the source and the destination (see LANDLOCK_ACCESS_FS_REFER). It may look like this would grant more access on disconnected files and directories, but the security policies are always enforced for all the evaluated hierarchies. This new behavior should be less surprising to users and safer from an access control perspective.
Remove a wrong WARN_ON_ONCE() canary in collect_domain_accesses() and fix the related comment.
Because opened files have their access rights stored in the related file security properties, there is no impact for disconnected or unlinked files.
--- security/landlock/errata/abi-1.h | 16 +++++++++++++ security/landlock/fs.c | 40 +++++++++++++++++++++++--------- 2 files changed, 45 insertions(+), 11 deletions(-) create mode 100644 security/landlock/errata/abi-1.h
security/landlock/errata/abi-1.h new file +16 -0
@@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +/** +* DOC: erratum_3 +* +* Erratum 3: Disconnected directory handling +* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +* +* This fix addresses an issue with disconnected directories that occur when a +* directory is moved outside the scope of a bind mount. The change ensures +* that evaluated access rights include both those from the disconnected file +* hierarchy down to its filesystem root and those from the related mount point +* hierarchy. This prevents access right widening through rename or link +* actions. +*/ +LANDLOCK_ERRATUM (3 )
security/landlock/fs.c +29 -11
@@ -616,19 +616,31 @@ static bool is_access_to_paths_allowed(break ; } } +if (unlikely (IS_ROOT (walker_path.dentry))) { +if (likely (walker_path.mnt->mnt_flags &MNT_INTERNAL )) { +/* +* Stops and allows access when reaching disconnected root +* directories that are part of internal filesystems (e.g. nsfs, +* which is reachable through /proc/<pid>/ns/<namespace>). +*/ + allowed_parent1 = true; + allowed_parent2 = true; +break ; + } +/* -* Stops at disconnected root directories. Only allows -* access to internal filesystems (e.g. nsfs, which is -* reachable through /proc/<pid>/ns/<namespace>). +* We reached a disconnected root directory from a bind mount. +* Let's continue the walk with the mount point we missed. */ - allowed_parent1 = allowed_parent2 = - !!(walker_path.mnt->mnt_flags &MNT_INTERNAL ); -break ; +dput (walker_path.dentry); + walker_path.dentry = walker_path.mnt->mnt_root; +dget (walker_path.dentry); + }else { + parent_dentry =dget_parent (walker_path.dentry); +dput (walker_path.dentry); + walker_path.dentry = parent_dentry; } - parent_dentry =dget_parent (walker_path.dentry); -dput (walker_path.dentry); - walker_path.dentry = parent_dentry; }path_put (&walker_path);
@@ -705,6 +717,9 @@ static inline access_mask_t maybe_remove(const struct dentry *const dentry)* file. While walking from @dir to @mnt_root, we record all the domain's * allowed accesses in @layer_masks_dom. * +* Because of disconnected directories, this walk may not reach @mnt_dir. In +* this case, the walk will continue to @mnt_dir after this call. +* * This is similar to is_access_to_paths_allowed() but much simpler because it * only handles walking on the same mount point and only checks one set of * accesses.
@@ -744,8 +759,11 @@ static bool collect_domain_accesses(break ; } -/* We should not reach a root other than @mnt_root. */ -if (dir == mnt_root ||WARN_ON_ONCE (IS_ROOT (dir))) +/* +* Stops at the mount point or the filesystem root for a disconnected +* directory. +*/ +if (dir == mnt_root ||unlikely (IS_ROOT (dir)))break ; parent_dentry =dget_parent (dir);
Signature · 1 line
2.53.0
PATCH 1/2 selftests/landlock: Add tests for access through disconnected paths
From: Tingmao Wang <[email protected]>
[ Upstream commit a18ee3f31fd714173a62515d049d77e76ab55649 ]
This adds tests for the edge case discussed in [1], with specific ones for rename and link operations when the operands are through disconnected paths, as that go through a separate code path in Landlock.
This has resulted in a warning, due to collect_domain_accesses() not expecting to reach a different root from path->mnt:
# RUN layout1_bind.path_disconnected ...
# OK layout1_bind.path_disconnected
ok 96 layout1_bind.path_disconnected
# RUN layout1_bind.path_disconnected_rename ...
[..] ------------[ cut here ]------------
[..] WARNING: CPU: 3 PID: 385 at security/landlock/fs.c:1065 collect_domain_accesses
[..] ...
[..] RIP: 0010:collect_domain_accesses (security/landlock/fs.c:1065 (discriminator 2) security/landlock/fs.c:1031 (discriminator 2)) [..] current_check_refer_path (security/landlock/fs.c:1205)
[..] ...
[..] hook_path_rename (security/landlock/fs.c:1526)
[..] security_path_rename (security/security.c:2026 (discriminator 1))
[..] do_renameat2 (fs/namei.c:5264)
# OK layout1_bind.path_disconnected_rename
ok 97 layout1_bind.path_disconnected_rename
Move the const char definitions a bit above so that we can use the path for s4d1 in cleanup code.
--- tools/testing/selftests/landlock/fs_test.c | 423 ++++++++++++++++++++- 1 file changed, 415 insertions(+), 8 deletions(-)
tools/testing/selftests/landlock/fs_test.c +415 -8
@@ -3783,6 +3783,18 @@ TEST(memfd_ftruncate)FIXTURE (layout1_bind) {};/* clang-format on */ +static const char bind_dir_s1d3[] =TMP_DIR "/s2d1/s2d2/s1d3" ; +static const char bind_file1_s1d3[] =TMP_DIR "/s2d1/s2d2/s1d3/f1" ; + +/* Move targets for disconnected path tests. */ +static const char dir_s4d1[] =TMP_DIR "/s4d1" ; +static const char file1_s4d1[] =TMP_DIR "/s4d1/f1" ; +static const char file2_s4d1[] =TMP_DIR "/s4d1/f2" ; +static const char dir_s4d2[] =TMP_DIR "/s4d1/s4d2" ; +static const char file1_s4d2[] =TMP_DIR "/s4d1/s4d2/f1" ; +static const char file1_name[] ="f1" ; +static const char file2_name[] ="f2" ; +FIXTURE_SETUP (layout1_bind) {prepare_layout (_metadata);
@@ -3800,14 +3812,14 @@ FIXTURE_TEARDOWN(layout1_bind)EXPECT_EQ (0 ,umount (dir_s2d2));clear_cap (_metadata,CAP_SYS_ADMIN ); +remove_path (file1_s4d1); +remove_path (file2_s4d1); +remove_layout1 (_metadata);cleanup_layout (_metadata); } -static const char bind_dir_s1d3[] =TMP_DIR "/s2d1/s2d2/s1d3" ; -static const char bind_file1_s1d3[] =TMP_DIR "/s2d1/s2d2/s1d3/f1" ; -/* * layout1_bind hierarchy: *
@@ -3818,20 +3830,25 @@ static const char bind_file1_s1d3[] = TMP_DIR "/s2d1/s2d2/s1d3/f1";* │ └── s1d2 * │ ├── f1 * │ ├── f2 -* │ └── s1d3 +* │ └── s1d3 [disconnected by path_disconnected] * │ ├── f1 * │ └── f2 * ├── s2d1 * │ ├── f1 -* │ └── s2d2 +* │ └── s2d2 [bind mount from s1d2] * │ ├── f1 * │ ├── f2 * │ └── s1d3 * │ ├── f1 * │ └── f2 -* └── s3d1 -* └── s3d2 -* └── s3d3 +* ├── s3d1 +* │ └── s3d2 +* │ └── s3d3 +* └── s4d1 [renamed from s1d3 by path_disconnected] +* ├── f1 +* ├── f2 +* └── s4d2 +* └── f1 */ TEST_F_FORK (layout1_bind, no_restriction)
@@ -4030,6 +4047,396 @@ TEST_F_FORK(layout1_bind, reparent_cross_mount)ASSERT_EQ (0 ,rename (bind_file1_s1d3, file1_s2d2)); } +/* +* Make sure access to file through a disconnected path works as expected. +* This test moves s1d3 to s4d1. +*/ +TEST_F_FORK(layout1_bind, path_disconnected) +{ +const struct rule layer1_allow_all[] = { + { + .path =TMP_DIR , + .access =ACCESS_ALL , + }, + {}, + }; +const struct rule layer2_allow_just_f1[] = { + { + .path = file1_s1d3, + .access =LANDLOCK_ACCESS_FS_READ_FILE , + }, + {}, + }; +const struct rule layer3_only_s1d2[] = { + { + .path = dir_s1d2, + .access =LANDLOCK_ACCESS_FS_READ_FILE , + }, + {}, + }; + +/* Landlock should not deny access just because it is disconnected. */ +int ruleset_fd_l1 = +create_ruleset (_metadata,ACCESS_ALL , layer1_allow_all); + +/* Creates the new ruleset now before we move the dir containing the file. */ +int ruleset_fd_l2 = +create_ruleset (_metadata,ACCESS_RW , layer2_allow_just_f1); +int ruleset_fd_l3 = +create_ruleset (_metadata,ACCESS_RW , layer3_only_s1d2); +int bind_s1d3_fd; + +ASSERT_LE (0 , ruleset_fd_l1); +ASSERT_LE (0 , ruleset_fd_l2); +ASSERT_LE (0 , ruleset_fd_l3); + +enforce_ruleset (_metadata, ruleset_fd_l1); +EXPECT_EQ (0 ,close (ruleset_fd_l1)); + + bind_s1d3_fd =open (bind_dir_s1d3,O_PATH |O_CLOEXEC ); +ASSERT_LE (0 , bind_s1d3_fd); + +/* Tests access is possible before we move. */ +EXPECT_EQ (0 ,test_open_rel (bind_s1d3_fd, file1_name,O_RDONLY )); +EXPECT_EQ (0 ,test_open_rel (bind_s1d3_fd, file2_name,O_RDONLY )); +EXPECT_EQ (0 ,test_open_rel (bind_s1d3_fd,".." ,O_RDONLY |O_DIRECTORY )); + +/* Makes it disconnected. */ + ASSERT_EQ(0, rename(dir_s1d3, dir_s4d1)) + { +TH_LOG ("Failed to rename %s to %s: %s" , dir_s1d3, dir_s4d1, +strerror (errno)); + } + +/* Tests that access is still possible. */ +EXPECT_EQ (0 ,test_open_rel (bind_s1d3_fd, file1_name,O_RDONLY )); +EXPECT_EQ (0 ,test_open_rel (bind_s1d3_fd, file2_name,O_RDONLY )); + +/* +* Tests that ".." is not possible (not because of Landlock, but just +* because it's disconnected). +*/ +EXPECT_EQ (ENOENT , +test_open_rel (bind_s1d3_fd,".." ,O_RDONLY |O_DIRECTORY )); + +/* This should still work with a narrower rule. */ +enforce_ruleset (_metadata, ruleset_fd_l2); +EXPECT_EQ (0 ,close (ruleset_fd_l2)); + +EXPECT_EQ (0 ,test_open (file1_s4d1,O_RDONLY )); +/* +* Accessing a file through a disconnected file descriptor can still be +* allowed by a rule tied to this file, even if it is no longer visible in +* its mount point. +*/ +EXPECT_EQ (0 ,test_open_rel (bind_s1d3_fd, file1_name,O_RDONLY )); +EXPECT_EQ (EACCES ,test_open_rel (bind_s1d3_fd, file2_name,O_RDONLY )); + +enforce_ruleset (_metadata, ruleset_fd_l3); +EXPECT_EQ (0 ,close (ruleset_fd_l3)); + +EXPECT_EQ (EACCES ,test_open (file1_s4d1,O_RDONLY )); +/* +* Accessing a file through a disconnected file descriptor can still be +* allowed by a rule tied to the original mount point, even if it is no +* longer visible in its mount point. +*/ +EXPECT_EQ (0 ,test_open_rel (bind_s1d3_fd, file1_name,O_RDONLY )); +EXPECT_EQ (EACCES ,test_open_rel (bind_s1d3_fd, file2_name,O_RDONLY )); +} + +/* +* Test that renameat with disconnected paths works under Landlock. This test +* moves s1d3 to s4d2, so that we can have a rule allowing refers on the move +* target's immediate parent. +*/ +TEST_F_FORK(layout1_bind, path_disconnected_rename) +{ +const struct rule layer1[] = { + { + .path = dir_s1d2, + .access =LANDLOCK_ACCESS_FS_REFER | +LANDLOCK_ACCESS_FS_MAKE_DIR | +LANDLOCK_ACCESS_FS_REMOVE_DIR | +LANDLOCK_ACCESS_FS_MAKE_REG | +LANDLOCK_ACCESS_FS_REMOVE_FILE | +LANDLOCK_ACCESS_FS_READ_FILE , + }, + { + .path = dir_s4d1, + .access =LANDLOCK_ACCESS_FS_REFER | +LANDLOCK_ACCESS_FS_MAKE_DIR | +LANDLOCK_ACCESS_FS_REMOVE_DIR | +LANDLOCK_ACCESS_FS_MAKE_REG | +LANDLOCK_ACCESS_FS_REMOVE_FILE | +LANDLOCK_ACCESS_FS_READ_FILE , + }, + {} + }; + +/* This layer only handles LANDLOCK_ACCESS_FS_READ_FILE. */ +const struct rule layer2_only_s1d2[] = { + { + .path = dir_s1d2, + .access =LANDLOCK_ACCESS_FS_READ_FILE , + }, + {}, + }; +int ruleset_fd_l1, ruleset_fd_l2; +pid_t child_pid; +int bind_s1d3_fd, status; + + ASSERT_EQ(0, mkdir(dir_s4d1, 0755)) + { +TH_LOG ("Failed to create %s: %s" , dir_s4d1,strerror (errno)); + } + ruleset_fd_l1 =create_ruleset (_metadata,ACCESS_ALL , layer1); + ruleset_fd_l2 =create_ruleset (_metadata,LANDLOCK_ACCESS_FS_READ_FILE , + layer2_only_s1d2); +ASSERT_LE (0 , ruleset_fd_l1); +ASSERT_LE (0 , ruleset_fd_l2); + +enforce_ruleset (_metadata, ruleset_fd_l1); +EXPECT_EQ (0 ,close (ruleset_fd_l1)); + + bind_s1d3_fd =open (bind_dir_s1d3,O_PATH |O_CLOEXEC ); +ASSERT_LE (0 , bind_s1d3_fd); +EXPECT_EQ (0 ,test_open_rel (bind_s1d3_fd, file1_name,O_RDONLY )); + +/* Tests ENOENT priority over EACCES for disconnected directory. */ +EXPECT_EQ (EACCES ,test_open_rel (bind_s1d3_fd,".." ,O_DIRECTORY )); + ASSERT_EQ(0, rename(dir_s1d3, dir_s4d2)) + { +TH_LOG ("Failed to rename %s to %s: %s" , dir_s1d3, dir_s4d2, +strerror (errno)); + } +EXPECT_EQ (ENOENT ,test_open_rel (bind_s1d3_fd,".." ,O_DIRECTORY )); + +/* +* The file is no longer under s1d2 but we should still be able to access it +* with layer 2 because its mount point is evaluated as the first valid +* directory because it was initially a parent. Do a fork to test this so +* we don't prevent ourselves from renaming it back later. +*/ + child_pid =fork (); +ASSERT_LE (0 , child_pid); +if (child_pid ==0 ) { +enforce_ruleset (_metadata, ruleset_fd_l2); +EXPECT_EQ (0 ,close (ruleset_fd_l2)); +EXPECT_EQ (0 ,test_open_rel (bind_s1d3_fd, file1_name,O_RDONLY )); +EXPECT_EQ (EACCES ,test_open (file1_s4d2,O_RDONLY )); + +/* +* Tests that access widening checks indeed prevents us from renaming it +* back. +*/ +EXPECT_EQ (-1 ,rename (dir_s4d2, dir_s1d3)); +EXPECT_EQ (EXDEV , errno); + +/* +* Including through the now disconnected fd (but it should return +* EXDEV). +*/ +EXPECT_EQ (-1 ,renameat (bind_s1d3_fd, file1_name,AT_FDCWD , + file1_s2d2)); +EXPECT_EQ (EXDEV , errno); +_exit (_metadata->passed ?EXIT_SUCCESS :EXIT_FAILURE ); +return ; + } + +EXPECT_EQ (child_pid,waitpid (child_pid, &status,0 )); +EXPECT_EQ (1 ,WIFEXITED (status)); +EXPECT_EQ (EXIT_SUCCESS ,WEXITSTATUS (status)); + + ASSERT_EQ(0, rename(dir_s4d2, dir_s1d3)) + { +TH_LOG ("Failed to rename %s back to %s: %s" , dir_s4d1, dir_s1d3, +strerror (errno)); + } + +/* Now checks that we can access it under l2. */ + child_pid =fork (); +ASSERT_LE (0 , child_pid); +if (child_pid ==0 ) { +enforce_ruleset (_metadata, ruleset_fd_l2); +EXPECT_EQ (0 ,close (ruleset_fd_l2)); +EXPECT_EQ (0 ,test_open_rel (bind_s1d3_fd, file1_name,O_RDONLY )); +EXPECT_EQ (0 ,test_open (file1_s1d3,O_RDONLY )); +_exit (_metadata->passed ?EXIT_SUCCESS :EXIT_FAILURE ); +return ; + } + +EXPECT_EQ (child_pid,waitpid (child_pid, &status,0 )); +EXPECT_EQ (1 ,WIFEXITED (status)); +EXPECT_EQ (EXIT_SUCCESS ,WEXITSTATUS (status)); + +/* +* Also test that we can rename via a disconnected path. We move the +* dir back to the disconnected place first, then we rename file1 to +* file2 through our dir fd. +*/ + ASSERT_EQ(0, rename(dir_s1d3, dir_s4d2)) + { +TH_LOG ("Failed to rename %s to %s: %s" , dir_s1d3, dir_s4d2, +strerror (errno)); + } +ASSERT_EQ (0 , + renameat(bind_s1d3_fd, file1_name, bind_s1d3_fd, file2_name)) + { +TH_LOG ("Failed to rename %s to %s within disconnected %s: %s" , + file1_name, file2_name, bind_dir_s1d3,strerror (errno)); + } +EXPECT_EQ (0 ,test_open_rel (bind_s1d3_fd, file2_name,O_RDONLY )); + ASSERT_EQ(0, renameat(bind_s1d3_fd, file2_name, AT_FDCWD, file1_s2d2)) + { +TH_LOG ("Failed to rename %s to %s through disconnected %s: %s" , + file2_name, file1_s2d2, bind_dir_s1d3,strerror (errno)); + } +EXPECT_EQ (0 ,test_open (file1_s2d2,O_RDONLY )); +EXPECT_EQ (0 ,test_open (file1_s1d2,O_RDONLY )); + +/* Move it back using the disconnected path as the target. */ + ASSERT_EQ(0, renameat(AT_FDCWD, file1_s2d2, bind_s1d3_fd, file1_name)) + { +TH_LOG ("Failed to rename %s to %s through disconnected %s: %s" , + file1_s1d2, file1_name, bind_dir_s1d3,strerror (errno)); + } + +/* Now make it connected again. */ + ASSERT_EQ(0, rename(dir_s4d2, dir_s1d3)) + { +TH_LOG ("Failed to rename %s back to %s: %s" , dir_s4d2, dir_s1d3, +strerror (errno)); + } + +/* Checks again that we can access it under l2. */ +enforce_ruleset (_metadata, ruleset_fd_l2); +EXPECT_EQ (0 ,close (ruleset_fd_l2)); +EXPECT_EQ (0 ,test_open_rel (bind_s1d3_fd, file1_name,O_RDONLY )); +EXPECT_EQ (0 ,test_open (file1_s1d3,O_RDONLY )); +} + +/* +* Test that linkat(2) with disconnected paths works under Landlock. This +* test moves s1d3 to s4d1. +*/ +TEST_F_FORK(layout1_bind, path_disconnected_link) +{ +/* Ruleset to be applied after renaming s1d3 to s4d1. */ +const struct rule layer1[] = { + { + .path = dir_s4d1, + .access =LANDLOCK_ACCESS_FS_REFER | +LANDLOCK_ACCESS_FS_READ_FILE | +LANDLOCK_ACCESS_FS_MAKE_REG | +LANDLOCK_ACCESS_FS_REMOVE_FILE , + }, + { + .path = dir_s2d2, + .access =LANDLOCK_ACCESS_FS_REFER | +LANDLOCK_ACCESS_FS_READ_FILE | +LANDLOCK_ACCESS_FS_MAKE_REG | +LANDLOCK_ACCESS_FS_REMOVE_FILE , + }, + {} + }; +int ruleset_fd, bind_s1d3_fd; + +/* Removes unneeded files created by layout1, otherwise it will EEXIST. */ +ASSERT_EQ (0 ,unlink (file1_s1d2)); +ASSERT_EQ (0 ,unlink (file2_s1d3)); + + bind_s1d3_fd =open (bind_dir_s1d3,O_PATH |O_CLOEXEC ); +ASSERT_LE (0 , bind_s1d3_fd); +EXPECT_EQ (0 ,test_open_rel (bind_s1d3_fd, file1_name,O_RDONLY )); + +/* Disconnects bind_s1d3_fd. */ + ASSERT_EQ(0, rename(dir_s1d3, dir_s4d1)) + { +TH_LOG ("Failed to rename %s to %s: %s" , dir_s1d3, dir_s4d1, +strerror (errno)); + } + +/* Need this later to test different parent link. */ + ASSERT_EQ(0, mkdir(dir_s4d2, 0755)) + { +TH_LOG ("Failed to create %s: %s" , dir_s4d2,strerror (errno)); + } + + ruleset_fd =create_ruleset (_metadata,ACCESS_ALL , layer1); +ASSERT_LE (0 , ruleset_fd); +enforce_ruleset (_metadata, ruleset_fd); +EXPECT_EQ (0 ,close (ruleset_fd)); + +/* From disconnected to connected. */ + ASSERT_EQ(0, linkat(bind_s1d3_fd, file1_name, AT_FDCWD, file1_s2d2, 0)) + { +TH_LOG ("Failed to link %s to %s via disconnected %s: %s" , + file1_name, file1_s2d2, bind_dir_s1d3,strerror (errno)); + } + +/* Tests that we can access via the new link... */ + EXPECT_EQ(0, test_open(file1_s2d2, O_RDONLY)) + { +TH_LOG ("Failed to open newly linked %s: %s" , file1_s2d2, +strerror (errno)); + } + +/* ...as well as the old one. */ + EXPECT_EQ(0, test_open(file1_s4d1, O_RDONLY)) + { +TH_LOG ("Failed to open original %s: %s" , file1_s4d1, +strerror (errno)); + } + +/* From connected to disconnected. */ +ASSERT_EQ (0 ,unlink (file1_s4d1)); + ASSERT_EQ(0, linkat(AT_FDCWD, file1_s2d2, bind_s1d3_fd, file2_name, 0)) + { +TH_LOG ("Failed to link %s to %s via disconnected %s: %s" , + file1_s2d2, file2_name, bind_dir_s1d3,strerror (errno)); + } +EXPECT_EQ (0 ,test_open (file2_s4d1,O_RDONLY )); +ASSERT_EQ (0 ,unlink (file1_s2d2)); + +/* From disconnected to disconnected (same parent). */ +ASSERT_EQ (0 , + linkat(bind_s1d3_fd, file2_name, bind_s1d3_fd, file1_name, 0)) + { +TH_LOG ("Failed to link %s to %s within disconnected %s: %s" , + file2_name, file1_name, bind_dir_s1d3,strerror (errno)); + } + EXPECT_EQ(0, test_open(file1_s4d1, O_RDONLY)) + { +TH_LOG ("Failed to open newly linked %s: %s" , file1_s4d1, +strerror (errno)); + } + EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, file1_name, O_RDONLY)) + { +TH_LOG ("Failed to open %s through newly created link under disconnected path: %s" , + file1_name,strerror (errno)); + } +ASSERT_EQ (0 ,unlink (file2_s4d1)); + +/* From disconnected to disconnected (different parent). */ +ASSERT_EQ (0 , + linkat(bind_s1d3_fd, file1_name, bind_s1d3_fd, "s4d2/f1", 0)) + { +TH_LOG ("Failed to link %s to %s within disconnected %s: %s" , + file1_name,"s4d2/f1" , bind_dir_s1d3,strerror (errno)); + } + EXPECT_EQ(0, test_open(file1_s4d2, O_RDONLY)) + { +TH_LOG ("Failed to open %s after link: %s" , file1_s4d2, +strerror (errno)); + } + EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, "s4d2/f1", O_RDONLY)) + { +TH_LOG ("Failed to open %s through disconnected path after link: %s" , +"s4d2/f1" ,strerror (errno)); + } +} +#define LOWER_BASE TMP_DIR "/lower"#define LOWER_DATA LOWER_BASE "/data"static const char lower_fl1[] =LOWER_DATA "/fl1" ;
PATCH 2/2 selftests/landlock: Add disconnected leafs and branch test suites
In reply to Greg Kroah-Hartman[ Upstream commit 54f9baf537b0a091adad860ec92e3e18e0a0754c ]
Test disconnected directories with two test suites (layout4_disconnected_leafs and layout5_disconnected_branch) and 43 variants to cover the main corner cases.
These tests are complementary to the previous commit.
Add test_renameat() and test_exchangeat() helpers.
Test coverage for security/landlock is 92.1% of 1927 lines according to LLVM 20.
[mic: Backport: skip empty rules, use the older fixture teardown, and explicitly detach nested mounts]
--- tools/testing/selftests/landlock/fs_test.c | 1064 ++++++++++++++++++++ 1 file changed, 1064 insertions(+)
tools/testing/selftests/landlock/fs_test.c +1064 -0
@@ -304,6 +304,16 @@ static void cleanup_layout(struct __test_metadata *const _metadata)EXPECT_EQ (0 ,remove_path (TMP_DIR )); } +static void +cleanup_layout_with_nested_mounts (struct __test_metadata *const _metadata) +{ +set_cap (_metadata,CAP_SYS_ADMIN ); +/* Also detaches disconnected bind mounts nested below TMP_DIR. */ +EXPECT_EQ (0 ,umount2 (TMP_DIR ,MNT_DETACH )); +clear_cap (_metadata,CAP_SYS_ADMIN ); +EXPECT_EQ (0 ,remove_path (TMP_DIR )); +} +/* clang-format off */ FIXTURE (layout0) {};/* clang-format on */
@@ -681,6 +691,9 @@ static int create_ruleset(struct __test_metadata *const _metadata, }for (i =0 ; rules[i].path; i++) { +if (!rules[i].access) +continue ; +add_path_beneath (_metadata, ruleset_fd, rules[i].access, rules[i].path); }
@@ -1975,6 +1988,22 @@ static int test_exchange(const char *const oldpath, const char *const newpath)return 0 ; } +static int test_renameat (int olddirfd,const char *oldpath,int newdirfd, +const char *newpath) +{ +if (renameat2 (olddirfd, oldpath, newdirfd, newpath,0 )) +return errno; +return 0 ; +} + +static int test_exchangeat (int olddirfd,const char *oldpath,int newdirfd, +const char *newpath) +{ +if (renameat2 (olddirfd, oldpath, newdirfd, newpath,RENAME_EXCHANGE )) +return errno; +return 0 ; +} + TEST_F_FORK(layout1, rename_file) {const struct rule rules[] = {
@@ -4437,6 +4466,1041 @@ TEST_F_FORK(layout1_bind, path_disconnected_link) } } +/* +* layout4_disconnected_leafs with bind mount and renames: +* +* tmp +* ├── s1d1 +* │ └── s1d2 [source of the bind mount] +* │ ├── s1d31 +* │ │ └── s1d41 [now renamed beneath s3d1] +* │ │ ├── f1 +* │ │ └── f2 +* │ └── s1d32 +* │ └── s1d42 [now renamed beneath s4d1] +* │ ├── f3 +* │ └── f4 +* ├── s2d1 +* │ └── s2d2 [bind mount of s1d2] +* │ ├── s1d31 +* │ │ └── s1d41 [opened FD, now renamed beneath s3d1] +* │ │ ├── f1 +* │ │ └── f2 +* │ └── s1d32 +* │ └── s1d42 [opened FD, now renamed beneath s4d1] +* │ ├── f3 +* │ └── f4 +* ├── s3d1 +* │ └── s1d41 [renamed here] +* │ ├── f1 +* │ └── f2 +* └── s4d1 +* └── s1d42 [renamed here] +* ├── f3 +* └── f4 +*/ +/* clang-format off */ +FIXTURE (layout4_disconnected_leafs) { +int s2d2_fd; +}; +/* clang-format on */ + +FIXTURE_SETUP (layout4_disconnected_leafs) +{ +prepare_layout (_metadata); + +create_file (_metadata,TMP_DIR "/s1d1/s1d2/s1d31/s1d41/f1" ); +create_file (_metadata,TMP_DIR "/s1d1/s1d2/s1d31/s1d41/f2" ); +create_file (_metadata,TMP_DIR "/s1d1/s1d2/s1d32/s1d42/f3" ); +create_file (_metadata,TMP_DIR "/s1d1/s1d2/s1d32/s1d42/f4" ); +create_directory (_metadata,TMP_DIR "/s2d1/s2d2" ); +create_directory (_metadata,TMP_DIR "/s3d1" ); +create_directory (_metadata,TMP_DIR "/s4d1" ); + + self->s2d2_fd = +open (TMP_DIR "/s2d1/s2d2" ,O_DIRECTORY |O_PATH |O_CLOEXEC ); +ASSERT_LE (0 , self->s2d2_fd); + +set_cap (_metadata,CAP_SYS_ADMIN ); +ASSERT_EQ (0 ,mount (TMP_DIR "/s1d1/s1d2" ,TMP_DIR "/s2d1/s2d2" ,NULL , +MS_BIND ,NULL )); +clear_cap (_metadata,CAP_SYS_ADMIN ); +} + +FIXTURE_TEARDOWN (layout4_disconnected_leafs) +{ +/* Teardown runs before this mount namespace exits. */ + +/* Removes files after renames. */ +remove_path (TMP_DIR "/s3d1/s1d41/f1" ); +remove_path (TMP_DIR "/s3d1/s1d41/f2" ); +remove_path (TMP_DIR "/s4d1/s1d42/f1" ); +remove_path (TMP_DIR "/s4d1/s1d42/f3" ); +remove_path (TMP_DIR "/s4d1/s1d42/f4" ); +remove_path (TMP_DIR "/s4d1/s1d42/f5" ); + +cleanup_layout_with_nested_mounts (_metadata); +} + +FIXTURE_VARIANT (layout4_disconnected_leafs) +{ +/* +* Parent of the bind mount source. It should always be ignored when +* testing against files under the s1d41 or s1d42 disconnected directories. +*/ +const __u64 allowed_s1d1; +/* +* Source of bind mount (to s2d2). It should always be enforced when +* testing against files under the s1d41 or s1d42 disconnected directories. +*/ +const __u64 allowed_s1d2; +/* +* Original parent of s1d41. It should always be ignored when testing +* against files under the s1d41 disconnected directory. +*/ +const __u64 allowed_s1d31; +/* +* Original parent of s1d42. It should always be ignored when testing +* against files under the s1d42 disconnected directory. +*/ +const __u64 allowed_s1d32; +/* +* Opened and disconnected source directory. It should always be enforced +* when testing against files under the s1d41 disconnected directory. +*/ +const __u64 allowed_s1d41; +/* +* Opened and disconnected source directory. It should always be enforced +* when testing against files under the s1d42 disconnected directory. +*/ +const __u64 allowed_s1d42; +/* +* File in the s1d41 disconnected directory. It should always be enforced +* when testing against itself under the s1d41 disconnected directory. +*/ +const __u64 allowed_f1; +/* +* File in the s1d41 disconnected directory. It should always be enforced +* when testing against itself under the s1d41 disconnected directory. +*/ +const __u64 allowed_f2; +/* +* File in the s1d42 disconnected directory. It should always be enforced +* when testing against itself under the s1d42 disconnected directory. +*/ +const __u64 allowed_f3; +/* +* Parent of the bind mount destination. It should always be enforced when +* testing against files under the s1d41 or s1d42 disconnected directories. +*/ +const __u64 allowed_s2d1; +/* +* Directory covered by the bind mount. It should always be ignored when +* testing against files under the s1d41 or s1d42 disconnected directories. +*/ +const __u64 allowed_s2d2; +/* +* New parent of the renamed s1d41. It should always be ignored when +* testing against files under the s1d41 disconnected directory. +*/ +const __u64 allowed_s3d1; +/* +* New parent of the renamed s1d42. It should always be ignored when +* testing against files under the s1d42 disconnected directory. +*/ +const __u64 allowed_s4d1; + +/* Expected result of the call to open([fd:s1d41]/f1, O_RDONLY). */ +const int expected_read_result; +/* Expected result of the call to renameat([fd:s1d41]/f1, [fd:s1d42]/f1). */ +const int expected_rename_result; +/* +* Expected result of the call to renameat([fd:s1d41]/f2, [fd:s1d42]/f3, +* RENAME_EXCHANGE). +*/ +const int expected_exchange_result; +/* Expected result of the call to renameat([fd:s1d42]/f4, [fd:s1d42]/f5). */ +const int expected_same_dir_rename_result; +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s1d1_mount_src_parent) { +/* clang-format on */ + .allowed_s1d1 =LANDLOCK_ACCESS_FS_REFER | +LANDLOCK_ACCESS_FS_READ_FILE | +LANDLOCK_ACCESS_FS_EXECUTE | + LANDLOCK_ACCESS_FS_MAKE_REG, + .expected_read_result = EACCES, + .expected_same_dir_rename_result = EACCES, + .expected_rename_result = EACCES, + .expected_exchange_result =EACCES , +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s1d2_mount_src_refer) { +/* clang-format on */ + .allowed_s1d2 =LANDLOCK_ACCESS_FS_REFER |LANDLOCK_ACCESS_FS_READ_FILE , + .expected_read_result = 0, + .expected_same_dir_rename_result = EACCES, + .expected_rename_result = EACCES, + .expected_exchange_result = EACCES, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s1d2_mount_src_create) { +/* clang-format on */ + .allowed_s1d2 =LANDLOCK_ACCESS_FS_READ_FILE | + LANDLOCK_ACCESS_FS_MAKE_REG, + .expected_read_result = 0, + .expected_same_dir_rename_result = 0, + .expected_rename_result = EXDEV, + .expected_exchange_result =EXDEV , +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s1d2_mount_src_rename) { +/* clang-format on */ + .allowed_s1d2 =LANDLOCK_ACCESS_FS_REFER |LANDLOCK_ACCESS_FS_MAKE_REG , + .expected_read_result = EACCES, + .expected_same_dir_rename_result = 0, + .expected_rename_result = 0, + .expected_exchange_result = 0, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s1d31_s1d32_old_parent) { +/* clang-format on */ + .allowed_s1d31 =LANDLOCK_ACCESS_FS_REFER | +LANDLOCK_ACCESS_FS_READ_FILE | +LANDLOCK_ACCESS_FS_EXECUTE | + LANDLOCK_ACCESS_FS_MAKE_REG, + .allowed_s1d32 =LANDLOCK_ACCESS_FS_REFER | +LANDLOCK_ACCESS_FS_READ_FILE | +LANDLOCK_ACCESS_FS_EXECUTE | + LANDLOCK_ACCESS_FS_MAKE_REG, + .expected_read_result = EACCES, + .expected_same_dir_rename_result = EACCES, + .expected_rename_result = EACCES, + .expected_exchange_result =EACCES , +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s1d41_s1d42_disconnected_refer) { +/* clang-format on */ + .allowed_s1d41 =LANDLOCK_ACCESS_FS_REFER | +LANDLOCK_ACCESS_FS_READ_FILE , + .allowed_s1d42 = LANDLOCK_ACCESS_FS_REFER | +LANDLOCK_ACCESS_FS_READ_FILE , + .expected_read_result = 0, + .expected_same_dir_rename_result = EACCES, + .expected_rename_result = EACCES, + .expected_exchange_result = EACCES, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s1d41_s1d42_disconnected_create) { +/* clang-format on */ + .allowed_s1d41 =LANDLOCK_ACCESS_FS_READ_FILE | + LANDLOCK_ACCESS_FS_MAKE_REG, + .allowed_s1d42 =LANDLOCK_ACCESS_FS_READ_FILE | + LANDLOCK_ACCESS_FS_MAKE_REG, + .expected_read_result = 0, + .expected_same_dir_rename_result = 0, + .expected_rename_result = EXDEV, + .expected_exchange_result =EXDEV , +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s1d41_s1d42_disconnected_rename_even) { +/* clang-format on */ + .allowed_s1d41 =LANDLOCK_ACCESS_FS_REFER |LANDLOCK_ACCESS_FS_MAKE_REG , + .allowed_s1d42 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, + .expected_read_result = EACCES, + .expected_same_dir_rename_result = 0, + .expected_rename_result = 0, + .expected_exchange_result = 0, +}; + +/* The destination directory has more access right. */ +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s1d41_s1d42_disconnected_rename_more) { +/* clang-format on */ + .allowed_s1d41 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, + .allowed_s1d42 =LANDLOCK_ACCESS_FS_REFER | +LANDLOCK_ACCESS_FS_MAKE_REG | + LANDLOCK_ACCESS_FS_EXECUTE, + .expected_read_result = EACCES, + .expected_same_dir_rename_result = 0, +/* Access denied. */ + .expected_rename_result = EXDEV, + .expected_exchange_result = EXDEV, +}; + +/* The destination directory has less access right. */ +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s1d41_s1d42_disconnected_rename_less) { +/* clang-format on */ + .allowed_s1d41 =LANDLOCK_ACCESS_FS_REFER | +LANDLOCK_ACCESS_FS_MAKE_REG | + LANDLOCK_ACCESS_FS_EXECUTE, + .allowed_s1d42 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, + .expected_read_result = EACCES, + .expected_same_dir_rename_result = 0, +/* Access allowed. */ + .expected_rename_result = 0, + .expected_exchange_result =EXDEV , +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s2d1_mount_dst_parent_create) { +/* clang-format on */ + .allowed_s2d1 =LANDLOCK_ACCESS_FS_READ_FILE | +LANDLOCK_ACCESS_FS_MAKE_REG , + .expected_read_result = 0, + .expected_same_dir_rename_result = 0, + .expected_rename_result = EXDEV, + .expected_exchange_result = EXDEV, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s2d1_mount_dst_parent_refer) { +/* clang-format on */ + .allowed_s2d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE, + .expected_read_result = 0, + .expected_same_dir_rename_result = EACCES, + .expected_rename_result = EACCES, + .expected_exchange_result =EACCES , +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s2d1_mount_dst_parent_mini) { +/* clang-format on */ + .allowed_s2d1 =LANDLOCK_ACCESS_FS_REFER | +LANDLOCK_ACCESS_FS_READ_FILE | +LANDLOCK_ACCESS_FS_MAKE_REG , + .expected_read_result = 0, + .expected_same_dir_rename_result = 0, + .expected_rename_result = 0, + .expected_exchange_result = 0, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s2d2_covered_by_mount) { +/* clang-format on */ + .allowed_s2d2 =LANDLOCK_ACCESS_FS_REFER | +LANDLOCK_ACCESS_FS_READ_FILE | +LANDLOCK_ACCESS_FS_EXECUTE | + LANDLOCK_ACCESS_FS_MAKE_REG, + .expected_read_result = EACCES, + .expected_same_dir_rename_result = EACCES, + .expected_rename_result = EACCES, + .expected_exchange_result = EACCES, +}; + +/* Tests collect_domain_accesses(). */ +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s3d1_s4d1_new_parent_refer) { +/* clang-format on */ + .allowed_s3d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE, + .allowed_s4d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE, + .expected_read_result = 0, + .expected_same_dir_rename_result = EACCES, + .expected_rename_result = EACCES, + .expected_exchange_result =EACCES , +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s3d1_s4d1_new_parent_create) { +/* clang-format on */ + .allowed_s3d1 =LANDLOCK_ACCESS_FS_READ_FILE | +LANDLOCK_ACCESS_FS_MAKE_REG , + .allowed_s4d1 = LANDLOCK_ACCESS_FS_READ_FILE | +LANDLOCK_ACCESS_FS_MAKE_REG , + .expected_read_result = 0, + .expected_same_dir_rename_result = 0, + .expected_rename_result = EXDEV, + .expected_exchange_result = EXDEV, +}; + +FIXTURE_VARIANT_ADD (layout4_disconnected_leafs, + s3d1_s4d1_disconnected_rename_even){ +/* clang-format on */ + .allowed_s3d1 =LANDLOCK_ACCESS_FS_REFER |LANDLOCK_ACCESS_FS_MAKE_REG , + .allowed_s4d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, + .expected_read_result = EACCES, + .expected_same_dir_rename_result = 0, + .expected_rename_result = 0, + .expected_exchange_result = 0, +}; + +/* The destination directory has more access right. */ +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s3d1_s4d1_disconnected_rename_more) { +/* clang-format on */ + .allowed_s3d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, + .allowed_s4d1 =LANDLOCK_ACCESS_FS_REFER |LANDLOCK_ACCESS_FS_MAKE_REG | + LANDLOCK_ACCESS_FS_EXECUTE, + .expected_read_result = EACCES, + .expected_same_dir_rename_result = 0, +/* Access denied. */ + .expected_rename_result = EXDEV, + .expected_exchange_result = EXDEV, +}; + +/* The destination directory has less access right. */ +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s3d1_s4d1_disconnected_rename_less) { +/* clang-format on */ + .allowed_s3d1 =LANDLOCK_ACCESS_FS_REFER |LANDLOCK_ACCESS_FS_MAKE_REG | + LANDLOCK_ACCESS_FS_EXECUTE, + .allowed_s4d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, + .expected_read_result = EACCES, + .expected_same_dir_rename_result = 0, +/* Access allowed. */ + .expected_rename_result = 0, + .expected_exchange_result =EXDEV , +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, f1_f2_f3) { +/* clang-format on */ + .allowed_f1 =LANDLOCK_ACCESS_FS_READ_FILE , + .allowed_f2 = LANDLOCK_ACCESS_FS_READ_FILE, + .allowed_f3 = LANDLOCK_ACCESS_FS_READ_FILE, + .expected_read_result = 0, + .expected_same_dir_rename_result = EACCES, + .expected_rename_result = EACCES, + .expected_exchange_result = EACCES, +}; + +TEST_F_FORK(layout4_disconnected_leafs, read_rename_exchange) +{ + const __u64 handled_access = +LANDLOCK_ACCESS_FS_REFER |LANDLOCK_ACCESS_FS_READ_FILE | +LANDLOCK_ACCESS_FS_EXECUTE |LANDLOCK_ACCESS_FS_MAKE_REG ; +const struct rule rules[] = { + { + .path =TMP_DIR "/s1d1" , + .access = variant->allowed_s1d1, + }, + { + .path =TMP_DIR "/s1d1/s1d2" , + .access = variant->allowed_s1d2, + }, + { + .path =TMP_DIR "/s1d1/s1d2/s1d31" , + .access = variant->allowed_s1d31, + }, + { + .path =TMP_DIR "/s1d1/s1d2/s1d32" , + .access = variant->allowed_s1d32, + }, + { + .path =TMP_DIR "/s1d1/s1d2/s1d31/s1d41" , + .access = variant->allowed_s1d41, + }, + { + .path =TMP_DIR "/s1d1/s1d2/s1d32/s1d42" , + .access = variant->allowed_s1d42, + }, + { + .path =TMP_DIR "/s1d1/s1d2/s1d31/s1d41/f1" , + .access = variant->allowed_f1, + }, + { + .path =TMP_DIR "/s1d1/s1d2/s1d31/s1d41/f2" , + .access = variant->allowed_f2, + }, + { + .path =TMP_DIR "/s1d1/s1d2/s1d32/s1d42/f3" , + .access = variant->allowed_f3, + }, + { + .path =TMP_DIR "/s2d1" , + .access = variant->allowed_s2d1, + }, +/* s2d2_fd */ + { + .path =TMP_DIR "/s3d1" , + .access = variant->allowed_s3d1, + }, + { + .path =TMP_DIR "/s4d1" , + .access = variant->allowed_s4d1, + }, + {}, + }; +int ruleset_fd, s1d41_bind_fd, s1d42_bind_fd; + + ruleset_fd =create_ruleset (_metadata, handled_access, rules); +ASSERT_LE (0 , ruleset_fd); + +/* Adds rule for the covered directory. */ +if (variant->allowed_s2d2) { +ASSERT_EQ (0 ,landlock_add_rule ( + ruleset_fd,LANDLOCK_RULE_PATH_BENEATH , + &(struct landlock_path_beneath_attr ){ + .parent_fd = self->s2d2_fd, + .allowed_access = + variant->allowed_s2d2, + }, +0 )); + } +EXPECT_EQ (0 ,close (self->s2d2_fd)); + + s1d41_bind_fd =open (TMP_DIR "/s2d1/s2d2/s1d31/s1d41" , +O_DIRECTORY |O_PATH |O_CLOEXEC ); +ASSERT_LE (0 , s1d41_bind_fd); + s1d42_bind_fd =open (TMP_DIR "/s2d1/s2d2/s1d32/s1d42" , +O_DIRECTORY |O_PATH |O_CLOEXEC ); +ASSERT_LE (0 , s1d42_bind_fd); + +/* Disconnects and checks source and destination directories. */ +EXPECT_EQ (0 ,test_open_rel (s1d41_bind_fd,".." ,O_DIRECTORY )); +EXPECT_EQ (0 ,test_open_rel (s1d42_bind_fd,".." ,O_DIRECTORY )); +/* Renames to make it accessible through s3d1/s1d41 */ +ASSERT_EQ (0 ,test_renameat (AT_FDCWD ,TMP_DIR "/s1d1/s1d2/s1d31/s1d41" , +AT_FDCWD ,TMP_DIR "/s3d1/s1d41" )); +/* Renames to make it accessible through s4d1/s1d42 */ +ASSERT_EQ (0 ,test_renameat (AT_FDCWD ,TMP_DIR "/s1d1/s1d2/s1d32/s1d42" , +AT_FDCWD ,TMP_DIR "/s4d1/s1d42" )); +EXPECT_EQ (ENOENT ,test_open_rel (s1d41_bind_fd,".." ,O_DIRECTORY )); +EXPECT_EQ (ENOENT ,test_open_rel (s1d42_bind_fd,".." ,O_DIRECTORY )); + +enforce_ruleset (_metadata, ruleset_fd); +EXPECT_EQ (0 ,close (ruleset_fd)); + +EXPECT_EQ (variant->expected_read_result, +test_open_rel (s1d41_bind_fd,"f1" ,O_RDONLY )); + +EXPECT_EQ (variant->expected_rename_result, +test_renameat (s1d41_bind_fd,"f1" , s1d42_bind_fd,"f1" )); +EXPECT_EQ (variant->expected_exchange_result, +test_exchangeat (s1d41_bind_fd,"f2" , s1d42_bind_fd,"f3" )); + +EXPECT_EQ (variant->expected_same_dir_rename_result, +test_renameat (s1d42_bind_fd,"f4" , s1d42_bind_fd,"f5" )); +} + +/* +* layout5_disconnected_branch before rename: +* +* tmp +* ├── s1d1 +* │ └── s1d2 [source of the first bind mount] +* │ └── s1d3 +* │ ├── s1d41 +* │ │ ├── f1 +* │ │ └── f2 +* │ └── s1d42 +* │ ├── f3 +* │ └── f4 +* ├── s2d1 +* │ └── s2d2 [source of the second bind mount] +* │ └── s2d3 +* │ └── s2d4 [first s1d2 bind mount] +* │ └── s1d3 +* │ ├── s1d41 +* │ │ ├── f1 +* │ │ └── f2 +* │ └── s1d42 +* │ ├── f3 +* │ └── f4 +* ├── s3d1 +* │ └── s3d2 [second s2d2 bind mount] +* │ └── s2d3 +* │ └── s2d4 [first s1d2 bind mount] +* │ └── s1d3 +* │ ├── s1d41 +* │ │ ├── f1 +* │ │ └── f2 +* │ └── s1d42 +* │ ├── f3 +* │ └── f4 +* └── s4d1 +* +* After rename: +* +* tmp +* ├── s1d1 +* │ └── s1d2 [source of the first bind mount] +* │ └── s1d3 +* │ ├── s1d41 +* │ │ ├── f1 +* │ │ └── f2 +* │ └── s1d42 +* │ ├── f3 +* │ └── f4 +* ├── s2d1 +* │ └── s2d2 [source of the second bind mount] +* ├── s3d1 +* │ └── s3d2 [second s2d2 bind mount] +* └── s4d1 +* └── s2d3 [renamed here] +* └── s2d4 [first s1d2 bind mount] +* └── s1d3 +* ├── s1d41 +* │ ├── f1 +* │ └── f2 +* └── s1d42 +* ├── f3 +* └── f4 +* +* Decision path for access from the s3d1/s3d2/s2d3/s2d4/s1d3 file descriptor: +* 1. first bind mount: s1d3 -> s1d2 +* 2. second bind mount: s2d3 +* 3. tmp mount: s4d1 -> tmp [disconnected branch] +* 4. second bind mount: s2d2 +* 5. tmp mount: s3d1 -> tmp +* 6. parent mounts: [...] -> / +* +* The s4d1 directory is evaluated even if it is not in the s2d2 mount. +*/ + +/* clang-format off */ +FIXTURE (layout5_disconnected_branch) { +int s2d4_fd, s3d2_fd; +}; +/* clang-format on */ + +FIXTURE_SETUP (layout5_disconnected_branch) +{ +prepare_layout (_metadata); + +create_file (_metadata,TMP_DIR "/s1d1/s1d2/s1d3/s1d41/f1" ); +create_file (_metadata,TMP_DIR "/s1d1/s1d2/s1d3/s1d41/f2" ); +create_file (_metadata,TMP_DIR "/s1d1/s1d2/s1d3/s1d42/f3" ); +create_file (_metadata,TMP_DIR "/s1d1/s1d2/s1d3/s1d42/f4" ); +create_directory (_metadata,TMP_DIR "/s2d1/s2d2/s2d3/s2d4" ); +create_directory (_metadata,TMP_DIR "/s3d1/s3d2" ); +create_directory (_metadata,TMP_DIR "/s4d1" ); + + self->s2d4_fd =open (TMP_DIR "/s2d1/s2d2/s2d3/s2d4" , +O_DIRECTORY |O_PATH |O_CLOEXEC ); +ASSERT_LE (0 , self->s2d4_fd); + + self->s3d2_fd = +open (TMP_DIR "/s3d1/s3d2" ,O_DIRECTORY |O_PATH |O_CLOEXEC ); +ASSERT_LE (0 , self->s3d2_fd); + +set_cap (_metadata,CAP_SYS_ADMIN ); +ASSERT_EQ (0 ,mount (TMP_DIR "/s1d1/s1d2" ,TMP_DIR "/s2d1/s2d2/s2d3/s2d4" , +NULL ,MS_BIND ,NULL )); +ASSERT_EQ (0 ,mount (TMP_DIR "/s2d1/s2d2" ,TMP_DIR "/s3d1/s3d2" ,NULL , +MS_BIND |MS_REC ,NULL )); +clear_cap (_metadata,CAP_SYS_ADMIN ); +} + +FIXTURE_TEARDOWN (layout5_disconnected_branch) +{ +/* Teardown runs before this mount namespace exits. */ + +/* Removes files after renames. */ +remove_path (TMP_DIR "/s1d1/s1d2/s1d3/s1d41/f1" ); +remove_path (TMP_DIR "/s1d1/s1d2/s1d3/s1d41/f2" ); +remove_path (TMP_DIR "/s1d1/s1d2/s1d3/s1d42/f1" ); +remove_path (TMP_DIR "/s1d1/s1d2/s1d3/s1d42/f3" ); +remove_path (TMP_DIR "/s1d1/s1d2/s1d3/s1d42/f4" ); +remove_path (TMP_DIR "/s1d1/s1d2/s1d3/s1d42/f5" ); + +cleanup_layout_with_nested_mounts (_metadata); +} + +FIXTURE_VARIANT (layout5_disconnected_branch) +{ +/* +* Parent of all files. It should always be enforced when testing against +* files under the s1d41 or s1d42 disconnected directories. +*/ +const __u64 allowed_base; +/* +* Parent of the first bind mount source. It should always be ignored when +* testing against files under the s1d41 or s1d42 disconnected directories. +*/ +const __u64 allowed_s1d1; +const __u64 allowed_s1d2; +const __u64 allowed_s1d3; +const __u64 allowed_s2d1; +const __u64 allowed_s2d2; +const __u64 allowed_s2d3; +const __u64 allowed_s2d4; +const __u64 allowed_s3d1; +const __u64 allowed_s3d2; +const __u64 allowed_s4d1; + +/* Expected result of the call to open([fd:s1d3]/s1d41/f1, O_RDONLY). */ +const int expected_read_result; +/* +* Expected result of the call to renameat([fd:s1d3]/s1d41/f1, +* [fd:s1d3]/s1d42/f1). +*/ +const int expected_rename_result; +/* +* Expected result of the call to renameat([fd:s1d3]/s1d41/f2, +* [fd:s1d3]/s1d42/f3, RENAME_EXCHANGE). +*/ +const int expected_exchange_result; +/* +* Expected result of the call to renameat([fd:s1d3]/s1d42/f4, +* [fd:s1d3]/s1d42/f5). +*/ +const int expected_same_dir_rename_result; +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s1d1_mount1_src_parent) { +/* clang-format on */ + .allowed_s1d1 =LANDLOCK_ACCESS_FS_REFER | +LANDLOCK_ACCESS_FS_READ_FILE | +LANDLOCK_ACCESS_FS_EXECUTE | + LANDLOCK_ACCESS_FS_MAKE_REG, + .expected_read_result = EACCES, + .expected_same_dir_rename_result = EACCES, + .expected_rename_result = EACCES, + .expected_exchange_result = EACCES, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s1d2_mount1_src_refer) { +/* clang-format on */ + .allowed_s1d2 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE, + .expected_read_result = 0, + .expected_same_dir_rename_result = EACCES, + .expected_rename_result = EACCES, + .expected_exchange_result = EACCES, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s1d2_mount1_src_create) { +/* clang-format on */ + .allowed_s1d2 =LANDLOCK_ACCESS_FS_READ_FILE | + LANDLOCK_ACCESS_FS_MAKE_REG, + .expected_read_result = 0, + .expected_same_dir_rename_result = 0, + .expected_rename_result = EXDEV, + .expected_exchange_result = EXDEV, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s1d2_mount1_src_rename) { +/* clang-format on */ + .allowed_s1d2 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, + .expected_read_result = EACCES, + .expected_same_dir_rename_result = 0, + .expected_rename_result = 0, + .expected_exchange_result = 0, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s1d3_fd_refer) { +/* clang-format on */ + .allowed_s1d3 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE, + .expected_read_result = 0, + .expected_same_dir_rename_result = EACCES, + .expected_rename_result = EACCES, + .expected_exchange_result = EACCES, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s1d3_fd_create) { +/* clang-format on */ + .allowed_s1d3 =LANDLOCK_ACCESS_FS_READ_FILE | + LANDLOCK_ACCESS_FS_MAKE_REG, + .expected_read_result = 0, + .expected_same_dir_rename_result = 0, + .expected_rename_result = EXDEV, + .expected_exchange_result = EXDEV, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s1d3_fd_rename) { +/* clang-format on */ + .allowed_s1d3 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, + .expected_read_result = EACCES, + .expected_same_dir_rename_result = 0, + .expected_rename_result = 0, + .expected_exchange_result = 0, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s1d3_fd_full) { +/* clang-format on */ + .allowed_s1d3 =LANDLOCK_ACCESS_FS_REFER | +LANDLOCK_ACCESS_FS_READ_FILE | +LANDLOCK_ACCESS_FS_EXECUTE | + LANDLOCK_ACCESS_FS_MAKE_REG, + .expected_read_result = 0, + .expected_same_dir_rename_result = 0, + .expected_rename_result = 0, + .expected_exchange_result = 0, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s2d1_mount2_src_parent) { +/* clang-format on */ + .allowed_s2d1 =LANDLOCK_ACCESS_FS_REFER | +LANDLOCK_ACCESS_FS_READ_FILE | +LANDLOCK_ACCESS_FS_EXECUTE | + LANDLOCK_ACCESS_FS_MAKE_REG, + .expected_read_result = EACCES, + .expected_same_dir_rename_result = EACCES, + .expected_rename_result = EACCES, + .expected_exchange_result = EACCES, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s2d2_mount2_src_refer) { +/* clang-format on */ + .allowed_s2d2 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE, + .expected_read_result = 0, + .expected_same_dir_rename_result = EACCES, + .expected_rename_result = EACCES, + .expected_exchange_result = EACCES, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s2d2_mount2_src_create) { +/* clang-format on */ + .allowed_s2d2 =LANDLOCK_ACCESS_FS_READ_FILE | + LANDLOCK_ACCESS_FS_MAKE_REG, + .expected_read_result = 0, + .expected_same_dir_rename_result = 0, + .expected_rename_result = EXDEV, + .expected_exchange_result = EXDEV, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s2d2_mount2_src_rename) { +/* clang-format on */ + .allowed_s2d2 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, + .expected_read_result = EACCES, + .expected_same_dir_rename_result = 0, + .expected_rename_result = 0, + .expected_exchange_result = 0, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s2d3_mount1_dst_parent_refer) { +/* clang-format on */ + .allowed_s2d3 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE, + .expected_read_result = 0, + .expected_same_dir_rename_result = EACCES, + .expected_rename_result = EACCES, + .expected_exchange_result = EACCES, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s2d3_mount1_dst_parent_create) { +/* clang-format on */ + .allowed_s2d3 =LANDLOCK_ACCESS_FS_READ_FILE | + LANDLOCK_ACCESS_FS_MAKE_REG, + .expected_read_result = 0, + .expected_same_dir_rename_result = 0, + .expected_rename_result = EXDEV, + .expected_exchange_result = EXDEV, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s2d3_mount1_dst_parent_rename) { +/* clang-format on */ + .allowed_s2d3 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, + .expected_read_result = EACCES, + .expected_same_dir_rename_result = 0, + .expected_rename_result = 0, + .expected_exchange_result = 0, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s2d4_mount1_dst) { +/* clang-format on */ + .allowed_s2d4 =LANDLOCK_ACCESS_FS_REFER | +LANDLOCK_ACCESS_FS_READ_FILE | +LANDLOCK_ACCESS_FS_EXECUTE | + LANDLOCK_ACCESS_FS_MAKE_REG, + .expected_read_result = EACCES, + .expected_same_dir_rename_result = EACCES, + .expected_rename_result = EACCES, + .expected_exchange_result = EACCES, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s3d1_mount2_dst_parent_refer) { +/* clang-format on */ + .allowed_s3d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE, + .expected_read_result = 0, + .expected_same_dir_rename_result = EACCES, + .expected_rename_result = EACCES, + .expected_exchange_result = EACCES, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s3d1_mount2_dst_parent_create) { +/* clang-format on */ + .allowed_s3d1 =LANDLOCK_ACCESS_FS_READ_FILE | + LANDLOCK_ACCESS_FS_MAKE_REG, + .expected_read_result = 0, + .expected_same_dir_rename_result = 0, + .expected_rename_result = EXDEV, + .expected_exchange_result = EXDEV, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s3d1_mount2_dst_parent_rename) { +/* clang-format on */ + .allowed_s3d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, + .expected_read_result = EACCES, + .expected_same_dir_rename_result = 0, + .expected_rename_result = 0, + .expected_exchange_result = 0, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s3d2_mount1_dst) { +/* clang-format on */ + .allowed_s3d2 =LANDLOCK_ACCESS_FS_REFER | +LANDLOCK_ACCESS_FS_READ_FILE | +LANDLOCK_ACCESS_FS_EXECUTE | + LANDLOCK_ACCESS_FS_MAKE_REG, + .expected_read_result = EACCES, + .expected_same_dir_rename_result = EACCES, + .expected_rename_result = EACCES, + .expected_exchange_result = EACCES, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s4d1_rename_parent_refer) { +/* clang-format on */ + .allowed_s4d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE, + .expected_read_result = 0, + .expected_same_dir_rename_result = EACCES, + .expected_rename_result = EACCES, + .expected_exchange_result = EACCES, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s4d1_rename_parent_create) { +/* clang-format on */ + .allowed_s4d1 =LANDLOCK_ACCESS_FS_READ_FILE | + LANDLOCK_ACCESS_FS_MAKE_REG, + .expected_read_result = 0, + .expected_same_dir_rename_result = 0, + .expected_rename_result = EXDEV, + .expected_exchange_result = EXDEV, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s4d1_rename_parent_rename) { +/* clang-format on */ + .allowed_s4d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, + .expected_read_result = EACCES, + .expected_same_dir_rename_result = 0, + .expected_rename_result = 0, + .expected_exchange_result = 0, +}; + +TEST_F_FORK(layout5_disconnected_branch, read_rename_exchange) +{ +const __u64 handled_access = +LANDLOCK_ACCESS_FS_REFER |LANDLOCK_ACCESS_FS_READ_FILE | +LANDLOCK_ACCESS_FS_EXECUTE |LANDLOCK_ACCESS_FS_MAKE_REG ; +const struct rule rules[] = { + { + .path =TMP_DIR "/s1d1" , + .access = variant->allowed_s1d1, + }, + { + .path =TMP_DIR "/s1d1/s1d2" , + .access = variant->allowed_s1d2, + }, + { + .path =TMP_DIR "/s1d1/s1d2/s1d3" , + .access = variant->allowed_s1d3, + }, + { + .path =TMP_DIR "/s2d1" , + .access = variant->allowed_s2d1, + }, + { + .path =TMP_DIR "/s2d1/s2d2" , + .access = variant->allowed_s2d2, + }, + { + .path =TMP_DIR "/s2d1/s2d2/s2d3" , + .access = variant->allowed_s2d3, + }, +/* s2d4_fd */ + { + .path =TMP_DIR "/s3d1" , + .access = variant->allowed_s3d1, + }, +/* s3d2_fd */ + { + .path =TMP_DIR "/s4d1" , + .access = variant->allowed_s4d1, + }, + {}, + }; +int ruleset_fd, s1d3_bind_fd; + + ruleset_fd =create_ruleset (_metadata, handled_access, rules); +ASSERT_LE (0 , ruleset_fd); + +/* Adds rules for the covered directories. */ +if (variant->allowed_s2d4) { +ASSERT_EQ (0 ,landlock_add_rule ( + ruleset_fd,LANDLOCK_RULE_PATH_BENEATH , + &(struct landlock_path_beneath_attr ){ + .parent_fd = self->s2d4_fd, + .allowed_access = + variant->allowed_s2d4, + }, +0 )); + } +EXPECT_EQ (0 ,close (self->s2d4_fd)); + +if (variant->allowed_s3d2) { +ASSERT_EQ (0 ,landlock_add_rule ( + ruleset_fd,LANDLOCK_RULE_PATH_BENEATH , + &(struct landlock_path_beneath_attr ){ + .parent_fd = self->s3d2_fd, + .allowed_access = + variant->allowed_s3d2, + }, +0 )); + } +EXPECT_EQ (0 ,close (self->s3d2_fd)); + + s1d3_bind_fd =open (TMP_DIR "/s3d1/s3d2/s2d3/s2d4/s1d3" , +O_DIRECTORY |O_PATH |O_CLOEXEC ); +ASSERT_LE (0 , s1d3_bind_fd); + +/* Disconnects and checks source and destination directories. */ +EXPECT_EQ (0 ,test_open_rel (s1d3_bind_fd,".." ,O_DIRECTORY )); +EXPECT_EQ (0 ,test_open_rel (s1d3_bind_fd,"../.." ,O_DIRECTORY )); +/* Renames to make it accessible through s3d1/s1d41 */ +ASSERT_EQ (0 ,test_renameat (AT_FDCWD ,TMP_DIR "/s2d1/s2d2/s2d3" , +AT_FDCWD ,TMP_DIR "/s4d1/s2d3" )); +EXPECT_EQ (0 ,test_open_rel (s1d3_bind_fd,".." ,O_DIRECTORY )); +EXPECT_EQ (ENOENT ,test_open_rel (s1d3_bind_fd,"../.." ,O_DIRECTORY )); + +enforce_ruleset (_metadata, ruleset_fd); +EXPECT_EQ (0 ,close (ruleset_fd)); + +EXPECT_EQ (variant->expected_read_result, +test_open_rel (s1d3_bind_fd,"s1d41/f1" ,O_RDONLY )); + +EXPECT_EQ (variant->expected_rename_result, +test_renameat (s1d3_bind_fd,"s1d41/f1" , s1d3_bind_fd, +"s1d42/f1" )); +EXPECT_EQ (variant->expected_exchange_result, +test_exchangeat (s1d3_bind_fd,"s1d41/f2" , s1d3_bind_fd, +"s1d42/f3" )); + +EXPECT_EQ (variant->expected_same_dir_rename_result, +test_renameat (s1d3_bind_fd,"s1d42/f4" , s1d3_bind_fd, +"s1d42/f5" )); +} +#define LOWER_BASE TMP_DIR "/lower"#define LOWER_DATA LOWER_BASE "/data"static const char lower_fl1[] =LOWER_DATA "/fl1" ;
Greg Kroah-Hartman
maintainerPATCH 195/452 USB: serial: digi_acceleport: fix memory corruption with small endpoints
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Johan Hovold <[email protected]>
commit cb3560e8eab1dfa1cac1ed52631adf8ec6ff2cd5 upstream.
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.
--- drivers/usb/serial/digi_acceleport.c | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-)
drivers/usb/serial/digi_acceleport.c +21 -2
@@ -1231,15 +1231,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 (sizeof (*serial_priv),GFP_KERNEL );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);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 196/452 xhci: tegra: Fix ghost USB device on dual-role port unplug
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Wei-Cheng Chen <[email protected]>
[ Upstream commit 5a4c828b8b29b47534814ade26d9aee09d5101fc ]
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)
[ Backport to 6.6.y: keep the host-mode snapshot in the existing
tegra->lock section, retain pm_runtime_mark_last_busy() in the host port-power path, and resolve context around the SoC ops/Tegra234 entries. ]
--- drivers/usb/host/xhci-tegra.c | 79 ++++++++++++++++++++--------------- 1 file changed, 45 insertions(+), 34 deletions(-)
drivers/usb/host/xhci-tegra.c +45 -34
@@ -243,6 +243,7 @@ struct tegra_xusb_soc {bool has_ipfs;bool lpm_support;bool otg_reset_sspi; +bool otg_set_port_power;bool has_bar2; };
@@ -1346,14 +1347,17 @@ 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;u32 status;int ret; -dev_dbg (tegra->dev,"host mode %s\n" , tegra->host_mode ?"on" :"off" ); -mutex_lock (&tegra->lock); -if (tegra->host_mode) + host_mode = tegra->host_mode; + +dev_dbg (tegra->dev,"host mode %s\n" , host_mode ?"on" :"off" ); + +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 );
@@ -1364,42 +1368,44 @@ 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); - -/* 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); +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 ; + + 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); -pm_runtime_mark_last_busy (tegra->dev); +tegra_xhci_set_port_power (tegra, true, true); +pm_runtime_mark_last_busy (tegra->dev); - }else { -if (tegra->otg_usb3_port >=0 ) -tegra_xhci_set_port_power (tegra, false, false); + }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, false); + } }pm_runtime_put_autosuspend (tegra->dev); }
@@ -2497,6 +2503,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,
@@ -2535,6 +2542,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,
@@ -2578,6 +2586,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,
@@ -2611,6 +2620,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,
@@ -2643,6 +2653,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,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 197/452 serial: dz: Fix bootconsole handover lockup
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Maciej W. Rozycki <[email protected]>
commit 7f127b2208e5e2b817243cad41fe4211a6d5a7a3 upstream.
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.
--- drivers/tty/serial/dz.c | 36 ++++++++++++------------------------ 1 file changed, 12 insertions(+), 24 deletions(-)
drivers/tty/serial/dz.c +12 -24
@@ -572,6 +572,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 );
@@ -655,26 +667,6 @@ static void dz_set_termios(struct uart_port *uport, struct ktermios *termios,spin_unlock_irqrestore (&dport->port.lock, 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; - -spin_lock_irqsave (&dport->port.lock, flags); -if (state <3 ) -dz_start_tx (&dport->port); -else -dz_stop_tx (&dport->port); -spin_unlock_irqrestore (&dport->port.lock, flags); -} - -static const char *dz_type (struct uart_port *uport) {return "DZ" ;
@@ -770,7 +762,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,
@@ -895,10 +886,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);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 198/452 serial: dz: Convert to use a platform device
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Maciej W. Rozycki <[email protected]>
commit 5d7a49d60b8fda66da60e240fd7315232fa1754f upstream.
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.
--- arch/mips/dec/platform.c | 55 ++++++++++++++++++- drivers/tty/serial/dz.c | 116 +++++++++++++++++++-------------------- 2 files changed, 110 insertions(+), 61 deletions(-)
arch/mips/dec/platform.c +54 -1
@@ -10,6 +10,13 @@#include <linux/mc146818rtc.h> #include <linux/platform_device.h> +#include <asm/bootinfo.h> + +#include <asm/dec/interrupts.h> +#include <asm/dec/kn01.h> +#include <asm/dec/kn02.h> +#include <asm/dec/system.h> +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);
drivers/tty/serial/dz.c +56 -60
223 lines not on this page · raw message
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 199/452 serial: zs: Convert to use a platform device
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Maciej W. Rozycki <[email protected]>
commit 7cac59d08a73cb866ec51a483a6f3fe0f531947c upstream.
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.
--- arch/mips/dec/platform.c | 60 +++++++++++- drivers/tty/serial/zs.c | 192 +++++++++++++++------------------------ drivers/tty/serial/zs.h | 1 - 3 files changed, 129 insertions(+), 124 deletions(-)
arch/mips/dec/platform.c +57 -3
@@ -13,6 +13,7 @@#include <asm/bootinfo.h> #include <asm/dec/interrupts.h> +#include <asm/dec/ioasic_addrs.h> #include <asm/dec/kn01.h> #include <asm/dec/kn02.h> #include <asm/dec/system.h>
@@ -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);
drivers/tty/serial/zs.c +72 -120
276 lines not on this page · raw message
drivers/tty/serial/zs.h +0 -1
@@ -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. */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 200/452 HID: core: Add printk_ratelimited variants to hid_warn() etc
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Vicki Pfau <[email protected]>
commit 1d64624243af8329b4b219d8c39e28ea448f9929 upstream.
hid_warn_ratelimited() is needed. Add the others as part of the block.
--- include/linux/hid.h | 11 +++++++++++ 1 file changed, 11 insertions(+)
include/linux/hid.h +11 -0
@@ -1252,4 +1252,15 @@ int hid_pidff_init_with_quirks(struct hi#define hid_dbg_once (hid, fmt, ...) \ dev_dbg_once(&(hid)->dev, fmt, ##__VA_ARGS__) +#define hid_err_ratelimited (hid, fmt, ...) \ + dev_err_ratelimited(&(hid)->dev, fmt, ##__VA_ARGS__) +#define hid_notice_ratelimited (hid, fmt, ...) \ + dev_notice_ratelimited(&(hid)->dev, fmt, ##__VA_ARGS__) +#define hid_warn_ratelimited (hid, fmt, ...) \ + dev_warn_ratelimited(&(hid)->dev, fmt, ##__VA_ARGS__) +#define hid_info_ratelimited (hid, fmt, ...) \ + dev_info_ratelimited(&(hid)->dev, fmt, ##__VA_ARGS__) +#define hid_dbg_ratelimited (hid, fmt, ...) \ + dev_dbg_ratelimited(&(hid)->dev, fmt, ##__VA_ARGS__) +#endif
Greg Kroah-Hartman
maintainerPATCH 201/452 HID: pass the buffer size to hid_report_raw_event
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Benjamin Tissoires <[email protected]>
commit 2c85c61d1332e1e16f020d76951baf167dcb6f7a upstream.
commit 0a3fe972a7cb ("HID: core: Mitigate potential OOB by removing bogus memset()") enforced the provided data to be at least the size of the declared buffer in the report descriptor to prevent a buffer overflow. However, we can try to be smarter by providing both the buffer size and the data size, meaning that hid_report_raw_event() can make better decision whether we should plaining reject the buffer (buffer overflow attempt) or if we can safely memset it to 0 and pass it to the rest of the stack.
--- drivers/hid/bpf/hid_bpf_dispatch.c | 3 ++- drivers/hid/hid-core.c | 35 +++++++++++++++++++++++++---------- drivers/hid/hid-gfrm.c | 4 ++-- drivers/hid/hid-logitech-hidpp.c | 2 +- drivers/hid/hid-multitouch.c | 2 +- drivers/hid/hid-primax.c | 2 +- drivers/hid/hid-vivaldi-common.c | 2 +- drivers/hid/wacom_sys.c | 6 +++--- drivers/staging/greybus/hid.c | 2 +- include/linux/hid.h | 4 ++-- include/linux/hid_bpf.h | 4 ++-- 11 files changed, 41 insertions(+), 25 deletions(-)
drivers/hid/bpf/hid_bpf_dispatch.c +2 -1
@@ -47,7 +47,7 @@ __weak noinline int hid_bpf_device_eventu8 *dispatch_hid_bpf_device_event (struct hid_device *hdev,enum hid_report_type type,u8 *data, -u32 *size,int interrupt) +size_t *buf_size,u32 *size,int interrupt) {struct hid_bpf_ctx_kern ctx_kern = { .ctx = {
@@ -81,6 +81,7 @@ dispatch_hid_bpf_device_event(struct hid *size = ret; } + *buf_size = ctx_kern.ctx.allocated_size;return ctx_kern.data; }EXPORT_SYMBOL_GPL (dispatch_hid_bpf_device_event);
drivers/hid/hid-core.c +25 -10
@@ -1986,24 +1986,32 @@ out: }EXPORT_SYMBOL_GPL (__hid_request); -int hid_report_raw_event (struct hid_device *hid,enum hid_report_type type,u8 *data,u32 size, -int interrupt) +int hid_report_raw_event (struct hid_device *hid,enum hid_report_type type,u8 *data, +size_t bufsize,u32 size,int interrupt) {struct hid_report_enum *report_enum = hid->report_enum + type;struct hid_report *report;struct hid_driver *hdrv;int max_buffer_size =HID_MAX_BUFFER_SIZE ;u32 rsize, csize = size; +size_t bsize = bufsize;u8 *cdata = data;int ret =0 ; report =hid_get_report (report_enum, data);if (!report) - goto out; +return 0 ; + +if (unlikely (bsize < csize)) { +hid_warn_ratelimited (hid,"Event data for report %d is incorrect (%d vs %ld)\n" , + report->id, csize, bsize); +return -EINVAL ; + }if (report_enum->numbered) { cdata++; csize--; + bsize--; } rsize =hid_compute_report_size (report);
@@ -2016,9 +2024,15 @@ int hid_report_raw_event(struct hid_devielse if (rsize > max_buffer_size) rsize = max_buffer_size; +if (bsize < rsize) { +hid_warn_ratelimited (hid,"Event data for report %d was too short (%d vs %ld)\n" , + report->id, rsize, bsize); +return -EINVAL ; + } +if (csize < rsize) {dbg_hid ("report %d is too short, (%d < %d)\n" , report->id, - csize, rsize); + csize, rsize);memset (cdata + csize,0 , rsize - csize); }
@@ -2027,7 +2041,7 @@ int hid_report_raw_event(struct hid_deviif (hid->claimed &HID_CLAIMED_HIDRAW ) { ret =hidraw_report_event (hid, data, size);if (ret) - goto out; +return ret; }if (hid->claimed !=HID_CLAIMED_HIDRAW && report->maxfield) {
@@ -2039,7 +2053,7 @@ int hid_report_raw_event(struct hid_deviif (hid->claimed &HID_CLAIMED_INPUT )hidinput_report_event (hid, report); -out: +return ret; }EXPORT_SYMBOL_GPL (hid_report_raw_event);
@@ -2055,12 +2069,13 @@ EXPORT_SYMBOL_GPL(hid_report_raw_event);* * This is data entry for lower layers. */ -int hid_input_report (struct hid_device *hid,enum hid_report_type type,u8 *data,u32 size, -int interrupt) +int hid_input_report (struct hid_device *hid,enum hid_report_type type,u8 *data, +u32 size,int interrupt) {struct hid_report_enum *report_enum;struct hid_driver *hdrv;struct hid_report *report; +size_t bufsize = size;int ret =0 ;if (!hid)
@@ -2076,7 +2091,7 @@ int hid_input_report(struct hid_device * report_enum = hid->report_enum + type; hdrv = hid->driver; - data =dispatch_hid_bpf_device_event (hid, type, data, &size, interrupt); + data =dispatch_hid_bpf_device_event (hid, type, data, &bufsize, &size, interrupt);if (IS_ERR (data)) { ret =PTR_ERR (data); goto unlock;
@@ -2105,7 +2120,7 @@ int hid_input_report(struct hid_device * goto unlock; } - ret =hid_report_raw_event (hid, type, data, size, interrupt); + ret =hid_report_raw_event (hid, type, data, bufsize, size, interrupt); unlock:up (&hid->driver_input_lock);
drivers/hid/hid-gfrm.c +2 -2
@@ -66,7 +66,7 @@ static int gfrm_raw_event(struct hid_devswitch (data[1 ]) {case GFRM100_SEARCH_KEY_DOWN : ret =hid_report_raw_event (hdev,HID_INPUT_REPORT , search_key_dn, -sizeof (search_key_dn),1 ); +sizeof (search_key_dn),sizeof (search_key_dn),1 );break ;case GFRM100_SEARCH_KEY_AUDIO_DATA :
@@ -74,7 +74,7 @@ static int gfrm_raw_event(struct hid_devcase GFRM100_SEARCH_KEY_UP : ret =hid_report_raw_event (hdev,HID_INPUT_REPORT , search_key_up, -sizeof (search_key_up),1 ); +sizeof (search_key_up),sizeof (search_key_up),1 );break ;default :
drivers/hid/hid-logitech-hidpp.c +1 -1
@@ -3692,7 +3692,7 @@ static int hidpp10_consumer_keys_raw_evememcpy (&consumer_report[1 ], &data[3 ],4 );/* We are called from atomic context */ hid_report_raw_event (hidpp->hid_dev,HID_INPUT_REPORT , - consumer_report,5 ,1 ); + consumer_report,sizeof (consumer_report),5 ,1 );return 1 ; }
drivers/hid/hid-multitouch.c +1 -1
@@ -479,7 +479,7 @@ static void mt_get_feature(struct hid_de } ret =hid_report_raw_event (hdev,HID_FEATURE_REPORT , buf, - size,0 ); + size, size,0 );if (ret)dev_warn (&hdev->dev,"failed to report feature\n" ); }
drivers/hid/hid-primax.c +1 -1
@@ -44,7 +44,7 @@ static int px_raw_event(struct hid_devic data[0 ] |= (1 << (data[idx] -0xE0 )); data[idx] =0 ; } -hid_report_raw_event (hid,HID_INPUT_REPORT , data, size,0 ); +hid_report_raw_event (hid,HID_INPUT_REPORT , data, size, size,0 );return 1 ;default :/* unknown report */
drivers/hid/hid-vivaldi-common.c +1 -1
@@ -85,7 +85,7 @@ void vivaldi_feature_mapping(struct hid_ } ret =hid_report_raw_event (hdev,HID_FEATURE_REPORT , report_data, - report_len,0 ); + report_len, report_len,0 );if (ret) {dev_warn (&hdev->dev,"failed to report feature %d\n" , field->report->id);
drivers/hid/wacom_sys.c +3 -3
@@ -74,7 +74,7 @@ static void wacom_wac_queue_flush(structint err; size =kfifo_out (fifo, buf,sizeof (buf)); - err =hid_report_raw_event (hdev,HID_INPUT_REPORT , buf, size, false); + err =hid_report_raw_event (hdev,HID_INPUT_REPORT , buf, size, size, false);if (err) {hid_warn (hdev,"%s: unable to flush event due to error %d\n" , __func__, err);
@@ -319,7 +319,7 @@ static void wacom_feature_mapping(struct data, n, WAC_CMD_RETRIES);if (ret == n && features->type ==HID_GENERIC ) { ret =hid_report_raw_event (hdev, -HID_FEATURE_REPORT , data, n,0 ); +HID_FEATURE_REPORT , data, n, n,0 ); }else if (ret ==2 && features->type !=HID_GENERIC ) { features->touch_max = data[1 ]; }else {
@@ -381,7 +381,7 @@ static void wacom_feature_mapping(struct data, n, WAC_CMD_RETRIES);if (ret == n) { ret =hid_report_raw_event (hdev,HID_FEATURE_REPORT , - data, n,0 ); + data, n, n,0 ); }else {hid_warn (hdev,"%s: could not retrieve sensor offsets\n" , __func__);
drivers/staging/greybus/hid.c +1 -1
@@ -201,7 +201,7 @@ static void gb_hid_init_report(struct gb* we just need to setup the input fields, so using * hid_report_raw_event is safe. */ -hid_report_raw_event (ghid->hid, report->type, ghid->inbuf, size,1 ); +hid_report_raw_event (ghid->hid, report->type, ghid->inbuf, ghid->bufsize, size,1 ); }static void gb_hid_init_reports (struct gb_hid *ghid)
include/linux/hid.h +2 -2
@@ -1205,8 +1205,8 @@ static inline u32 hid_report_len(structreturn DIV_ROUND_UP (report->size,8 ) + (report->id >0 ); } -int hid_report_raw_event (struct hid_device *hid,enum hid_report_type type,u8 *data,u32 size, -int interrupt); +int hid_report_raw_event (struct hid_device *hid,enum hid_report_type type,u8 *data, +size_t bufsize,u32 size,int interrupt);/* HID quirks API */ unsigned long hid_lookup_quirk (const struct hid_device *hdev);
include/linux/hid_bpf.h +2 -2
@@ -147,7 +147,7 @@ struct hid_bpf_link {#ifdef CONFIG_HID_BPF u8 *dispatch_hid_bpf_device_event (struct hid_device *hid,enum hid_report_type type,u8 *data, -u32 *size,int interrupt); +size_t *buf_size,u32 *size,int interrupt);int hid_bpf_connect_device (struct hid_device *hdev);void hid_bpf_disconnect_device (struct hid_device *hdev);void hid_bpf_destroy_device (struct hid_device *hid);
@@ -155,7 +155,7 @@ void hid_bpf_device_init(struct hid_deviu8 *call_hid_bpf_rdesc_fixup (struct hid_device *hdev,u8 *rdesc,unsigned int *size);#else /* CONFIG_HID_BPF */ static inline u8 *dispatch_hid_bpf_device_event (struct hid_device *hid,enum hid_report_type type, -u8 *data,u32 *size,int interrupt) {return data; } +u8 *data,size_t *buf_size,u32 *size,int interrupt) {return data; }static inline int hid_bpf_connect_device (struct hid_device *hdev) {return 0 ; }static inline void hid_bpf_disconnect_device (struct hid_device *hdev) {}static inline void hid_bpf_destroy_device (struct hid_device *hid) {}
Greg Kroah-Hartman
maintainerPATCH 202/452 HID: core: Fix size_t specifier in hid_report_raw_event()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Nathan Chancellor <[email protected]>
commit 4d3a2a466b8d68d852a1f3bbf11204b718428dc4 upstream.
When building for 32-bit platforms, for which 'size_t' is 'unsigned int', there are warnings around using the incorrect format specifier to print bsize in hid_report_raw_event():
drivers/hid/hid-core.c:2054:29: error: format specifies type 'long' but the argument has type 'size_t' (aka 'unsigned int') [-Werror,-Wformat]
2053 | hid_warn_ratelimited(hid, "Event data for report %d is incorrect (%d vs %ld)\n",
| ~~~
| %zu
2054 | report->id, csize, bsize);
| ^~~~~
drivers/hid/hid-core.c:2076:29: error: format specifies type 'long' but the argument has type 'size_t' (aka 'unsigned int') [-Werror,-Wformat]
2075 | hid_warn_ratelimited(hid, "Event data for report %d was too short (%d vs %ld)\n",
| ~~~
| %zu
2076 | report->id, rsize, bsize);
| ^~~~~Use the proper 'size_t' format specifier, '%zu', to clear up the warnings.
--- drivers/hid/hid-core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/hid/hid-core.c +2 -2
@@ -2003,7 +2003,7 @@ int hid_report_raw_event(struct hid_devireturn 0 ;if (unlikely (bsize < csize)) { -hid_warn_ratelimited (hid,"Event data for report %d is incorrect (%d vs %ld)\n" , +hid_warn_ratelimited (hid,"Event data for report %d is incorrect (%d vs %zu)\n" , report->id, csize, bsize);return -EINVAL ; }
@@ -2025,7 +2025,7 @@ int hid_report_raw_event(struct hid_devi rsize = max_buffer_size;if (bsize < rsize) { -hid_warn_ratelimited (hid,"Event data for report %d was too short (%d vs %ld)\n" , +hid_warn_ratelimited (hid,"Event data for report %d was too short (%d vs %zu)\n" , report->id, rsize, bsize);return -EINVAL ; }
Greg Kroah-Hartman
maintainerPATCH 203/452 usb: core: Fix SuperSpeed root hub wMaxPacketSize
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Michal Pecio <[email protected]>
commit d1e280334b7f0a1df441e08bd1f6a1bcc36b3bbb upstream.
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.
--- drivers/usb/core/hcd.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-)
drivers/usb/core/hcd.c +1 -3
@@ -332,9 +332,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 */
Greg Kroah-Hartman
maintainerPATCH 204/452 bpf: Free reuseport cBPF prog after RCU grace period.
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kuniyuki Iwashima <[email protected]>
[ Upstream commit 18fc650ccd7fe3376eca89203668cfb8268f60df ]
Eulgyu Kim reported the splat below with a repro. [0]
The repro sets up a UDP reuseport group with a cBPF prog and replaces it with a new one while another thread is sending a UDP packet to the group.
The reuseport prog is freed by sk_reuseport_prog_free(). bpf_prog_put() is called for "e"BPF prog to destruct through multiple stages while cBPF prog is freed immediately by bpf_release_orig_filter() and bpf_prog_free().
If a reuseport prog is detached from the setsockopt() path (reuseport_attach_prog() or reuseport_detach_prog()), sk_reuseport_prog_free() is called without waiting for RCU readers to complete, resulting in various bugs.
Let's defer freeing the reuseport cBPF prog after one RCU grace period.
Note "e"BPF prog is safe as is unless the fast path starts to touch fields destroyed in bpf_prog_put_deferred() and __bpf_prog_put_noref().
[0]:
BUG: KASAN: vmalloc-out-of-bounds in reuseport_select_sock+0xedc/0x1220 net/core/sock_reuseport.c:596 Read of size 4 at addr ffffc9000051e004 by task slowme/10208 CPU: 6 UID: 1000 PID: 10208 Comm: slowme Not tainted 7.0.0-geb7ac95ff75e #32 PREEMPT(full) Hardware name: QEMU Ubuntu 24.04 PC v2 (i440FX + PIIX, arch_caps fix, 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014 Call Trace: <IRQ> dump_stack_lvl+0xe8/0x150 lib/dump_stack.c:120 print_address_description mm/kasan/report.c:378 [inline] print_report+0xca/0x240 mm/kasan/report.c:482 kasan_report+0x118/0x150 mm/kasan/report.c:595 reuseport_select_sock+0xedc/0x1220 net/core/sock_reuseport.c:596 udp4_lib_lookup2+0x3bc/0x950 net/ipv4/udp.c:495 __udp4_lib_lookup+0x768/0xe20 net/ipv4/udp.c:723 __udp4_lib_lookup_skb+0x297/0x390 net/ipv4/udp.c:752 __udp4_lib_rcv+0x1312/0x2620 net/ipv4/udp.c:2752 ip_protocol_deliver_rcu+0x282/0x440 net/ipv4/ip_input.c:207 ip_local_deliver_finish+0x3bb/0x6f0 net/ipv4/ip_input.c:241 NF_HOOK+0x30c/0x3a0 include/linux/netfilter.h:318 NF_HOOK+0x30c/0x3a0 include/linux/netfilter.h:318 __netif_receive_skb_one_core net/core/dev.c:6181 [inline] __netif_receive_skb net/core/dev.c:6294 [inline] process_backlog+0xaa4/0x1960 net/core/dev.c:6645 __napi_poll+0xae/0x340 net/core/dev.c:7709 napi_poll net/core/dev.c:7772 [inline] net_rx_action+0x5d7/0xf50 net/core/dev.c:7929 handle_softirqs+0x22b/0x870 kernel/softirq.c:622 do_softirq+0x76/0xd0 kernel/softirq.c:523 </IRQ> <TASK> __local_bh_enable_ip+0xf8/0x130 kernel/softirq.c:450 local_bh_enable include/linux/bottom_half.h:33 [inline] rcu_read_unlock_bh include/linux/rcupdate.h:924 [inline] __dev_queue_xmit+0x1dd7/0x3710 net/core/dev.c:4890 neigh_output include/net/neighbour.h:556 [inline] ip_finish_output2+0xca9/0x1070 net/ipv4/ip_output.c:237 NF_HOOK_COND include/linux/netfilter.h:307 [inline] ip_output+0x29f/0x450 net/ipv4/ip_output.c:438 ip_send_skb+0x45/0xc0 net/ipv4/ip_output.c:1508 udp_send_skb+0xb04/0x1510 net/ipv4/udp.c:1195 udp_sendmsg+0x1a71/0x2350 net/ipv4/udp.c:1485 sock_sendmsg_nosec net/socket.c:727 [inline] __sock_sendmsg net/socket.c:742 [inline] __sys_sendto+0x554/0x680 net/socket.c:2206 __do_sys_sendto net/socket.c:2213 [inline] __se_sys_sendto net/socket.c:2209 [inline] __x64_sys_sendto+0xde/0x100 net/socket.c:2209 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline] do_syscall_64+0x160/0xf80 arch/x86/entry/syscall_64.c:94 entry_SYSCALL_64_after_hwframe+0x77/0x7f RIP: 0033:0x415a2d Code: b3 66 2e 0f 1f 84 00 00 00 00 00 66 90 f3 0f 1e fa 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 b8 ff ff ff f7 d8 64 89 01 48 RSP: 002b:00007f6bc31e41e8 EFLAGS: 00000212 ORIG_RAX: 000000000000002c RAX: ffffffffffffffda RBX: 00007f6bc31e4cdc RCX: 0000000000415a2d RDX: 0000000000000001 RSI: 00007f6bc31e421f RDI: 0000000000000003 RBP: 00007f6bc31e4240 R08: 00007f6bc31e4220 R09: 0000000000000010 R10: 0000000000000000 R11: 0000000000000212 R12: 00007f6bc31e46c0 R13: ffffffffffffffb8 R14: 0000000000000000 R15: 00007ffc9b0d70b0 </TASK>
--- net/core/filter.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-)
net/core/filter.c +12 -3
@@ -1642,15 +1642,24 @@ int sk_reuseport_attach_bpf(u32 ufd, struct sock *sk)return err; } +static void sk_reuseport_prog_free_rcu (struct rcu_head *rcu) +{ + struct bpf_prog_aux *aux = container_of(rcu, struct bpf_prog_aux, rcu); +struct bpf_prog *prog = aux->prog; + +bpf_release_orig_filter (prog); +bpf_prog_free (prog); +} +void sk_reuseport_prog_free (struct bpf_prog *prog) {if (!prog)return ; -if (prog->type ==BPF_PROG_TYPE_SK_REUSEPORT ) -bpf_prog_put (prog); +if (bpf_prog_was_classic (prog)) +call_rcu (&prog->aux->rcu, sk_reuseport_prog_free_rcu);else -bpf_prog_destroy (prog); +bpf_prog_put (prog); }struct bpf_scratchpad {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 205/452 USB: serial: mct_u232: fix memory corruption with small endpoint
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Johan Hovold <[email protected]>
commit 915b36d701950503c4ea0f6e314b10868e59fce3 upstream.
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.
--- drivers/usb/serial/mct_u232.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-)
drivers/usb/serial/mct_u232.c +11 -10
@@ -379,6 +379,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) {
@@ -386,6 +387,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 (sizeof (*priv),GFP_KERNEL );if (!priv)return -ENOMEM ;
@@ -411,7 +422,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;
@@ -419,15 +429,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
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 206/452 Bluetooth: hci_conn: fix potential UAF in set_cig_params_sync
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pauli Virtanen <[email protected]>
[ Upstream commit a2639a7f0f5bf7d73f337f8f077c19415c62ed2c ]
hci_conn lookup and field access must be covered by hdev lock in set_cig_params_sync, otherwise it's possible it is freed concurrently.
Take hdev lock to prevent hci_conn from being deleted or modified concurrently. Just RCU lock is not suitable here, as we also want to avoid "tearing" in the configuration.
--- net/bluetooth/hci_conn.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-)
net/bluetooth/hci_conn.c +7 -1
@@ -1734,9 +1734,13 @@ static int set_cig_params_sync(struct hci_dev *hdev, void *data)struct iso_cig_params pdu;u8 cis_id; +hci_dev_lock (hdev); + conn =hci_conn_hash_lookup_cig (hdev, cig_id); -if (!conn) +if (!conn) { +hci_dev_unlock (hdev);return 0 ; + }memset (&pdu,0 ,sizeof (pdu));
@@ -1776,6 +1780,8 @@ static int set_cig_params_sync(struct hci_dev *hdev, void *data) cis->p_rtn = qos->ucast.in.rtn; } +hci_dev_unlock (hdev); +if (!pdu.cp.num_cis)return 0 ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 207/452 RDMA/rxe: Fix "trying to register non-static key in rxe_qp_do_cleanup" bug
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zhu Yanjun <[email protected]>
commit 1c7eec4d5f3b39cdea2153abaebf1b7229a47072 upstream.
Call Trace: <TASK> __dump_stack lib/dump_stack.c:94 [inline] dump_stack_lvl+0x116/0x1f0 lib/dump_stack.c:120 assign_lock_key kernel/locking/lockdep.c:986 [inline] register_lock_class+0x4a3/0x4c0 kernel/locking/lockdep.c:1300 __lock_acquire+0x99/0x1ba0 kernel/locking/lockdep.c:5110 lock_acquire kernel/locking/lockdep.c:5866 [inline] lock_acquire+0x179/0x350 kernel/locking/lockdep.c:5823 __timer_delete_sync+0x152/0x1b0 kernel/time/timer.c:1644 rxe_qp_do_cleanup+0x5c3/0x7e0 drivers/infiniband/sw/rxe/rxe_qp.c:815 execute_in_process_context+0x3a/0x160 kernel/workqueue.c:4596 __rxe_cleanup+0x267/0x3c0 drivers/infiniband/sw/rxe/rxe_pool.c:232 rxe_create_qp+0x3f7/0x5f0 drivers/infiniband/sw/rxe/rxe_verbs.c:604 create_qp+0x62d/0xa80 drivers/infiniband/core/verbs.c:1250 ib_create_qp_kernel+0x9f/0x310 drivers/infiniband/core/verbs.c:1361 ib_create_qp include/rdma/ib_verbs.h:3803 [inline] rdma_create_qp+0x10c/0x340 drivers/infiniband/core/cma.c:1144 rds_ib_setup_qp+0xc86/0x19a0 net/rds/ib_cm.c:600 rds_ib_cm_initiate_connect+0x1e8/0x3d0 net/rds/ib_cm.c:944 rds_rdma_cm_event_handler_cmn+0x61f/0x8c0 net/rds/rdma_transport.c:109 cma_cm_event_handler+0x94/0x300 drivers/infiniband/core/cma.c:2184 cma_work_handler+0x15b/0x230 drivers/infiniband/core/cma.c:3042 process_one_work+0x9cc/0x1b70 kernel/workqueue.c:3238 process_scheduled_works kernel/workqueue.c:3319 [inline] worker_thread+0x6c8/0xf10 kernel/workqueue.c:3400 kthread+0x3c2/0x780 kernel/kthread.c:464 ret_from_fork+0x45/0x80 arch/x86/kernel/process.c:153 ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245 </TASK>
The root cause is as below:
In the function rxe_create_qp, the function rxe_qp_from_init is called to create qp, if this function rxe_qp_from_init fails, rxe_cleanup will be called to handle all the allocated resources, including the timers:retrans_timer and rnr_nak_timer.
The function rxe_qp_from_init calls the function rxe_qp_init_req to initialize the timers: retrans_timer and rnr_nak_timer.
But these timers are initialized in the end of rxe_qp_init_req. If some errors occur before the initialization of these timers, this problem will occur.
The solution is to check whether these timers are initialized or not. If these timers are not initialized, ignore these timers.
[ Vladislav: keep del_timer_sync() because linux-6.6.y has not renamed it
to timer_delete_sync() yet. The actual fix is unchanged: check the timer .function fields before deleting the timers. ]
--- drivers/infiniband/sw/rxe/rxe_qp.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-)
drivers/infiniband/sw/rxe/rxe_qp.c +6 -1
@@ -817,7 +817,12 @@ static void rxe_qp_do_cleanup(struct work_struct *work)spin_unlock_irqrestore (&qp->state_lock, flags); qp->qp_timeout_jiffies =0 ; -if (qp_type (qp) ==IB_QPT_RC ) { +/* In the function timer_setup, .function is initialized. If .function +* is NULL, it indicates the function timer_setup is not called, the +* timer is not initialized. Or else, the timer is initialized. +*/ +if (qp_type (qp) ==IB_QPT_RC && qp->retrans_timer.function && + qp->rnr_nak_timer.function) {del_timer_sync (&qp->retrans_timer);del_timer_sync (&qp->rnr_nak_timer); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 208/452 hwmon: (pmbus/core) Protect regulator operations with mutex
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Guenter Roeck <[email protected]>
[ Upstream commit 754bd2b4a084b90b5e7b630e1f423061a9b9b761 ]
The regulator operations pmbus_regulator_get_voltage(), pmbus_regulator_set_voltage(), and pmbus_regulator_list_voltage() access PMBus registers and shared data but were not protected by the update_lock mutex. This could lead to race conditions.
However, adding mutex protection directly to these functions causes a deadlock because pmbus_regulator_notify() (which calls regulator_notifier_call_chain()) is often called with the mutex already held (e.g., from pmbus_fault_handler()). If a regulator callback then calls one of the now-protected voltage functions, it will attempt to acquire the same mutex.
Rework pmbus_regulator_notify() to utilize a worker function to send notifications outside of the mutex protection. Events are stored as atomics in a per-page bitmask and processed by the worker.
Initialize the worker and its associated data during regulator registration, and ensure it is cancelled on device removal using devm_add_action_or_reset().
While at it, remove the unnecessary include of linux/of.h.
--- drivers/hwmon/pmbus/pmbus_core.c | 117 ++++++++++++++++++++++++------- 1 file changed, 91 insertions(+), 26 deletions(-)
drivers/hwmon/pmbus/pmbus_core.c +91 -26
@@ -6,6 +6,7 @@* Copyright (c) 2012 Guenter Roeck */ +#include <linux/atomic.h> #include <linux/debugfs.h> #include <linux/kernel.h> #include <linux/math64.h>
@@ -19,8 +20,8 @@#include <linux/pmbus.h> #include <linux/regulator/driver.h> #include <linux/regulator/machine.h> -#include <linux/of.h> #include <linux/thermal.h> +#include <linux/workqueue.h> #include "pmbus.h" /*
@@ -102,6 +103,11 @@ struct pmbus_data {struct mutex update_lock; +#if IS_ENABLED (CONFIG_REGULATOR ) +atomic_t regulator_events[PMBUS_PAGES ]; +struct work_struct regulator_notify_work; +#endif +bool has_status_word;/* device uses STATUS_WORD register */ int (*read_status)(struct i2c_client *client,int page);
@@ -3056,12 +3062,19 @@ static int pmbus_regulator_get_voltage(struct regulator_dev *rdev) .class = PSC_VOLTAGE_OUT, .convert = true, }; +int ret; +mutex_lock (&data->update_lock); s.data =_pmbus_read_word_data (client, s.page,0xff ,PMBUS_READ_VOUT ); -if (s.data <0 ) -return s.data; +if (s.data <0 ) { + ret = s.data; + goto unlock; + } -return (int )pmbus_reg2data (data, &s) *1000 ;/* unit is uV */ + ret = (int )pmbus_reg2data (data, &s) *1000 ;/* unit is uV */ +unlock: +mutex_unlock (&data->update_lock); +return ret; } static int pmbus_regulator_set_voltage(struct regulator_dev *rdev, int min_uv,
@@ -3078,16 +3091,22 @@ static int pmbus_regulator_set_voltage(struct regulator_dev *rdev, int min_uv, };int val =DIV_ROUND_CLOSEST (min_uv,1000 );/* convert to mV */ int low, high; +int ret; *selector =0 ; +mutex_lock (&data->update_lock); low =pmbus_regulator_get_low_margin (client, s.page); -if (low <0 ) -return low; +if (low <0 ) { + ret = low; + goto unlock; + } high =pmbus_regulator_get_high_margin (client, s.page); -if (high <0 ) -return high; +if (high <0 ) { + ret = high; + goto unlock; + }/* Make sure we are within margins */ if (low > val)
@@ -3097,7 +3116,10 @@ static int pmbus_regulator_set_voltage(struct regulator_dev *rdev, int min_uv, val =pmbus_data2reg (data, &s, val); -return _pmbus_write_word_data (client, s.page,PMBUS_VOUT_COMMAND , (u16 )val); + ret =_pmbus_write_word_data (client, s.page,PMBUS_VOUT_COMMAND , (u16 )val); +unlock: +mutex_unlock (&data->update_lock); +return ret; } static int pmbus_regulator_list_voltage(struct regulator_dev *rdev,
@@ -3105,7 +3127,9 @@ static int pmbus_regulator_list_voltage(struct regulator_dev *rdev, {struct device *dev =rdev_get_dev (rdev);struct i2c_client *client =to_i2c_client (dev->parent); +struct pmbus_data *data =i2c_get_clientdata (client);int val, low, high; +int ret;if (selector >= rdev->desc->n_voltages || selector < rdev->desc->linear_min_sel)
@@ -3115,18 +3139,29 @@ static int pmbus_regulator_list_voltage(struct regulator_dev *rdev, val =DIV_ROUND_CLOSEST (rdev->desc->min_uV + (rdev->desc->uV_step * selector),1000 );/* convert to mV */ +mutex_lock (&data->update_lock); + low =pmbus_regulator_get_low_margin (client,rdev_get_id (rdev)); -if (low <0 ) -return low; +if (low <0 ) { + ret = low; + goto unlock; + } high =pmbus_regulator_get_high_margin (client,rdev_get_id (rdev)); -if (high <0 ) -return high; +if (high <0 ) { + ret = high; + goto unlock; + } -if (val >= low && val <= high) -return val *1000 ;/* unit is uV */ +if (val >= low && val <= high) { + ret = val *1000 ;/* unit is uV */ + goto unlock; + } -return 0 ; + ret =0 ; +unlock: +mutex_unlock (&data->update_lock); +return ret; }const struct regulator_ops pmbus_regulator_ops = {
@@ -3141,12 +3176,42 @@ const struct regulator_ops pmbus_regulator_ops = { };EXPORT_SYMBOL_NS_GPL (pmbus_regulator_ops,PMBUS ); +static void pmbus_regulator_notify_work_cancel (void *data) +{ +struct pmbus_data *pdata = data; + +cancel_work_sync (&pdata->regulator_notify_work); +} + +static void pmbus_regulator_notify_worker (struct work_struct *work) +{ +struct pmbus_data *data = + container_of(work, struct pmbus_data, regulator_notify_work); +int i, j; + +for (i =0 ; i < data->info->pages; i++) { +int event; + + event =atomic_xchg (&data->regulator_events[i],0 ); +if (!event) +continue ; + +for (j =0 ; j < data->info->num_regulators; j++) { +if (i ==rdev_get_id (data->rdevs[j])) { +regulator_notifier_call_chain (data->rdevs[j], + event,NULL ); +break ; + } + } + } +} +static int pmbus_regulator_register (struct pmbus_data *data) {struct device *dev = data->dev;const struct pmbus_driver_info *info = data->info;const struct pmbus_platform_data *pdata =dev_get_platdata (dev); -int i; +int i, ret; data->rdevs =devm_kzalloc (dev,sizeof (struct regulator_dev *) * info->num_regulators,GFP_KERNEL );
@@ -3170,20 +3235,20 @@ static int pmbus_regulator_register(struct pmbus_data *data) info->reg_desc[i].name); } +INIT_WORK (&data->regulator_notify_work, pmbus_regulator_notify_worker); + + ret =devm_add_action_or_reset (dev, pmbus_regulator_notify_work_cancel, data); +if (ret) +return ret; +return 0 ; }static int pmbus_regulator_notify (struct pmbus_data *data,int page,int event) { -int j; - -for (j =0 ; j < data->info->num_regulators; j++) { -if (page ==rdev_get_id (data->rdevs[j])) { -regulator_notifier_call_chain (data->rdevs[j], event,NULL ); -break ; - } - } -return 0 ; +atomic_or (event, &data->regulator_events[page]); +schedule_work (&data->regulator_notify_work); +return 0 ; }#else static int pmbus_regulator_register (struct pmbus_data *data)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 209/452 Disable -Wattribute-alias for clang-23 and newer
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Nathan Chancellor <[email protected]>
commit 175db11786bde9061db526bf1ac5107d915f5163 upstream.
Clang recently added support for -Wattribute-alias [1], which results in the same warnings that necessitated commit bee20031772a ("disable -Wattribute-alias warning for SYSCALL_DEFINEx()") for GCC.
kernel/time/itimer.c:325:1: error: alias and aliasee have different types 'long (unsigned int)' and 'long (typeof (__builtin_choose_expr((__builtin_types_compatible_p(typeof ((unsigned int)0), typeof (0LL)) || __builtin_types_compatible_p(typeof ((unsigned int)0), typeof (0ULL))), 0LL, 0L)))' (aka 'long (long)') [-Werror,-Wattribute-alias]
325 | SYSCALL_DEFINE1(alarm, unsigned int, seconds)
| ^
include/linux/syscalls.h:225:36: note: expanded from macro 'SYSCALL_DEFINE1'
225 | #define SYSCALL_DEFINE1(name, ...) SYSCALL_DEFINEx(1, _##name, __VA_ARGS__)
| ^
include/linux/syscalls.h:236:2: note: expanded from macro 'SYSCALL_DEFINEx'
236 | __SYSCALL_DEFINEx(x, sname, __VA_ARGS__)
| ^
include/linux/syscalls.h:251:18: note: expanded from macro '__SYSCALL_DEFINEx'
251 | __attribute__((alias(__stringify(__se_sys##name)))); \
| ^
kernel/time/itimer.c:325:1: note: aliasee is declared here
include/linux/syscalls.h:225:36: note: expanded from macro 'SYSCALL_DEFINE1'
225 | #define SYSCALL_DEFINE1(name, ...) SYSCALL_DEFINEx(1, _##name, __VA_ARGS__)
| ^
include/linux/syscalls.h:236:2: note: expanded from macro 'SYSCALL_DEFINEx'
236 | __SYSCALL_DEFINEx(x, sname, __VA_ARGS__)
| ^
include/linux/syscalls.h:255:18: note: expanded from macro '__SYSCALL_DEFINEx'
255 | asmlinkage long __se_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__)) \
| ^
<scratch space>:16:1: note: expanded from here
16 | __se_sys_alarm
| ^Disable the warnings in the same way for clang-23 and newer. Disable the warning about unknown warning options to avoid breaking the build for versions of clang-23 that do not have -Wattribute-alias, such as ones deployed by vendors like Android or CI systems or when bisecting LLVM between llvmorg-23-init and release/23.x.
--- include/linux/compat.h | 4 ++++ include/linux/compiler-clang.h | 6 ++++++ include/linux/compiler_types.h | 4 ++++ include/linux/syscalls.h | 4 ++++ 4 files changed, 18 insertions(+)
include/linux/compat.h +4 -0
@@ -72,6 +72,10 @@__diag_push (); \__diag_ignore (GCC ,8 ,"-Wattribute-alias" , \"Type aliasing is used to sanitize syscall arguments" );\ +__diag_ignore (clang,23 ,"-Wunknown-warning-option" , \ +"Avoid breaking versions without -Wattribute-alias" ); \ +__diag_ignore (clang,23 ,"-Wattribute-alias" , \ +"Type aliasing is used to sanitize syscall arguments" ); \ asmlinkage long compat_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)) \ __attribute__((alias(__stringify(__se_compat_sys##name)))); \ ALLOW_ERROR_INJECTION(compat_sys##name, ERRNO); \
include/linux/compiler-clang.h +6 -0
@@ -144,5 +144,11 @@#define __diag_clang_11 (s)#endif +#if CONFIG_CLANG_VERSION >=230000 +#define __diag_clang_23 (s) __diag(s) +#else +#define __diag_clang_23 (s) +#endif +#define __diag_ignore_all (option, comment) \ __diag_clang(11, ignore, option)
include/linux/compiler_types.h +4 -0
@@ -486,6 +486,10 @@ struct ftrace_likely_data {#define __diag_GCC (version, severity, string)#endif +#ifndef __diag_clang +#define __diag_clang (version, severity, string) +#endif +#define __diag_push () __diag(push)#define __diag_pop () __diag(pop)
include/linux/syscalls.h +4 -0
@@ -242,6 +242,10 @@ static inline int is_syscall_trace_event(struct trace_event_call *tp_event)__diag_push (); \__diag_ignore (GCC ,8 ,"-Wattribute-alias" , \"Type aliasing is used to sanitize syscall arguments" );\ +__diag_ignore (clang,23 ,"-Wunknown-warning-option" , \ +"Avoid breaking versions without -Wattribute-alias" );\ +__diag_ignore (clang,23 ,"-Wattribute-alias" , \ +"Type aliasing is used to sanitize syscall arguments" );\ asmlinkage long sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)) \ __attribute__((alias(__stringify(__se_sys##name)))); \ ALLOW_ERROR_INJECTION(sys##name, ERRNO); \
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 210/452 i2c: dev: prevent integer overflow in I2C_TIMEOUT ioctl
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mingyu Wang <[email protected]>
commit 617eb7c0961a8dfcfc811844a6396e406b2923ea upstream.
While fuzzing with Syzkaller, a persistent schedule_timeout: wrong timeout value warning was observed, accompanied by SMBus controller state machine corruption.
The I2C_TIMEOUT ioctl accepts a user-provided timeout in multiples of 10 ms. The user argument is checked against INT_MAX, but it is subsequently multiplied by 10 before being passed to msecs_to_jiffies().
A malicious user can pass a large value (e.g., 429496729) that passes the arg > INT_MAX check but overflows when multiplied by 10. This results in a truncated 32-bit unsigned value that bypasses the internal (int)m < 0 check in msecs_to_jiffies().
The truncated value is then assigned to client->adapter->timeout (a signed 32-bit int), which is reinterpreted as a negative number. When passed to wait_for_completion_timeout(), this negative value undergoes sign extension to a 64-bit unsigned long, triggering the schedule_timeout warning and causing premature returns. This leaves the SMBus state machine in an unrecoverable state, constituting a local Denial of Service (DoS).
Fix this by bounding the user argument to INT_MAX / 10.
--- drivers/i2c/i2c-dev.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-)
drivers/i2c/i2c-dev.c +5 -4
@@ -476,12 +476,13 @@ static long i2cdev_ioctl(struct file *fi client->adapter->retries = arg;break ;case I2C_TIMEOUT : -if (arg >INT_MAX ) +/* +* For historical reasons, user-space sets the timeout value in +* units of 10 ms. +*/ +if (arg >INT_MAX /10 )return -EINVAL ; -/* For historical reasons, user-space sets the timeout -* value in units of 10 ms. -*/ client->adapter->timeout =msecs_to_jiffies (arg *10 );break ;default :
Greg Kroah-Hartman
maintainerPATCH 211/452 ipv6: mcast: Fix use-after-free when processing MLD queries
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ido Schimmel <[email protected]>
commit 791c91dc7a9dfb2457d5e29b8216a6484b9c4b40 upstream.
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: <TASK> 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)
</TASK>
[...]
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)
--- net/ipv6/mcast.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
net/ipv6/mcast.c +4 -4
@@ -1392,9 +1392,9 @@ out: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;
@@ -1426,8 +1426,8 @@ static void __mld_query_work(struct sk_b 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 ))
@@ -1477,7 +1477,7 @@ static void __mld_query_work(struct sk_b } }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 */
Greg Kroah-Hartman
maintainerPATCH 212/452 net/smc: fix sleep-inside-lock in __smc_setsockopt() causing local DoS
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Nicolò Coccia <[email protected]>
commit a3fdd924d88c30b9f488636ce0e4696012cf5511 upstream.
A logic flaw in __smc_setsockopt() allows a local unprivileged user to cause a Denial of Service (DoS) by holding the socket lock indefinitely.
The function __smc_setsockopt() calls copy_from_sockptr() while holding lock_sock(sk). By passing a userfaultfd-monitored memory page (or FUSE-backed memory on systems where unprivileged userfaultfd is disabled) as the optval, an attacker can halt execution during the copy operation, keeping the lock held.
Combined with asynchronous tear-down operations like shutdown(), this exhausts the kernel wq (kworkers) and triggers the hung task watchdog.
[ 240.123456] INFO: task kworker/u8:2 blocked for more than 120 seconds. [ 240.123489] Call Trace: [ 240.123501] smc_shutdown+... [ 240.123512] lock_sock_nested+...
This patch moves the user-space copy outside the lock_sock() critical section to prevent the issue.
--- net/smc/af_smc.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-)
net/smc/af_smc.c +8 -9
@@ -3048,18 +3048,17 @@ static int __smc_setsockopt(struct socke smc =smc_sk (sk); +/* pre-fetch user data outside the lock */ +if (optname ==SMC_LIMIT_HS ) { +if (optlen <sizeof (int )) +return -EINVAL ; +if (copy_from_sockptr (&val, optval,sizeof (int ))) +return -EFAULT ; + } +lock_sock (sk);switch (optname) {case SMC_LIMIT_HS : -if (optlen <sizeof (int )) { - rc = -EINVAL ; -break ; - } -if (copy_from_sockptr (&val, optval,sizeof (int ))) { - rc = -EFAULT ; -break ; - } - smc->limit_smc_hs = !!val; rc =0 ;break ;
Greg Kroah-Hartman
maintainerPATCH 213/452 tee: optee: prevent use-after-free when the client exits before the supplicant
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Amirreza Zarrabi <[email protected]>
[ Upstream commit 387a926ee166814611acecb960207fe2f3c4fd3e ]
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.
--- drivers/tee/optee/supp.c | 107 +++++++++++++++++++++++++++------------ 1 file changed, 74 insertions(+), 33 deletions(-)
drivers/tee/optee/supp.c +74 -33
@@ -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 ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 214/452 ksmbd: fix NULL-deref of opinfo->conn in oplock/lease break notifiers
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Gil Portnoy <[email protected]>
[ Upstream commit b003086d76968298f22e7cf62239833b5a3a06b1 ]
smb2_oplock_break_noti() and smb2_lease_break_noti() read opinfo->conn into a local with neither READ_ONCE() nor a NULL check. Both run from oplock_break() after opinfo_get_list() has dropped ci->m_lock, so a concurrent SMB2 LOGOFF (session_fd_check()) can set op->conn = NULL under ci->m_lock within that window. ksmbd_conn_r_count_inc(conn) then writes through NULL at offset 0xc4 -- a remotely triggerable oops.
Guard both reads the way compare_guid_key() already does: read opinfo->conn with READ_ONCE() and return early if it is NULL, before allocating the work struct so nothing leaks. A NULL conn means the client is gone and the break is moot, so return 0; oplock_break() treats that as success and runs the normal teardown.
--- fs/smb/server/oplock.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-)
fs/smb/server/oplock.c +12 -3
@@ -714,11 +714,16 @@ static void __smb2_oplock_break_noti(struct work_struct *wk)*/ static int smb2_oplock_break_noti (struct oplock_info *opinfo) { -struct ksmbd_conn *conn = opinfo->conn; +struct ksmbd_conn *conn;struct oplock_break_info *br_info;int ret =0 ; -struct ksmbd_work *work =ksmbd_alloc_work_struct (); +struct ksmbd_work *work; + + conn =READ_ONCE (opinfo->conn); +if (!conn) +return 0 ; + work =ksmbd_alloc_work_struct ();if (!work)return -ENOMEM ;
@@ -818,11 +823,15 @@ static void __smb2_lease_break_noti(struct work_struct *wk)*/ static int smb2_lease_break_noti (struct oplock_info *opinfo) { -struct ksmbd_conn *conn = opinfo->conn; +struct ksmbd_conn *conn;struct ksmbd_work *work;struct lease_break_info *br_info;struct lease *lease = opinfo->o_lease; + conn =READ_ONCE (opinfo->conn); +if (!conn) +return 0 ; + work =ksmbd_alloc_work_struct ();if (!work)return -ENOMEM ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 215/452 netfilter: xt_NFQUEUE: prefer raw_smp_processor_id
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Fernando Fernandez Mancera <[email protected]>
[ Upstream commit c6c5327dd18bec1e1bbf139b2cf5ae53608a9d30 ]
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.
--- net/netfilter/xt_NFQUEUE.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/netfilter/xt_NFQUEUE.c +1 -1
@@ -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 {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 216/452 ipvs: clear the svc scheduler ptr early on edit
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Julian Anastasov <[email protected]>
[ Upstream commit 193989cc6d80dd8e0460fb3992e69fa03bf0ff9b ]
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.
--- 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(-)
include/net/ip_vs.h +1 -2
@@ -1506,8 +1506,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 *
net/netfilter/ipvs/ip_vs_ctl.c +8 -5
@@ -1496,7 +1496,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);
@@ -1558,9 +1558,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 */
@@ -1568,6 +1567,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; } }
@@ -1624,7 +1627,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 */
net/netfilter/ipvs/ip_vs_sched.c +7 -7
@@ -57,19 +57,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 */ }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 217/452 netfilter: synproxy: add mutex to guard hook reference counting
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Fernando Fernandez Mancera <[email protected]>
[ Upstream commit 2fcba19caaeb2a33017459d3430f057967bb91b6 ]
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.
--- net/netfilter/nf_synproxy_core.c | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-)
net/netfilter/nf_synproxy_core.c +18 -6
@@ -21,6 +21,8 @@#include <net/netfilter/nf_conntrack_zones.h> #include <net/netfilter/nf_synproxy.h> +static DEFINE_MUTEX (synproxy_mutex); +unsigned int synproxy_net_id;EXPORT_SYMBOL_GPL (synproxy_net_id);
@@ -768,26 +770,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);
@@ -1192,27 +1199,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 */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 218/452 netfilter: conntrack_irc: fix possible out-of-bounds read
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Florian Westphal <[email protected]>
[ Upstream commit 66eba0ffce3b7e11449946b4cbbef8ea36112f56 ]
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.
--- net/netfilter/nf_conntrack_irc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
net/netfilter/nf_conntrack_irc.c +2 -2
@@ -208,7 +208,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",
@@ -222,7 +222,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);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 219/452 netfilter: nft_ct: bail out on template ct in get eval
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jiayuan Chen <[email protected]>
[ Upstream commit 3027ecbdb5fdf9200251c21d4818e4c447ef78e1 ]
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
--- net/netfilter/nft_ct.c | 8 +++----- net/netfilter/nft_ct_fast.c | 2 +- 2 files changed, 4 insertions(+), 6 deletions(-)
net/netfilter/nft_ct.c +3 -5
@@ -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);
net/netfilter/nft_ct_fast.c +1 -1
@@ -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 ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 220/452 netfilter: bridge: make ebt_snat ARP rewrite writable
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yiming Qian <[email protected]>
[ Upstream commit 67ba971ae02514d85818fe0c32549ab4bfa3bf49 ]
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().
--- net/bridge/netfilter/ebt_snat.c | 3 +++ 1 file changed, 3 insertions(+)
net/bridge/netfilter/ebt_snat.c +3 -0
@@ -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 ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 221/452 dm cache policy smq: check allocation under invalidate lock
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Guangshuo Li <[email protected]>
[ Upstream commit d3f0a606b9f278ece8a0df626ded9c4044071235 ]
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.
--- drivers/md/dm-cache-policy-smq.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-)
drivers/md/dm-cache-policy-smq.c +8 -4
@@ -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)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 222/452 net/sched: act_api: use RCU with deferred freeing for action lifecycle
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jamal Hadi Salim <[email protected]>
[ Upstream commit 5057e1aca011e51ef51498c940ef96f3d3e8a305 ]
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.
--- include/net/act_api.h | 1 + net/sched/act_api.c | 7 +------ 2 files changed, 2 insertions(+), 6 deletions(-)
include/net/act_api.h +1 -0
@@ -42,6 +42,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;
net/sched/act_api.c +1 -6
@@ -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,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 223/452 6lowpan: fix off-by-one in multicast context address compression
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yizhou Zhao <[email protected]>
[ Upstream commit 2a58899d11009bffc7b4b32a571858f381121837 ]
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.
--- net/6lowpan/iphc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
net/6lowpan/iphc.c +2 -2
@@ -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 ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 224/452 drm/imx: Fix three kernel-doc warnings in dcss-scaler.c
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yicong Hui <[email protected]>
[ Upstream commit ae0383e5a9a4b12d68c76c4769857def4665deff ]
Fix the following W=1 kerneldoc warnings by adding the missing parameter descriptions for @phase0_identity and @nn_interpolation in dcss_scaler_filter_design() and @phase0_identity in dcss_scaler_gaussian_filter()
Warning: drivers/gpu/drm/imx/dcss/dcss-scaler.c:173 function parameter 'phase0_identity' not described in 'dcss_scaler_gaussian_filter'
Warning: drivers/gpu/drm/imx/dcss/dcss-scaler.c:270 function parameter 'phase0_identity' not described in 'dcss_scaler_filter_design'
Warning: drivers/gpu/drm/imx/dcss/dcss-scaler.c:270 function parameter 'nn_interpolation' not described in 'dcss_scaler_filter_design'
--- drivers/gpu/drm/imx/dcss/dcss-scaler.c | 3 +++ 1 file changed, 3 insertions(+)
drivers/gpu/drm/imx/dcss/dcss-scaler.c +3 -0
@@ -166,6 +166,7 @@ static int exp_approx_q(int x)* dcss_scaler_gaussian_filter() - Generate gaussian prototype filter. * @fc_q: fixed-point cutoff frequency normalized to range [0, 1] * @use_5_taps: indicates whether to use 5 taps or 7 taps +* @phase0_identity: whether to override phase 0 coefficients with identity filter * @coef: output filter coefficients */ static void dcss_scaler_gaussian_filter(int fc_q, bool use_5_taps,
@@ -262,7 +263,9 @@ static void dcss_scaler_nearest_neighbor_filter(bool use_5_taps,* @src_length: length of input * @dst_length: length of output * @use_5_taps: 0 for 7 taps per phase, 1 for 5 taps +* @phase0_identity: whether to override phase 0 coefficients with identity filter * @coef: output coefficients +* @nn_interpolation: whether to use nearest neighbor instead of gaussian filter */ static void dcss_scaler_filter_design(int src_length, int dst_length, bool use_5_taps, bool phase0_identity,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 225/452 pcnet32: stop holding device spin lock during napi_complete_done
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Oscar Maes <[email protected]>
[ Upstream commit 73bf3cca7de6a73f53b6a52dc3b1c82ae5667a4d ]
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.
--- drivers/net/ethernet/amd/pcnet32.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/net/ethernet/amd/pcnet32.c +3 -1
@@ -1403,8 +1403,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 ;
@@ -1412,9 +1414,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; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 226/452 net: Annotate sk->sk_write_space() for UDP SOCKMAP.
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kuniyuki Iwashima <[email protected]>
[ Upstream commit b748765019fe9e9234660327090fc1a9665cdbdd ]
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}").
--- net/core/sock.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-)
net/core/sock.c +6 -2
@@ -2499,8 +2499,12 @@ void sock_wfree(struct sk_buff *skb)bool free;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);sock_def_write_space_wfree (sk);
@@ -2515,7 +2519,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 ; }/*
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 227/452 hsr: Remove WARN_ONCE() in hsr_addr_is_self().
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kuniyuki Iwashima <[email protected]>
[ Upstream commit afd0f17ca46258cec3a5cc48b8df9327fe772490 ]
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:
<TASK>
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
</TASK>--- net/hsr/hsr_framereg.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-)
net/hsr/hsr_framereg.c +1 -3
@@ -43,10 +43,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))
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 228/452 net: garp: fix unsigned integer underflow in garp_pdu_parse_attr
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yizhou Zhao <[email protected]>
[ Upstream commit 16e408e607a94b646fb14a2a98422c6877ae4b3c ]
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.
--- net/802/garp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/802/garp.c +1 -1
@@ -452,7 +452,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 ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 229/452 net: lan743x: permit VLAN-tagged packets up to configured MTU
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Thompson <[email protected]>
[ Upstream commit 8173d22b211f615015f7b35f48ab11a6dd78dc99 ]
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.
--- drivers/net/ethernet/microchip/lan743x_main.c | 32 +++++++++++++++++++ drivers/net/ethernet/microchip/lan743x_main.h | 1 + 2 files changed, 33 insertions(+)
drivers/net/ethernet/microchip/lan743x_main.c +32 -0
@@ -1255,6 +1255,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;
@@ -1294,6 +1324,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 ; }
drivers/net/ethernet/microchip/lan743x_main.h +1 -0
@@ -180,6 +180,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)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 230/452 net: fec: fix pinctrl default state restore order on resume
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Tapio Reijonen <[email protected]>
[ Upstream commit b455410146bf723c7ebcb49ecd5becc0d6611482 ]
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.
--- drivers/net/ethernet/freescale/fec_main.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
drivers/net/ethernet/freescale/fec_main.c +1 -2
@@ -4734,6 +4734,7 @@ static int __maybe_unused 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 ();
@@ -4750,8 +4751,6 @@ static int __maybe_unused 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);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 231/452 Bluetooth: RFCOMM: hold listener socket in rfcomm_connect_ind()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zhang Cen <[email protected]>
[ Upstream commit 43c441edacf953b39517a44f5e5e10a93618b226 ]
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().
--- net/bluetooth/rfcomm/sock.c | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-)
net/bluetooth/rfcomm/sock.c +22 -4
@@ -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; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 232/452 Bluetooth: MGMT: validate advertising TLV before type checks
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zhang Cen <[email protected]>
[ Upstream commit de23fb62259aa01d294f77238ae3b835eb674413 ]
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].
--- net/bluetooth/mgmt.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-)
net/bluetooth/mgmt.c +6 -6
@@ -8667,6 +8667,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;
@@ -8683,12 +8689,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;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 233/452 Bluetooth: RFCOMM: validate skb length in MCC handlers
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: SeungJu Cheon <[email protected]>
[ Upstream commit 23882b828c3c8c51d0c946446a396b10abb3b16b ]
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.
--- net/bluetooth/rfcomm/core.c | 67 +++++++++++++++++++++++++++---------- 1 file changed, 49 insertions(+), 18 deletions(-)
net/bluetooth/rfcomm/core.c +49 -18
@@ -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 (len ==1 ) { + rpn =skb_pull_data (skb,1 ); +if (!rpn) +return -EILSEQ ; -if (!cr) -return 0 ; + dlci =__get_dlci (rpn->dlci); + +if (!cr) +return 0 ; -if (len ==1 ) { -/* This is a request, return default (according to ETSI TS 07.10) settings */ 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 *skbstatic 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 *skbstatic 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);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 234/452 Bluetooth: bnep: fix incorrect length parsing in bnep_rx_frame() extension handling
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Dudu Lu <[email protected]>
[ Upstream commit 72b8deccff17a7644e0367e1aaf1a36cfb014324 ]
In bnep_rx_frame(), the BNEP_FILTER_NET_TYPE_SET and BNEP_FILTER_MULTI_ADDR_SET extension header parsing has two bugs:
1) The 2-byte length field is read with *(u16 *)(skb->data + 1), which performs a native-endian read. The BNEP protocol specifies this field in big-endian (network byte order), and the same file correctly uses get_unaligned_be16() for the identical fields in bnep_ctrl_set_netfilter() and bnep_ctrl_set_mcfilter().
2) The length is multiplied by 2, but unlike BNEP_SETUP_CONN_REQ where the length byte counts UUID pairs (requiring * 2 for two UUIDs per entry), the filter extension length field already represents the total data size in bytes. This is confirmed by bnep_ctrl_set_netfilter() which reads the same field as a byte count and divides by 4 to get the number of filter entries.
The bogus * 2 means skb_pull advances twice as far as it should, either dropping valid data from the next header or causing the pull to fail entirely when the doubled length exceeds the remaining skb.
Fix by splitting the pull into two steps: first use skb_pull_data() to safely pull and validate the 3-byte fixed header (ctrl type + length), then pull the variable-length data using the properly decoded length.
--- net/bluetooth/bnep/core.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-)
net/bluetooth/bnep/core.c +10 -3
@@ -330,11 +330,18 @@ static int bnep_rx_frame(struct bnep_session *s, struct sk_buff *skb) goto badframe;break ;case BNEP_FILTER_MULTI_ADDR_SET : -case BNEP_FILTER_NET_TYPE_SET : -/* Pull: ctrl type (1 b), len (2 b), data (len bytes) */ -if (!skb_pull (skb,3 + *(u16 *)(skb->data +1 ) *2 )) +case BNEP_FILTER_NET_TYPE_SET : { +u8 *hdr; + +/* Pull ctrl type (1 b) + len (2 b) */ + hdr =skb_pull_data (skb,3 ); +if (!hdr) + goto badframe; +/* Pull data (len bytes); length is big-endian */ +if (!skb_pull (skb,get_unaligned_be16 (&hdr[1 ]))) goto badframe;break ; + }default :kfree_skb (skb);return 0 ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 235/452 Bluetooth: bnep: reject short frames before parsing
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zhang Cen <[email protected]>
[ Upstream commit 6770d3a8acdf9151769180cc3710346c4cfbe6f0 ]
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 (?:?)
--- net/bluetooth/bnep/core.c | 57 ++++++++++++++++++++++++--------------- 1 file changed, 36 insertions(+), 21 deletions(-)
net/bluetooth/bnep/core.c +36 -21
@@ -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 )) +case BNEP_SETUP_CONN_REQ : { +u8 uuid_size; + +/* Pull uuid_size and the dst/src service UUIDs. */ + data =skb_pull_data (skb,sizeof (uuid_size)); +if (!data) + goto badframe; + 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 : { -u8 *hdr; - -/* Pull ctrl type (1 b) + len (2 b) */ - hdr =skb_pull_data (skb,3 ); -if (!hdr) +case BNEP_FILTER_NET_TYPE_SET : +/* Pull: len (2 b), data (len bytes) */ + data =skb_pull_data (skb,sizeof (u16)); +if (!data) goto badframe; -/* Pull data (len bytes); length is big-endian */ -if (!skb_pull (skb,get_unaligned_be16 (&hdr[1 ]))) +if (!skb_pull (skb,get_unaligned_be16 (data))) goto badframe;break ; - }default :kfree_skb (skb);return 0 ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 236/452 Bluetooth: fix memory leak in error path of hci_alloc_dev()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bharath Reddy <[email protected]>
[ Upstream commit 37b3009bf5976e8ab77c8b9a9bc3bbd7ff49e37f ]
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.
--- net/bluetooth/hci_sysfs.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
net/bluetooth/hci_sysfs.c +4 -2
@@ -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 ); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 237/452 Bluetooth: MGMT: Fix backward compatibility with userspace
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Luiz Augusto von Dentz <[email protected]>
[ Upstream commit 149324fc762c2a7acef9c26790566f81f475e51f ]
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.
--- net/bluetooth/mgmt.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
net/bluetooth/mgmt.c +3 -2
@@ -9144,8 +9144,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 );
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 238/452 ipv4: restrict IPOPT_SSRR and IPOPT_LSRR options
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit d3915a1f5a4bc0ac911032903c3c6ab8df9fcc7c ]
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.
--- net/ipv4/ip_options.c | 4 ++++ 1 file changed, 4 insertions(+)
net/ipv4/ip_options.c +4 -0
@@ -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 ;
Signature · 1 line
2.53.0