7.2.8-rc1 review
Greg Kroah-Hartman
maintainerPATCH 125/438 ntfs: ignore interrupted inode reads as corruption
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Namjae Jeon <[email protected]>
[ Upstream commit 8c5dc7587fdd45f957af81a9adc1e16863f300fc ]
Do not mark the volume in error or report an inode as corrupt when reading it was interrupted by a signal. -EINTR and -ERESTARTSYS indicate a transient read failure rather than on-disk NTFS corruption.
--- fs/ntfs/inode.c | 21 +++++++++++++-------- fs/ntfs/mft.c | 3 ++- 2 files changed, 15 insertions(+), 9 deletions(-)
fs/ntfs/inode.c +13 -8
@@ -1232,7 +1232,8 @@ static int ntfs_read_locked_inode(struct inode *vi)if (m)unmap_mft_record (ni); err_out: -if (err != -EOPNOTSUPP && err != -ENOMEM && vol_err == true) { +if (err != -EOPNOTSUPP && err != -ENOMEM && + err != -EINTR && err != -ERESTARTSYS && vol_err == true) {ntfs_error (vol->sb,"Failed with error code %i. Marking corrupt inode 0x%llx as bad. Run chkdsk." , err, ni->mft_no);
@@ -1458,12 +1459,13 @@ static int ntfs_read_locked_attr_inode(struct inode *base_vi, struct inode *vi)ntfs_attr_put_search_ctx (ctx);unmap_mft_record (base_ni); err_out: -if (err != -ENOENT ) +if (err != -ENOENT && err != -EINTR && err != -ERESTARTSYS )ntfs_error (vol->sb,"Failed with error code %i while reading attribute inode (mft_no 0x%llx, type 0x%x, name_len %i). Marking corrupt inode and base inode 0x%llx as bad. Run chkdsk." , err, ni->mft_no, ni->type, ni->name_len, base_ni->mft_no); -if (err != -ENOENT && err != -ENOMEM ) +if (err != -ENOENT && err != -ENOMEM && + err != -EINTR && err != -ERESTARTSYS )NVolSetErrors (vol);return err; }
@@ -1667,8 +1669,9 @@ static int ntfs_read_locked_index_inode(struct inode *base_vi, struct inode *vi)/* Get the index bitmap attribute inode. */ bvi =ntfs_attr_iget (base_vi,AT_BITMAP , ni->name, ni->name_len);if (IS_ERR (bvi)) { -ntfs_error (vi->i_sb,"Failed to get bitmap attribute." ); err =PTR_ERR (bvi); +if (err != -EINTR && err != -ERESTARTSYS ) +ntfs_error (vi->i_sb,"Failed to get bitmap attribute." ); goto unm_err_out; } bni =NTFS_I (bvi);
@@ -1712,10 +1715,12 @@ static int ntfs_read_locked_index_inode(struct inode *base_vi, struct inode *vi)if (m)unmap_mft_record (base_ni); err_out: -ntfs_error (vi->i_sb, -"Failed with error code %i while reading index inode (mft_no 0x%llx, name_len %i." , - err, ni->mft_no, ni->name_len); -if (err != -EOPNOTSUPP && err != -ENOMEM ) +if (err != -EINTR && err != -ERESTARTSYS ) +ntfs_error (vi->i_sb, +"Failed with error code %i while reading index inode (mft_no 0x%llx, name_len %i." , + err, ni->mft_no, ni->name_len); +if (err != -EOPNOTSUPP && err != -ENOMEM && + err != -EINTR && err != -ERESTARTSYS )NVolSetErrors (vol);return err; }
fs/ntfs/mft.c +2 -1
@@ -213,7 +213,8 @@ struct mft_record *map_mft_record(struct ntfs_inode *ni)return m;atomic_dec (&ni->count); -ntfs_error (ni->vol->sb,"Failed with error code %lu." , -PTR_ERR (m)); +if (PTR_ERR (m) != -EINTR &&PTR_ERR (m) != -ERESTARTSYS ) +ntfs_error (ni->vol->sb,"Failed with error code %lu." , -PTR_ERR (m));return m; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 126/438 phy: mediatek: phy-mtk-hdmi-mt8195: Fix PLL calc divisor overflow
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: AngeloGioacchino Del Regno <[email protected]>
[ Upstream commit de7f29a1fe1dc2864d8a47f8c39d508442cae167 ]
When trying to calculate a PLL rate for target display resolutions above 2560x1440, 24bpp, 30Hz, the pixel clock value will be more than 32-bits long but the division to finally calculate the digital clock divider is being done with div_u64(), which expects a 32bit unsigned divisor.
Fix the overflow by using div64_u64() instead.
--- drivers/phy/mediatek/phy-mtk-hdmi-mt8195.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/phy/mediatek/phy-mtk-hdmi-mt8195.c +1 -1
@@ -290,7 +290,7 @@ static int mtk_hdmi_pll_calc(struct mtk_hdmi_phy *hdmi_phy, struct clk_hw *hw, posdiv2 =1 ;/* Digital clk divider, max /32 */ - digital_div =div_u64 (ns_hdmipll_ck, posdiv1 * posdiv2 * pixel_clk); + digital_div =div64_u64 (ns_hdmipll_ck, posdiv1 * posdiv2 * pixel_clk);if (!(digital_div <=32 && digital_div >=1 ))return -EINVAL ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 127/438 phy: mediatek: phy-mtk-hdmi-mt8195: Fix TMDS clk bit ratio setting
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: AngeloGioacchino Del Regno <[email protected]>
[ Upstream commit 486a70ef848264dcf9a57f0bb0452848db9537de ]
The comment in the mtk_phy_tmds_clk_ratio() function clearly and correctly explains that the TMDS ratio has to be 1/10 for data rates under 3.4Gbps, and 1/40 over that.
Unfortunately though, the TXC_DIV register setting was wrong, as in value 3 means to divide by 8 and, in order to achieve the in spec 1/40 (tmds) data rate, this has to divide by 4 instead!
Add definitions for the TXC_DIV register values clearly explaining the meanings (DIV2, DIV4, DIV8), and program the correct, DIV 4, value to the register in mtk_phy_tmds_clk_ratio().
This fixes out of spec clocking and, with this change, SoCs using the MT8195 class HDMI PHYs can now successfully be configured to output 3840x2160@60Hz over HDMI.
--- drivers/phy/mediatek/phy-mtk-hdmi-mt8195.c | 2 +- drivers/phy/mediatek/phy-mtk-hdmi-mt8195.h | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-)
drivers/phy/mediatek/phy-mtk-hdmi-mt8195.c +1 -1
@@ -36,7 +36,7 @@ mtk_phy_tmds_clk_ratio(struct mtk_hdmi_phy *hdmi_phy, bool enable)* clock bit ratio 1:40, under 3.4Gbps, clock bit ratio 1:10 */ if (enable) -mtk_phy_update_field (regs +HDMI20_CLK_CFG ,REG_TXC_DIV ,3 ); +mtk_phy_update_field (regs +HDMI20_CLK_CFG ,REG_TXC_DIV ,VAL_TXC_DIV4 );else mtk_phy_clear_bits (regs +HDMI20_CLK_CFG ,REG_TXC_DIV ); }
drivers/phy/mediatek/phy-mtk-hdmi-mt8195.h +3 -0
@@ -17,6 +17,9 @@#define HDMI20_CLK_CFG 0x70#define REG_TXC_DIV GENMASK(31, 30) +#define VAL_TXC_DIV2 1 +#define VAL_TXC_DIV4 2 +#define VAL_TXC_DIV8 3#define HDMI_1_CFG_0 0x00#define RG_HDMITX21_DRV_IBIAS_CLK GENMASK(10, 5)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 128/438 ALSA: pcm: set timer->private_data before registering the PCM timer
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Nguyen Ngoc Thang <[email protected]>
[ Upstream commit 1e713f9bb2ac583521f06b0eb4e22440b1e3d078 ]
snd_pcm_timer_init() calls snd_device_register() to link the new struct snd_timer into the global timer list while it still carries hw.c_resolution = snd_pcm_timer_resolution (and hw.start/hw.stop), and only afterwards sets timer->private_data = substream.
Once the timer is on the list under register_mutex, a concurrent reader can already reach it through the same mutex and invoke these callbacks. /proc/asound/timers does this via c_resolution(), and snd_timer_open()+snd_timer_start() reach start()/stop() the same way. All three dereference timer->private_data, which for this brief window is NULL, giving a NULL-pointer dereference:
substream = timer->private_data; return substream->runtime ? ... // substream is NULL
Move the private_data/private_free assignment before snd_device_register() so the timer is never visible on the list without its private_data set. On the snd_device_register() failure path, private_free() (snd_pcm_timer_free()) can now run, but it only does substream->timer = NULL, which is already NULL at that point since substream->timer is set to the new timer just once, after a successful registration -- so the failure path stays safe.
--- sound/core/pcm_timer.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-)
sound/core/pcm_timer.c +5 -2
@@ -111,12 +111,15 @@ void snd_pcm_timer_init(struct snd_pcm_substream *substream)snd_pcm_direction_name (substream->stream), tid.card, tid.device, tid.subdevice); timer->hw = snd_pcm_timer; +/* Set before registering: a concurrent reader can invoke our hw +* callbacks as soon as the timer is on the global list. +*/ + timer->private_data = substream; + timer->private_free = snd_pcm_timer_free;if (snd_device_register (timer->card, timer) <0 ) {snd_device_free (timer->card, timer);return ; } - timer->private_data = substream; - timer->private_free = snd_pcm_timer_free; substream->timer = timer; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 129/438 drm/msm/dp: skip PUSH_IDLE when the link was never enabled
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jesse Casco <[email protected]>
[ Upstream commit e249a6e2a130c08bb4d8b0a55cbe29754307e5c9 ]
msm_dp_display_atomic_enable() returns early when link training fails, leaving ->power_on false and the main link down. msm_dp_display_atomic_disable() nevertheless writes DP_STATE_CTRL_PUSH_IDLE and waits for an idle-pattern completion that cannot arrive, so every failed enable is followed by "PUSH_IDLE pattern timedout".
Every other step of the teardown is already gated on that flag:msm_dp_display_disable(), called from .atomic_post_disable(), returns early on !power_on. The PUSH_IDLE write is the only one that is not, so the controller's runtime-PM reference is then dropped without the link having been taken down.
On glymur (Snapdragon X2 Elite) the consequence is not a warning. The SoC does not survive it: TrustZone force-stops the SOCCP and ADSP remote processors and the machine resets silently about 50 ms later, with no oops and no panic. On an ASUS Zenbook A16 (UX3607OA), whose eDP panel does not currently train, this reproduces without any compositor or GPU involvement:
# eDP enable has already failed with "Failed link training (rc=-104)" echo 1 > /sys/class/graphics/fb0/blank
[535.645455] === marker ===
[535.694833] qcom_q6v5_pas d00000.remoteproc: fatal error received: \
sys_m_smsm.c:512:TZ force stop
[535.694875] remoteproc remoteproc0: crash detected in soccp: type fatal error
[535.728857] qcom_q6v5_pas 6800000.remoteproc: fatal error received: \
sys_m_smsm.c:783:err fatal notification received from TZ
<SoC reset>Gate the PUSH_IDLE write on ->power_on so the disable path is consistent with the rest of the teardown. With this applied the same sequence is harmless and the machine stays up; without it, it resets every time.
The unconditional write dates back to the original DP driver (c943b4948b58 ("drm/msm/dp: add displayPort driver support")), but the surrounding code has been restructured several times since, so no Fixes:
tag is offered.
Note that the eDP link-training failure that exposes this on the A16 is a separate problem in the glymur eDP PHY and is reported separately; this change is about not damaging the machine when training fails, for whatever reason.
Tested on ASUS Zenbook A16 (UX3607OA), Snapdragon X2 Elite Extreme, on linux-next next-20260803 and next-20260807. The machine has since been running next-20260807 with this patch as its daily driver.
Patchwork: https://patchwork.freedesktop.org/patch/745167/
--- drivers/gpu/drm/msm/dp/dp_display.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+)
drivers/gpu/drm/msm/dp/dp_display.c +14 -0
@@ -1412,6 +1412,20 @@ void msm_dp_bridge_atomic_disable(struct drm_bridge *drm_bridge, msm_dp_display = container_of(dp, struct msm_dp_display_private, msm_dp_display); +/* +* If .atomic_enable() bailed out - link training failure is the common +* case - the mainlink was never brought up and ->power_on stayed false. +* Driving the PUSH_IDLE pattern into a controller that was never +* enabled times out, and .atomic_post_disable() then drops the +* controller's runtime-PM reference without tearing the PHY back down, +* because msm_dp_display_disable() returns early on !power_on. On +* glymur (Snapdragon X2 Elite) that combination is answered by a +* TrustZone-level SOCCP/ADSP force-stop and a silent SoC reset. +* There is nothing to push idle, so leave it alone. +*/ +if (!dp->power_on) +return ; +msm_dp_ctrl_push_idle (msm_dp_display->ctrl); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 130/438 drm/msm/dp: fix link bandwidth check when wide bus is enabled
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: William Bright <[email protected]>
[ Upstream commit 58995b11dfb7dda095d23f22fa4dc79b923b5adf ]
msm_dp_display_mode_valid() halves the pixel clock when either YUV420 or wide bus is in use, then uses that halved value both for the controller pixel clock limit and for the DP link bandwidth check.
Only YUV420 halves the data crossing the link. Wide bus widens the internal DPU to DP interface to two pixels per clock, halving the controller clock. Every pixel is still transmitted, so the link bandwidth requirement remains.
As a result, modes needing up to twice the available link bandwidth pass validation. On the IMDT QCS8550 SBC (rev5 with CYPD6125), where DP runs over USB-C alt mode where only two lanes are available, 3840x2160@60 was accepted despite needing 9.6 Gbps against the 8.64 Gbps the link can carry.
Use a separate link pixel clock that is only halved for YUV420 for the bandwidth calculation, leaving the wide bus halving to apply solely to the controller pixel clock limit. With this, 4k@60 is correctly rejected and 4k@30 selected instead.
Patchwork: https://patchwork.freedesktop.org/patch/746145/
--- drivers/gpu/drm/msm/dp/dp_display.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-)
drivers/gpu/drm/msm/dp/dp_display.c +5 -2
@@ -698,6 +698,7 @@ enum drm_mode_status msm_dp_bridge_mode_valid(struct drm_bridge *bridge,u32 mode_rate_khz =0 , supported_rate_khz =0 , mode_bpp =0 ;struct msm_dp *dp;int mode_pclk_khz = mode->clock; +int link_pclk_khz;bool is_yuv_420; dp =to_dp_bridge (bridge)->msm_dp_display;
@@ -719,6 +720,8 @@ enum drm_mode_status msm_dp_bridge_mode_valid(struct drm_bridge *bridge,if (is_yuv_420 && !msm_dp_display->panel->vsc_sdp_supported)return MODE_NO_420 ; + link_pclk_khz = is_yuv_420 ? mode_pclk_khz /2 : mode_pclk_khz; +if (is_yuv_420 || msm_dp_display->wide_bus_supported) mode_pclk_khz /=2 ;
@@ -730,9 +733,9 @@ enum drm_mode_status msm_dp_bridge_mode_valid(struct drm_bridge *bridge, mode_bpp = default_bpp; mode_bpp =msm_dp_panel_get_mode_bpp (msm_dp_display->panel, - mode_bpp, mode_pclk_khz); + mode_bpp, link_pclk_khz); - mode_rate_khz = mode_pclk_khz * mode_bpp; + mode_rate_khz = link_pclk_khz * mode_bpp; supported_rate_khz = link_info->num_lanes * link_info->rate *8 ;if (mode_rate_khz > supported_rate_khz)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 131/438 ASoC: Rename snd_soc_dai_link_ch_map.ch_mask to cpu_ch_mask
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Richard Fitzgerald <[email protected]>
[ Upstream commit 4d855d747521505b54457c96bc73577bf74b2374 ]
Rename the ch_mask member of snd_soc_dai_link_ch_map to cpu_ch_mask, as that is what it is used for.
The CPU and codec channel masks are not necessarily the same, and are quite likely different. SoundWire and I2S/TDM both support assigning different sample slots to each codec, so for example channel 0 on each codec could map to different channels at the CPU. So it's quite normal that the channel mask at the CPU end is different for each codec, but the codec channel masks are the same for each codec.
Stable-dep-of: 6b382bdfe26a ("ASoC: soc-pcm: Apply snd_soc_dai_link_ch_map.codec_ch_mask to codec params")
--- include/sound/soc.h | 2 +- sound/soc/sdw_utils/soc_sdw_utils.c | 2 +- sound/soc/soc-pcm.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-)
include/sound/soc.h +1 -1
@@ -698,7 +698,7 @@ struct snd_soc_dai_link_component {struct snd_soc_dai_link_ch_map {unsigned int cpu;unsigned int codec; -unsigned int ch_mask; +unsigned int cpu_ch_mask; };struct snd_soc_dai_link {
sound/soc/sdw_utils/soc_sdw_utils.c +1 -1
@@ -1541,7 +1541,7 @@ int asoc_sdw_hw_params(struct snd_pcm_substream *substream,* ASoC will set the corresponding channel numbers for each cpu dai. */ for_each_link_ch_maps (rtd->dai_link, i, ch_maps) - ch_maps->ch_mask = ch_mask << (i * step); + ch_maps->cpu_ch_mask = ch_mask << (i * step);return 0 ; }
sound/soc/soc-pcm.c +1 -1
@@ -1264,7 +1264,7 @@ static int __soc_pcm_hw_params(struct snd_pcm_substream *substream,*/ for_each_rtd_ch_maps (rtd, j, ch_maps)if (ch_maps->cpu == i) - ch_mask |= ch_maps->ch_mask; + ch_mask |= ch_maps->cpu_ch_mask;/* fixup cpu channel number */ if (ch_mask)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 132/438 ASoC: Add codec_ch_mask to snd_soc_dai_link_ch_map
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Richard Fitzgerald <[email protected]>
[ Upstream commit 88b14c0d0bab5c0f3e7c641f274e3c70210c0e36 ]
Add a codec_ch_mask member to snd_soc_dai_link_ch_map.
The CPU and codec channel masks are not necessarily the same, and are quite likely different. SoundWire and I2S/TDM both support assigning different sample slots to each codec, so for example channel 0 on each codec could map to different channels at the CPU.
It is also possible for one TX channel to map to multiple RX channels. So it isn't _always_ safe to assume that the total number of set bits in the CPU ch_mask is the same as the total number of enabled channels on the codec.
For example consider this mapping on a capture stream:
CPU0 CODEC0 cpu_ch_mask = 0x03
CPU1 CODEC0 cpu_ch_mask = 0x03
This could be either four TX channels on the codec split across two receiving CPUs, or two TX channels on the codec duplicated to two CPUs.
Stable-dep-of: 6b382bdfe26a ("ASoC: soc-pcm: Apply snd_soc_dai_link_ch_map.codec_ch_mask to codec params")
--- include/sound/soc.h | 1 + 1 file changed, 1 insertion(+)
include/sound/soc.h +1 -0
@@ -699,6 +699,7 @@ struct snd_soc_dai_link_ch_map {unsigned int cpu;unsigned int codec;unsigned int cpu_ch_mask; +unsigned int codec_ch_mask; };struct snd_soc_dai_link {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 133/438 ASoC: soc-pcm: Apply snd_soc_dai_link_ch_map.codec_ch_mask to codec params
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Richard Fitzgerald <[email protected]>
[ Upstream commit 6b382bdfe26a2232091bf743e454e6794295783e ]
In __soc_pcm_hw_params() if there is a snd_soc_dai_link_ch_map with non-zero codec_ch_mask, use that channel mask to restrict which channels are enabled on the codec. But only if there isn't a TDM mask.
It is possible that a snd_soc_dai_link_ch_map could include the same codec multiple times on different CPUs so the for_each_rtd_ch_maps() loop accumulates the channel masks for all entries of that codec.
If a TDM mask was also set, it takes priority and is used instead of any possible snd_soc_dai_link_ch_map entries. (They cannot be ANDed together because the bit positions are indicating different things: TDM is a bit for each TDM slot, codec_ch_mask is a bit for each codec channel.)
This fixes a problem of incorrect TX channels enabled on the codec when multiple codecs are aggregated on a single capture link. For example:
- Two CPUs with six 4-channel codecs.
- The machine driver chooses to assign one channel from each codec to one channel on the CPU
- But the codec hw_params() would be passed a channel count of 6, which
(a) is more channels than the codec has and (b) allows enabling channels that should not be driving the audio bus.
--- sound/soc/soc-pcm.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-)
sound/soc/soc-pcm.c +12 -4
@@ -1206,7 +1206,9 @@ static int __soc_pcm_hw_params(struct snd_pcm_substream *substream, goto out;for_each_rtd_codec_dais (rtd, i, codec_dai) { -unsigned int tdm_mask =snd_soc_dai_tdm_mask_get (codec_dai, substream->stream); +unsigned int ch_mask =snd_soc_dai_tdm_mask_get (codec_dai, substream->stream); +struct snd_soc_dai_link_ch_map *ch_maps; +int j;/* * Skip CODECs which don't support the current stream type,
@@ -1228,9 +1230,15 @@ static int __soc_pcm_hw_params(struct snd_pcm_substream *substream,/* copy params for each codec */ tmp_params = *params; -/* fixup params based on TDM slot masks */ -if (tdm_mask) -soc_pcm_codec_params_fixup (&tmp_params, tdm_mask); +/* fixup params based on TDM or ch_map masks */ +if (!ch_mask) { +for_each_rtd_ch_maps (rtd, j, ch_maps) +if (ch_maps->codec == i) + ch_mask |= ch_maps->codec_ch_mask; + } + +if (ch_mask) +soc_pcm_codec_params_fixup (&tmp_params, ch_mask); ret =snd_soc_dai_hw_params (codec_dai, substream, &tmp_params);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 134/438 spi: spi-qpic-snand: avoid writing QPIC_EBI2_ECC_BUF_CFG register
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Gabor Juhos <[email protected]>
[ Upstream commit 930a7312c946bf4731721cadd82bb9a2ada496ca ]
The description of commit bfb34eced559 ("mtd: rawnand: qcom: avoid writing to obsolete register") says this:
"QPIC_EBI2_ECC_BUF_CFG register got obsolete from QPIC V2.0 onwards. Avoid writing this register if QPIC version is V2.0 or newer."
Although the referenced commit is related to the 'qcom-nandc' driver, however the hardware supported by the current driver is also based on QPIC v2.0 so we should avoid writing that register here as well.
Remove the register writing code to avoid undefined behaviour.
--- drivers/spi/spi-qpic-snand.c | 4 ---- 1 file changed, 4 deletions(-)
drivers/spi/spi-qpic-snand.c +0 -4
@@ -740,8 +740,6 @@ static int qcom_spi_read_cw_raw(struct qcom_nand_controller *snandc, u8 *data_buqcom_write_reg_dma (snandc, &snandc->regs->addr0,NAND_ADDR0 ,2 ,0 );qcom_write_reg_dma (snandc, &snandc->regs->cfg0,NAND_DEV0_CFG0 ,3 ,0 ); -qcom_write_reg_dma (snandc, &snandc->regs->ecc_buf_cfg,NAND_EBI2_ECC_BUF_CFG ,1 ,0 ); -qcom_write_reg_dma (snandc, &snandc->regs->erased_cw_detect_cfg_clr,NAND_ERASED_CW_DETECT_CFG ,1 ,0 ); qcom_write_reg_dma(snandc, &snandc->regs->erased_cw_detect_cfg_set,
@@ -1008,8 +1006,6 @@ static void qcom_spi_config_page_write(struct qcom_nand_controller *snandc) {qcom_write_reg_dma (snandc, &snandc->regs->addr0,NAND_ADDR0 ,2 ,0 );qcom_write_reg_dma (snandc, &snandc->regs->cfg0,NAND_DEV0_CFG0 ,3 ,0 ); -qcom_write_reg_dma (snandc, &snandc->regs->ecc_buf_cfg,NAND_EBI2_ECC_BUF_CFG , -1 ,NAND_BAM_NEXT_SGL ); }static void qcom_spi_config_cw_write (struct qcom_nand_controller *snandc)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 135/438 Input: trackpoint - fix the inertia attribute name in the ABI document
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Karl Mehltretter <[email protected]>
[ Upstream commit 45b0037899704caf9078be2be4de69361ca7d933 ]
The attribute is created as "inertia" (TRACKPOINT_INT_ATTR(inertia, ...) in drivers/input/mouse/trackpoint.c); the ABI file spells the path "intertia". The description below it already says inertia.
Fix the spelling.
--- Documentation/ABI/testing/sysfs-devices-platform-trackpoint | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
Documentation/ABI/testing/sysfs-devices-platform-trackpoint +1 -1
@@ -5,7 +5,7 @@ Contact: [email protected] Description: (RW) Trackpoint sensitivity. -What: /sys/devices/platform/i8042/.../intertia +What: /sys/devices/platform/i8042/.../inertia Date: Aug, 2005 KernelVersion: 2.6.14 Contact: [email protected]
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 136/438 objtool: Validate disassembler headers in libopcodes probe
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ulises Mendez Martinez <[email protected]>
[ Upstream commit 7e61560628d17ea6b1d8ee370f6d42694cff8758 ]
commit 3f2de814c059 ("objtool: Fix libopcodes linking with static libraries") tested for libopcodes availability by linking a test snippet with a forward declaration of disassemble_init_for_target().
However, testing symbol linkage with an extern declaration only verifies the presence of the library (.so/.a) and bypasses checking for development headers (binutils-dev). On systems where libopcodes is present without development headers installed, the probe succeeds, enabling BUILD_DISAS. Subsequent compilation of objtool then fails:
fatal error: 'bfd.h' file not found
113 | #include <bfd.h>
Additionally, the probe invokes $(HOSTCC) without $(HOSTCFLAGS), ignoring any sysroot or include flags specified for the host compiler.
Fix this by including <bfd.h> and <dis-asm.h> directly in the test snippet, passing $(HOSTCFLAGS) so host compiler options are respected, and defining PACKAGE="objtool" to satisfy the configuration check in <bfd.h>.
--- tools/objtool/Makefile | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-)
tools/objtool/Makefile +5 -3
@@ -89,9 +89,11 @@ LIBOPCODES_LIBS := $(shell \ "-lopcodes -lbfd" \ "-lopcodes -lbfd -liberty" \ "-lopcodes -lbfd -liberty -lz"; do \ - echo 'extern void disassemble_init_for_target(void *);' \ - 'int main(void) { disassemble_init_for_target(0); return 0; }' | \ - $(HOSTCC) -xc - -o /dev/null $$libs 2>/dev/null && \ + printf '%s\n' \ + '$(pound)include <bfd.h>' \ + '$(pound)include <dis-asm.h>' \ + 'int main(void) { disassemble_init_for_target(0); return 0; }' | \ + $(HOSTCC) $(HOSTCFLAGS) -DPACKAGE='"objtool"' -xc - -o /dev/null $$libs 2>/dev/null && \ echo "$$libs" && break; \ done)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 137/438 gpio: virtuser: skip free_irq when no IRQ is installed
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Runyu Xiao <[email protected]>
[ Upstream commit 50fd0ada8d37587223001600933270b59cb30e19 ]
Disabling interrupt monitoring uses atomic_xchg() to clear the stored IRQ. When monitoring is already disabled, atomic_xchg() returns 0. It must not be passed to free_irq().
The bug is reproducible on an x86_64 QEMU guest with CONFIG_GPIO_VIRTUSER=y and CONFIG_GPIO_SIM=y. Configure a live gpio-virtuser device through configfs. Its input lookup must refer to a live gpio-sim bank, such as key gpio-sim-test with offset 0. The consumer's dev_name attribute is shown as <dev> below; then run:
echo 0 > /sys/kernel/debug/gpio-virtuser/<dev>/gpiod:input:0/interrupts
On an unpatched kernel, this reaches gpio_virtuser_interrupts_set() with ld->irq still at its initial value 0, and free_irq() reports:
Trying to free already-free IRQ 0
The same reproducer completes without the warning on the patched kernel.
--- drivers/gpio/gpio-virtuser.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/gpio/gpio-virtuser.c +2 -1
@@ -692,7 +692,8 @@ static int gpio_virtuser_interrupts_set(void *data, u64 val)atomic_set (&ld->irq, irq); }else { irq =atomic_xchg (&ld->irq,0 ); -free_irq (irq, ld); +if (irq) +free_irq (irq, ld); }return 0 ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 138/438 ALSA: usb: 6fire: Avoid embedded URBs
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Takashi Iwai <[email protected]>
[ Upstream commit 9fe49dbc023e82dfaee7b245997d820d01742a9a ]
The USB 6fire driver uses URBs embedded in different structs for PCM, MIDI and communication, and this is basically a buggy implementation nowadays; since a URB is managed with a refcount, this may lead to a UAF when the URB is released asynchronously.
For addressing the problem, this patch converts those embedded URBs to ones that are properly allocated via usb_alloc_urb(). The pcm_urb.packets[] is gone, as it's allocated by usb_alloc_urb(), hence it's found in urb.iso_frame_desc[] instead.
The conversions are rather straightforward; each embedded struct urb is changed to a pointer, and its callers are updated accordingly. The resource for those structs are released in the common destructor functions (usb6fire_comm_free(), etc), which are called at both the init error path and the disconnect.
No functional changes, only compile-tested.
Stable-dep-of: 1589afe2d099 ("ALSA: 6fire: fix OOB write from device-reported iso length")
--- sound/usb/6fire/comm.c | 42 +++++++++----- sound/usb/6fire/comm.h | 2 +- sound/usb/6fire/midi.c | 43 +++++++++----- sound/usb/6fire/midi.h | 2 +- sound/usb/6fire/pcm.c | 128 ++++++++++++++++++++++++----------------- sound/usb/6fire/pcm.h | 5 +- 6 files changed, 136 insertions(+), 86 deletions(-)
sound/usb/6fire/comm.c +28 -14
@@ -21,7 +21,6 @@ enum {static void usb6fire_comm_init_urb (struct comm_runtime *rt,struct urb *urb,u8 *buffer,void *context,void (*handler)(struct urb *urb)) { -usb_init_urb (urb); urb->transfer_buffer = buffer; urb->pipe =usb_sndintpipe (rt->chip->dev,COMM_EP ); urb->complete = handler;
@@ -142,6 +141,19 @@ static int usb6fire_comm_write16(struct comm_runtime *rt, u8 request,return ret; } +static void usb6fire_comm_free (struct comm_runtime *rt) +{ +if (!rt) +return ; + +if (rt->chip) + rt->chip->comm =NULL ; + +usb_free_urb (rt->receiver); +kfree (rt->receiver_buffer); +kfree (rt); +} +int usb6fire_comm_init (struct sfire_chip *chip) { struct comm_runtime *rt = kzalloc_obj(struct comm_runtime);
@@ -153,14 +165,18 @@ int usb6fire_comm_init(struct sfire_chip *chip) rt->receiver_buffer =kzalloc (COMM_RECEIVER_BUFSIZE ,GFP_KERNEL );if (!rt->receiver_buffer) { -kfree (rt); -return -ENOMEM ; + ret = -ENOMEM ; + goto error; } - urb = &rt->receiver; + urb =usb_alloc_urb (0 ,GFP_KERNEL ); +if (!urb) { + ret = -ENOMEM ; + goto error; + } + rt->receiver = urb; rt->serial =1 ; rt->chip = chip; -usb_init_urb (urb); rt->init_urb = usb6fire_comm_init_urb; rt->write8 = usb6fire_comm_write8; rt->write16 = usb6fire_comm_write16;
@@ -175,13 +191,15 @@ int usb6fire_comm_init(struct sfire_chip *chip) urb->interval =1 ; ret =usb_submit_urb (urb,GFP_KERNEL );if (ret <0 ) { -kfree (rt->receiver_buffer); -kfree (rt);dev_err (&chip->dev->dev,"cannot create comm data receiver." ); -return ret; + goto error; } chip->comm = rt;return 0 ; + + error: +usb6fire_comm_free (rt); +return ret; }void usb6fire_comm_abort (struct sfire_chip *chip)
@@ -189,14 +207,10 @@ void usb6fire_comm_abort(struct sfire_chip *chip)struct comm_runtime *rt = chip->comm;if (rt) -usb_poison_urb (&rt->receiver); +usb_poison_urb (rt->receiver); }void usb6fire_comm_destroy (struct sfire_chip *chip) { -struct comm_runtime *rt = chip->comm; - -kfree (rt->receiver_buffer); -kfree (rt); - chip->comm =NULL ; +usb6fire_comm_free (chip->comm); }
sound/usb/6fire/comm.h +1 -1
@@ -19,7 +19,7 @@ enum /* settings for comm */struct comm_runtime {struct sfire_chip *chip; -struct urb receiver; +struct urb *receiver;u8 *receiver_buffer;u8 serial;/* urb serial */
sound/usb/6fire/midi.c +30 -13
86 lines not on this page · raw message
sound/usb/6fire/midi.h +1 -1
@@ -22,7 +22,7 @@ struct midi_runtime {spinlock_t in_lock;spinlock_t out_lock;struct snd_rawmidi_substream *out; -struct urb out_urb; +struct urb *out_urb;u8 out_serial;/* serial number of out packet */ u8 *out_buffer;int buffer_offset;
sound/usb/6fire/pcm.c +75 -53
267 lines not on this page · raw message
sound/usb/6fire/pcm.h +1 -4
@@ -24,10 +24,7 @@ enum /* settings for pcm */struct pcm_urb {struct sfire_chip *chip; -/* BEGIN DO NOT SEPARATE */ -struct urb instance; -struct usb_iso_packet_descriptor packets[PCM_N_PACKETS_PER_URB ]; -/* END DO NOT SEPARATE */ +struct urb *instance;u8 *buffer;struct pcm_urb *peer;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 139/438 ALSA: 6fire: fix OOB write from device-reported iso length
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xiang Mei <[email protected]>
[ Upstream commit 1589afe2d099d3e817873bc474676968d7080410 ]
usb6fire_pcm_in_urb_handler() sizes each outgoing isochronous packet as (actual_length - 4) / (in_n_analog << 2) * (out_n_analog << 2) + 4, where actual_length is the unsigned length the device reported for the matching IN packet. A packet completed with status 0 and actual_length < 4 wraps the subtraction to 0x7fffffec; a zero-length isochronous packet is legal on the bus, and the preceding loop rejects only non-zero status. The sum reaches memset() on out_urb->buffer, a 4832-byte object from kcalloc(PCM_MAX_PACKET_SIZE, PCM_N_PACKETS_PER_URB).
Even without the wrap the result is out of bounds: at 88.2/96 kHz the 4-in/6-out scaling turns a full 420-byte IN packet into 628, so eight packets span 5024 bytes of that buffer. usb_submit_urb() rejects an over-long descriptor only after the memset() and the usb6fire_pcm_playback() copy of user PCM data have run.
Guard the subtraction as the sibling usb6fire_pcm_capture() already does, and limit the frame count to what fits in rt->out_packet_size, the OUT endpoint's wMaxPacketSize. This bounds total_length by the buffer size while keeping each packet length aligned to a whole output frame.
BUG: KASAN: out-of-bounds in usb6fire_pcm_in_urb_handler (sound/usb/6fire/pcm.c:338) Write of size 18446744073709551456 at addr ffff88802a3d0000 by task vhci_rx/5018 Call Trace: dump_stack_lvl (lib/dump_stack.c:94 lib/dump_stack.c:120) print_report (mm/kasan/report.c:378 mm/kasan/report.c:482) kasan_report (mm/kasan/report.c:595) kasan_check_range (mm/kasan/generic.c:186 mm/kasan/generic.c:200) __asan_memset (mm/kasan/shadow.c:84) usb6fire_pcm_in_urb_handler (sound/usb/6fire/pcm.c:338) __usb_hcd_giveback_urb (drivers/usb/core/hcd.c:1657) usb_hcd_giveback_urb (drivers/usb/core/hcd.c:1741) vhci_rx_loop (drivers/usb/usbip/vhci_rx.c:107 drivers/usb/usbip/vhci_rx.c:242) 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) Allocated by task 10: __kmalloc_cache_noprof (mm/slub.c:5563) usb6fire_pcm_init (sound/usb/6fire/pcm.c:560 sound/usb/6fire/pcm.c:595) usb6fire_chip_probe (sound/usb/6fire/chip.c:133) usb_probe_interface (drivers/usb/core/driver.c:399) The buggy address belongs to the object at ffff88802a3d0000 which belongs to the cache kmalloc-8k of size 8192 The buggy address is located 0 bytes inside of 4832-byte region [ffff88802a3d0000, ffff88802a3d12e0) Kernel panic - not syncing: Fatal exception in interrupt
--- sound/usb/6fire/pcm.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-)
sound/usb/6fire/pcm.c +10 -2
@@ -335,11 +335,19 @@ static void usb6fire_pcm_in_urb_handler(struct urb *usb_urb)/* setup out urb structure */ for (i =0 ; i <PCM_N_PACKETS_PER_URB ; i++) { +unsigned int frames =0 ; + isoc_out = &out_urb->instance->iso_frame_desc[i]; isoc_in = &in_urb->instance->iso_frame_desc[i]; +if (isoc_in->actual_length >4 ) + frames = (isoc_in->actual_length -4 ) + / (rt->in_n_analog <<2 ); + frames = min_t(unsigned int, frames, + (rt->out_packet_size -4 ) + / (rt->out_n_analog <<2 )); + isoc_out->offset = total_length; - isoc_out->length = (isoc_in->actual_length -4 ) / (rt->in_n_analog <<2 ) - * (rt->out_n_analog <<2 ) +4 ; + isoc_out->length = frames * (rt->out_n_analog <<2 ) +4 ; isoc_out->status =0 ; total_length += isoc_out->length; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 140/438 ksmbd: fix malformed procfs status output
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Namjae Jeon <[email protected]>
[ Upstream commit 1f7dd03a88a8405143aab195f6fa9ed2243494b1 ]
The ksmbd procfs monitoring files produce misleading or malformed output. The constant-name helper uses a bitwise test for enum values. This omits zero-valued constants and can print multiple names for one lease state. It also unconditionally emits a newline, splitting entries in the open-file table across two lines. Session capabilities are printed as numeric flag values even though a table of descriptive names is available.
Use exact matching for enum values. Print flag names as a comma-separated list, preserving unknown bits as hexadecimal values. Let callers control line termination so each open-file entry remains on one line. Print common session properties once, and report signing and encryption independently.
Adjust client and open-file column widths for IPv6 addresses and 64-bit file IDs, and fix the misspelled OPLOCK_EXCLUSIVE name. Also expose and maintain the total request count alongside the per-command counters.
Stable-dep-of: d7fd1f98607f ("ksmbd: follow SMB2 session expiration semantics")
--- fs/smb/server/connection.c | 12 ++--- fs/smb/server/mgmt/user_session.c | 75 +++++++++++++------------------ fs/smb/server/misc.h | 7 +-- fs/smb/server/proc.c | 38 ++++++++++++++++ fs/smb/server/stats.h | 4 +- fs/smb/server/vfs_cache.c | 16 ++++--- 6 files changed, 89 insertions(+), 63 deletions(-)
fs/smb/server/connection.c +6 -6
@@ -35,9 +35,9 @@ static int proc_show_clients(struct seq_file *m, void *v)struct timespec64 now, t;int i; -seq_printf (m,"#%-20s %-10s %-10s %-10s %-10s %-10s\n" , -"<name>" ,"<dialect>" ,"<credits>" ,"<open files>" , -"<requests>" ,"<last active>" ); +seq_printf (m,"#%-40s %-10s %-10s %-12s %-10s %s\n" , +"<client>" ,"<dialect>" ,"<credits>" ,"<open files>" , +"<requests>" ,"<last active>" );down_read (&conn_list_lock);hash_for_each (conn_list, i, conn, hlist) {
@@ -46,11 +46,11 @@ static int proc_show_clients(struct seq_file *m, void *v) t =timespec64_sub (now, t);#if IS_ENABLED (CONFIG_IPV6 )if (!conn->inet_addr) -seq_printf (m,"%-20pI6c" , &conn->inet6_addr); +seq_printf (m," %-40pI6c" , &conn->inet6_addr); else#endif -seq_printf (m,"%-20pI4" , &conn->inet_addr); - seq_printf(m, " 0x%-10x %-10u %-12d %-10d %ptT\n", +seq_printf (m," %-40pI4" , &conn->inet_addr); + seq_printf(m, " 0x%-8x %-10u %-12d %-10d %ptT\n", conn->dialect, conn->total_credits, atomic_read(&conn->stats.open_files_count),
fs/smb/server/mgmt/user_session.c +30 -45
103 lines not on this page · raw message
fs/smb/server/misc.h +2 -5
@@ -53,11 +53,8 @@ void ksmbd_proc_show_flag_names(struct seq_file *m,const struct ksmbd_const_name *table, int count, unsigned int flags); -void ksmbd_proc_show_const_name (struct seq_file *m, -const char *format, -const struct ksmbd_const_name *table, -int count, -unsigned int const_value); +const char *ksmbd_proc_const_name (const struct ksmbd_const_name *table, +int count,unsigned int const_value);#else static inline int ksmbd_proc_init (void ) {return 0 ; }static inline void ksmbd_proc_cleanup (void ) {}
fs/smb/server/proc.c +38 -0
@@ -27,6 +27,42 @@ struct proc_dir_entry *ksmbd_proc_create(const char *name, show, v); } +void ksmbd_proc_show_flag_names (struct seq_file *m, +const struct ksmbd_const_name *table, +int count,unsigned int flags) +{ +unsigned int remaining = flags; +bool separator = false; +int i; + +for (i =0 ; i < count; i++) { +unsigned int flag = table[i].const_value; + +if (!flag || (remaining & flag) != flag) +continue ; +seq_printf (m,"%s%s" , separator ?"," :"" , table[i].name); + separator = true; + remaining &= ~flag; + } + +if (remaining) +seq_printf (m,"%s0x%08x" , separator ?"," :"" , remaining); +else if (!separator) +seq_puts (m,"none" ); +} + +const char *ksmbd_proc_const_name (const struct ksmbd_const_name *table, +int count,unsigned int const_value) +{ +int i; + +for (i =0 ; i < count; i++) { +if (table[i].const_value == const_value) +return table[i].name; + } +return NULL ; +} +struct ksmbd_const_smb2_process_req {unsigned int const_value;const char *name;
@@ -71,6 +107,8 @@ static int proc_show_ksmbd_stats(struct seq_file *m, void *v) ksmbd_counter_sum(KSMBD_COUNTER_SESSIONS));seq_printf (m,"tree connects: %lld\n" ,ksmbd_counter_sum (KSMBD_COUNTER_TREE_CONNS )); +seq_printf (m,"requests: %lld\n" , +ksmbd_counter_sum (KSMBD_COUNTER_REQUESTS ));seq_printf (m,"read bytes: %lld\n" ,ksmbd_counter_sum (KSMBD_COUNTER_READ_BYTES )); seq_printf(m, "written bytes: %lld\n",
fs/smb/server/stats.h +3 -1
@@ -52,8 +52,10 @@ static inline void ksmbd_counter_sub(int type, s64 value)static inline void ksmbd_counter_inc_reqs (unsigned int cmd) { -if (cmd <KSMBD_COUNTER_MAX_REQS ) +if (cmd <KSMBD_COUNTER_MAX_REQS ) { +percpu_counter_inc (&ksmbd_counters.counters[KSMBD_COUNTER_REQUESTS ]);percpu_counter_inc (&ksmbd_counters.counters[KSMBD_COUNTER_FIRST_REQ + cmd]); + } }static inline s64 ksmbd_counter_sum (int type)
fs/smb/server/vfs_cache.c +10 -6
@@ -66,7 +66,7 @@ static const struct ksmbd_const_name ksmbd_lease_const_names[] = {static const struct ksmbd_const_name ksmbd_oplock_const_names[] = { {SMB2_OPLOCK_LEVEL_NONE ,"OPLOCK_NONE" }, {SMB2_OPLOCK_LEVEL_II ,"OPLOCK_II" }, - {SMB2_OPLOCK_LEVEL_EXCLUSIVE ,"OPLOCK_EXECL" }, + {SMB2_OPLOCK_LEVEL_EXCLUSIVE ,"OPLOCK_EXCLUSIVE" }, {SMB2_OPLOCK_LEVEL_BATCH ,"OPLOCK_BATCH" }, };
@@ -76,14 +76,14 @@ static int proc_show_files(struct seq_file *m, void *v)unsigned int id;struct oplock_info *opinfo; -seq_printf (m,"#%-10s %-10s %-10s %-10s %-15s %-10s %-10s %s\n" , +seq_printf (m,"#%-10s %-18s %-18s %-10s %-16s %-10s %-10s %s\n" ,"<tree id>" ,"<pid>" ,"<vid>" ,"<refcnt>" ,"<oplock>" ,"<daccess>" ,"<saccess>" ,"<name>" );read_lock (&global_ft.lock);idr_for_each_entry (global_ft.idr, fp, id) { - seq_printf(m, "%#-10x %#-10llx %#-10llx %#-10x", + seq_printf(m, " %#-10x %#-18llx %#-18llx %#-10x", fp->tcon ? fp->tcon->id : 0, fp->persistent_id, fp->volatile_id,
@@ -93,6 +93,7 @@ static int proc_show_files(struct seq_file *m, void *v) opinfo =rcu_dereference (fp->f_opinfo);if (opinfo) {const struct ksmbd_const_name *const_names; +const char *name;int count;unsigned int level;
@@ -106,11 +107,14 @@ static int proc_show_files(struct seq_file *m, void *v) level = opinfo->level; }rcu_read_unlock (); -ksmbd_proc_show_const_name (m," %-15s" , - const_names, count, level); + name =ksmbd_proc_const_name (const_names, count, level); +if (name) +seq_printf (m," %-16s" , name); +else +seq_printf (m," 0x%-14x" , level); }else {rcu_read_unlock (); -seq_printf (m," %-15s" ," " ); +seq_printf (m," %-16s" ," " ); } seq_printf(m, " %#010x %#010x %s\n",
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 141/438 ksmbd: extend procfs server statistics
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Namjae Jeon <[email protected]>
[ Upstream commit 4c670ccd5790816fc0f5714d5ee3c67dd5a9c67a ]
The server proc entry does not expose configured limits or enough outcome data to distinguish protocol errors from transport stalls.
Report the server state, listener and signing configuration, connection limits, timeout values, current client and open-file totals, IPC activity, and durable scavenger state. Classify processed SMB2 response statuses by NTSTATUS severity and provide counters for common error groups while retaining the per-command counters.
Stable-dep-of: d7fd1f98607f ("ksmbd: follow SMB2 session expiration semantics")
--- fs/smb/server/proc.c | 121 +++++++++++++++++++++++++++++++++---- fs/smb/server/server.c | 7 ++- fs/smb/server/smb_common.h | 2 +- fs/smb/server/stats.h | 53 +++++++++++++++- fs/smb/server/vfs_cache.c | 10 +++ fs/smb/server/vfs_cache.h | 1 + 6 files changed, 176 insertions(+), 18 deletions(-)
fs/smb/server/proc.c +108 -13
@@ -11,10 +11,12 @@#include <linux/seq_file.h> #include "misc.h" +#include "connection.h" #include "server.h" #include "stats.h" #include "smb_common.h" #include "smb2pdu.h" +#include "vfs_cache.h" static struct proc_dir_entry *ksmbd_proc_fs;struct ksmbd_counters ksmbd_counters;
@@ -90,34 +92,127 @@ static const struct ksmbd_const_smb2_process_req smb2_process_req[KSMBD_COUNTER_ {le16_to_cpu(SMB2_OPLOCK_BREAK), "SMB2_OPLOCK_BREAK"}, }; +static const char *ksmbd_server_state_string (void ) +{ +switch (READ_ONCE (server_conf.state)) { +case SERVER_STATE_STARTING_UP : +return "starting" ; +case SERVER_STATE_RUNNING : +return "running" ; +case SERVER_STATE_RESETTING : +return "resetting" ; +case SERVER_STATE_SHUTTING_DOWN : +return "shutdown" ; +default : +return "unknown" ; + } +} + +static const char *ksmbd_signing_mode_string (void ) +{ +switch (server_conf.signing) { +case KSMBD_CONFIG_OPT_DISABLED : +return "disabled" ; +case KSMBD_CONFIG_OPT_MANDATORY : +return "mandatory" ; +case KSMBD_CONFIG_OPT_AUTO : +return "auto" ; +default : +return "unknown" ; + } +} + +static void proc_show_runtime_totals (struct seq_file *m) +{ +struct ksmbd_conn *conn; +unsigned int clients =0 ; +unsigned int open_files =0 ; +int i; + +down_read (&conn_list_lock); +hash_for_each (conn_list, i, conn, hlist) { + clients++; + open_files +=atomic_read (&conn->stats.open_files_count); + } +up_read (&conn_list_lock); + +seq_printf (m,"clients:\t%u\n" , clients); +seq_printf (m,"open_files:\t%u\n" , open_files); +} +static int proc_show_ksmbd_stats (struct seq_file *m,void *v) {int i;seq_puts (m,"Server\n" ); -seq_printf (m,"name: %s\n" ,ksmbd_server_string ()); -seq_printf (m,"netbios: %s\n" ,ksmbd_netbios_name ()); -seq_printf (m,"work group: %s\n" ,ksmbd_work_group ()); -seq_printf (m,"min protocol: %s\n" ,ksmbd_get_protocol_string (server_conf.min_protocol)); -seq_printf (m,"max protocol: %s\n" ,ksmbd_get_protocol_string (server_conf.max_protocol)); -seq_printf (m,"flags: 0x%08x\n" , server_conf.flags); -seq_printf (m,"share_fake_fscaps: 0x%08x\n" , +seq_printf (m,"state:\t%s\n" ,ksmbd_server_state_string ()); +seq_printf (m,"name:\t%s\n" ,ksmbd_server_string ()); +seq_printf (m,"netbios:\t%s\n" ,ksmbd_netbios_name ()); +seq_printf (m,"work_group:\t%s\n" ,ksmbd_work_group ()); +seq_printf (m,"min_protocol:\t%s\n" ,ksmbd_get_protocol_string (server_conf.min_protocol)); +seq_printf (m,"max_protocol:\t%s\n" ,ksmbd_get_protocol_string (server_conf.max_protocol)); +seq_printf (m,"flags:\t0x%08x\n" , server_conf.flags); +seq_printf (m,"tcp_port:\t%u\n" , server_conf.tcp_port); +seq_printf (m,"signing:\t%s\n" ,ksmbd_signing_mode_string ()); +seq_printf (m,"signing_enforced:\t%s\n" , + server_conf.enforced_signing ?"yes" :"no" ); +seq_printf (m,"bind_interfaces_only:\t%s\n" , + server_conf.bind_interfaces_only ?"yes" :"no" ); +seq_printf (m,"max_connections:\t%u\n" , server_conf.max_connections); +seq_printf (m,"max_connections_per_ip:\t%u\n" , + server_conf.max_ip_connections); +seq_printf (m,"max_inflight_requests:\t%u\n" , + server_conf.max_inflight_req); +seq_printf (m,"deadtime_seconds:\t%lu\n" , server_conf.deadtime /HZ ); +seq_printf (m,"ipc_timeout_seconds:\t%u\n" , server_conf.ipc_timeout /HZ ); +if (server_conf.ipc_last_active) +seq_printf (m,"ipc_last_active_seconds:\t%lu\n" , +jiffies_to_msecs (jiffies - server_conf.ipc_last_active) / +MSEC_PER_SEC ); +else +seq_puts (m,"ipc_last_active_seconds:\tnever\n" ); +seq_printf (m,"durable_scavenger:\t%s\n" , +ksmbd_durable_scavenger_active () ?"running" :"stopped" ); +seq_printf (m,"share_fake_fscaps:\t0x%08x\n" , server_conf.share_fake_fscaps); -seq_printf (m,"sessions: %lld\n" , +proc_show_runtime_totals (m); +seq_printf (m,"sessions:\t%lld\n" ,ksmbd_counter_sum (KSMBD_COUNTER_SESSIONS )); -seq_printf (m,"tree connects: %lld\n" , +seq_printf (m,"tree_connects:\t%lld\n" ,ksmbd_counter_sum (KSMBD_COUNTER_TREE_CONNS )); -seq_printf (m,"requests: %lld\n" , +seq_printf (m,"requests:\t%lld\n" ,ksmbd_counter_sum (KSMBD_COUNTER_REQUESTS )); -seq_printf (m,"read bytes: %lld\n" , +seq_printf (m,"read_bytes:\t%lld\n" ,ksmbd_counter_sum (KSMBD_COUNTER_READ_BYTES )); -seq_printf (m,"written bytes: %lld\n" , +seq_printf (m,"written_bytes:\t%lld\n" ,ksmbd_counter_sum (KSMBD_COUNTER_WRITE_BYTES ));seq_puts (m,"\nSMB2\n" );for (i =0 ; i <KSMBD_COUNTER_MAX_REQS ; i++) -seq_printf (m,"%-20s:\t%lld\n" , smb2_process_req[i].name, +seq_printf (m,"%s:\t%lld\n" , smb2_process_req[i].name,ksmbd_counter_sum (KSMBD_COUNTER_FIRST_REQ + i)); + +seq_puts (m,"\nSMB2 status\n" ); +seq_printf (m,"success:\t%lld\n" , +ksmbd_counter_sum (KSMBD_COUNTER_STATUS_SUCCESS )); +seq_printf (m,"informational:\t%lld\n" , +ksmbd_counter_sum (KSMBD_COUNTER_STATUS_INFORMATIONAL )); +seq_printf (m,"warning:\t%lld\n" , +ksmbd_counter_sum (KSMBD_COUNTER_STATUS_WARNING )); +seq_printf (m,"error:\t%lld\n" , +ksmbd_counter_sum (KSMBD_COUNTER_STATUS_ERROR )); +seq_printf (m,"access_denied:\t%lld\n" , +ksmbd_counter_sum (KSMBD_COUNTER_ERROR_ACCESS_DENIED )); +seq_printf (m,"not_found:\t%lld\n" , +ksmbd_counter_sum (KSMBD_COUNTER_ERROR_NOT_FOUND )); +seq_printf (m,"invalid_parameter:\t%lld\n" , +ksmbd_counter_sum (KSMBD_COUNTER_ERROR_INVALID_PARAMETER )); +seq_printf (m,"sharing_violation:\t%lld\n" , +ksmbd_counter_sum (KSMBD_COUNTER_ERROR_SHARING_VIOLATION )); +seq_printf (m,"not_supported:\t%lld\n" , +ksmbd_counter_sum (KSMBD_COUNTER_ERROR_NOT_SUPPORTED )); +seq_printf (m,"other:\t%lld\n" , +ksmbd_counter_sum (KSMBD_COUNTER_ERROR_OTHER ));return 0 ; }
fs/smb/server/server.c +5 -2
@@ -156,8 +156,11 @@ static int __process_request(struct ksmbd_work *work, struct ksmbd_conn *conn, } ret = cmds->proc (work); -if (conn->ops->inc_reqs) - conn->ops->inc_reqs (command); +if (conn->ops->inc_reqs) { +struct smb2_hdr *rsp =ksmbd_resp_buf_curr (work); + + conn->ops->inc_reqs (command, rsp->Status); + }if (ret <0 )ksmbd_debug (CONN ,"Failed to process %u [%d]\n" , command, ret);
fs/smb/server/smb_common.h +1 -1
@@ -135,7 +135,7 @@ struct file_id_both_directory_info {struct smb_version_ops {u16 (*get_cmd_val)(struct ksmbd_work *swork); -void (*inc_reqs)(unsigned int cmd); +void (*inc_reqs)(unsigned int cmd,__le32 status);int (*init_rsp_hdr)(struct ksmbd_work *swork);void (*set_rsp_status)(struct ksmbd_work *swork,__le32 err);int (*allocate_rsp_buf)(struct ksmbd_work *work);
fs/smb/server/stats.h +51 -2
78 lines not on this page · raw message
fs/smb/server/vfs_cache.c +10 -0
@@ -140,6 +140,16 @@ static bool durable_scavenger_running;static DEFINE_MUTEX (durable_scavenger_lock);static wait_queue_head_t dh_wq; +bool ksmbd_durable_scavenger_active (void ) +{ +bool active; + +mutex_lock (&durable_scavenger_lock); + active = durable_scavenger_running; +mutex_unlock (&durable_scavenger_lock); +return active; +} +void ksmbd_set_fd_limit (unsigned long limit) { limit =min (limit,get_max_files ());
fs/smb/server/vfs_cache.h +1 -0
@@ -209,6 +209,7 @@ unsigned int ksmbd_open_durable_fd(struct ksmbd_file *fp);struct ksmbd_file *ksmbd_open_fd (struct ksmbd_work *work,struct file *filp);void ksmbd_launch_ksmbd_durable_scavenger (void );void ksmbd_stop_durable_scavenger (void ); +bool ksmbd_durable_scavenger_active (void );void ksmbd_close_tree_conn_fds (struct ksmbd_work *work);void ksmbd_close_session_fds (struct ksmbd_work *work);void ksmbd_wake_session_blocked_works (struct ksmbd_session *sess);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 142/438 ksmbd: follow SMB2 session expiration semantics
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Namjae Jeon <[email protected]>
[ Upstream commit d7fd1f98607f2cd358e583f9548bdf0173090a86 ]
ksmbd_session_register() destroys valid sessions after ten seconds of inactivity whenever a client starts another SessionSetup exchange. This confuses Session.IdleTime with Session.ExpirationTime. Windows can create additional authenticated sessions on an existing connection, so deleting the older session invalidates its tree connects and makes mapped drives fail with STATUS_NETWORK_NAME_DELETED.
The session expiration rules require the server to change a valid session to expired only after its credential expiration time passes. A valid or expired session otherwise keeps its connection from being scavenged. Connections that have not negotiated a dialect, have no sessions, or have only InProgress sessions are disconnected after an implementation-specific timeout. Use the Windows-compatible 45 second value and run the expiration check periodically for both TCP and SMB Direct.
Keep zero as an infinite credential expiration time, and count each Valid-to-Expired transition. Set expired sessions to InProgress when they reauthenticate. If authentication fails, remove the session from the global and per-connection tables immediately, including SMB3 multichannel connections.
Retain protection against abandoned SessionId-zero exchanges by allowing only one InProgress authentication per connection. Additional exchanges fail with STATUS_INSUFFICIENT_RESOURCES, and stale InProgress sessions are reaped after the same 45 second setup timeout. This bounds the original unauthenticated memory-exhaustion path without evicting established sessions.
--- fs/smb/server/connection.c | 72 +++++++++++++++++ fs/smb/server/connection.h | 3 + fs/smb/server/mgmt/user_session.c | 124 ++++++++++++++++++++++++++++-- fs/smb/server/mgmt/user_session.h | 8 +- fs/smb/server/proc.c | 2 + fs/smb/server/server.c | 2 +- fs/smb/server/smb2pdu.c | 37 ++++++--- fs/smb/server/smb2pdu.h | 2 - fs/smb/server/stats.h | 1 + 9 files changed, 227 insertions(+), 24 deletions(-)
fs/smb/server/connection.c +72 -0
@@ -22,6 +22,8 @@static DEFINE_MUTEX (init_lock);static struct ksmbd_conn_ops default_conn_ops; +static struct delayed_work session_expiration_work; +static bool stopping_session_expiration_work;DEFINE_HASHTABLE (conn_list,CONN_HASH_BITS );DECLARE_RWSEM (conn_list_lock);
@@ -84,18 +86,28 @@ static void delete_proc_clients(void) {}static struct workqueue_struct *ksmbd_conn_wq; +static void ksmbd_session_expiration_worker (struct work_struct *work); +int ksmbd_conn_wq_init (void ) { ksmbd_conn_wq =alloc_workqueue ("ksmbd-conn-release" ,WQ_UNBOUND |WQ_MEM_RECLAIM ,0 );if (!ksmbd_conn_wq)return -ENOMEM ; + +WRITE_ONCE (stopping_session_expiration_work, false); +INIT_DELAYED_WORK (&session_expiration_work, + ksmbd_session_expiration_worker); +queue_delayed_work (ksmbd_conn_wq, &session_expiration_work, +KSMBD_SESSION_EXPIRATION_INTERVAL );return 0 ; }void ksmbd_conn_wq_destroy (void ) {if (ksmbd_conn_wq) { +WRITE_ONCE (stopping_session_expiration_work, true); +cancel_delayed_work_sync (&session_expiration_work);destroy_workqueue (ksmbd_conn_wq); ksmbd_conn_wq =NULL ; }
@@ -205,6 +217,7 @@ struct ksmbd_conn *ksmbd_conn_alloc(void)return NULL ; conn->need_neg = true; + conn->creation_time = jiffies;ksmbd_conn_set_new (conn); conn->local_nls =load_nls ("utf8" );if (!conn->local_nls)
@@ -487,6 +500,20 @@ bool ksmbd_conn_alive(struct ksmbd_conn *conn)if (kthread_should_stop ())return false; +/* +* Stale connections that have not completed NEGOTIATE and SESSION_SETUP +* must be disconnected. Do not race a request that is currently +* completing authentication. +*/ +if (!atomic_read (&conn->req_running) && +time_after (jiffies, conn->creation_time + +KSMBD_UNAUTHENTICATED_CONN_TIMEOUT ) && + (READ_ONCE (conn->need_neg) || + !ksmbd_conn_has_valid_or_expired_session (conn))) { +ksmbd_debug (CONN ,"Connection setup timed out\n" ); +return false; + } +if (atomic_read (&conn->stats.open_files_count) >0 )return true;
@@ -504,6 +531,51 @@ bool ksmbd_conn_alive(struct ksmbd_conn *conn)return true; } +static void ksmbd_session_expiration_worker (struct work_struct *work) +{ +struct ksmbd_conn *conn, *target; +int bkt; + +if (!ksmbd_server_running ()) + goto reschedule; + +ksmbd_expire_sessions (); + +/* +* An old connection without a Valid or Expired session must be +* disconnected. Process one connection at a time without holding +* conn_list_lock across transport shutdown. +*/ +again: + target =NULL ; +down_read (&conn_list_lock); +hash_for_each (conn_list, bkt, conn, hlist) { +if (ksmbd_conn_exiting (conn) ||ksmbd_conn_releasing (conn) || +atomic_read (&conn->req_running) || +time_before_eq (jiffies, conn->creation_time + +KSMBD_UNAUTHENTICATED_CONN_TIMEOUT ) || + (!READ_ONCE (conn->need_neg) && +ksmbd_conn_has_valid_or_expired_session (conn))) +continue ; + + target =ksmbd_conn_get (conn); +break ; + } +up_read (&conn_list_lock); + +if (target) { +ksmbd_debug (CONN ,"Connection setup timed out\n" ); +ksmbd_conn_abort (target); +ksmbd_conn_put (target); + goto again; + } + +reschedule: +if (!READ_ONCE (stopping_session_expiration_work)) +queue_delayed_work (ksmbd_conn_wq, &session_expiration_work, +KSMBD_SESSION_EXPIRATION_INTERVAL ); +} +/* "+2" for BCC field (ByteCount, 2 bytes) */ #define SMB1_MIN_SUPPORTED_PDU_SIZE (sizeof(struct smb_hdr) + 2)#define SMB2_MIN_SUPPORTED_PDU_SIZE (sizeof(struct smb2_pdu))
fs/smb/server/connection.h +3 -0
@@ -67,6 +67,7 @@ struct ksmbd_conn {struct rw_semaphore session_lock;/* smb session 1 per user */ struct xarray sessions; +unsigned long creation_time;unsigned long last_active;/* How many request are running currently */ atomic_t req_running;
@@ -160,6 +161,8 @@ struct ksmbd_transport {#define KSMBD_TCP_RECV_TIMEOUT (7 * HZ)#define KSMBD_TCP_SEND_TIMEOUT (5 * HZ) +#define KSMBD_SESSION_EXPIRATION_INTERVAL (5 * HZ) +#define KSMBD_UNAUTHENTICATED_CONN_TIMEOUT (45 * HZ)#define KSMBD_TCP_PEER_SOCKADDR (c) ((struct sockaddr *)&((c)->peer_addr))#define CONN_HASH_BITS 12
fs/smb/server/mgmt/user_session.c +116 -8
170 lines not on this page · raw message
fs/smb/server/mgmt/user_session.h +7 -1
@@ -72,6 +72,8 @@ struct ksmbd_session {struct rw_semaphore rpc_lock; }; +#define KSMBD_MAX_CHANNELS 32 +static inline int test_session_flag (struct ksmbd_session *sess,int bit) {return sess->flags & bit;
@@ -98,7 +100,11 @@ bool is_ksmbd_session_in_connection(struct ksmbd_conn *conn, unsigned long long id);int ksmbd_session_register (struct ksmbd_conn *conn,struct ksmbd_session *sess); -void ksmbd_sessions_deregister (struct ksmbd_conn *conn); +void ksmbd_session_unregister (struct ksmbd_conn *conn, +struct ksmbd_session *sess); +void ksmbd_conn_sessions_cleanup (struct ksmbd_conn *conn); +bool ksmbd_conn_has_valid_or_expired_session (struct ksmbd_conn *conn); +void ksmbd_expire_sessions (void );struct ksmbd_session *__session_lookup (unsigned long long id);struct ksmbd_session *ksmbd_session_lookup_all (struct ksmbd_conn *conn,unsigned long long id);
fs/smb/server/proc.c +2 -0
@@ -178,6 +178,8 @@ static int proc_show_ksmbd_stats(struct seq_file *m, void *v)proc_show_runtime_totals (m);seq_printf (m,"sessions:\t%lld\n" ,ksmbd_counter_sum (KSMBD_COUNTER_SESSIONS )); +seq_printf (m,"session_timeouts:\t%lld\n" , +ksmbd_counter_sum (KSMBD_COUNTER_SESSION_TIMEOUTS ));seq_printf (m,"tree_connects:\t%lld\n" ,ksmbd_counter_sum (KSMBD_COUNTER_TREE_CONNS )); seq_printf(m, "requests:\t%lld\n",
fs/smb/server/server.c +1 -1
@@ -403,7 +403,7 @@ static int ksmbd_server_process_request(struct ksmbd_conn *conn)static int ksmbd_server_terminate_conn (struct ksmbd_conn *conn) { -ksmbd_sessions_deregister (conn); +ksmbd_conn_sessions_cleanup (conn);destroy_lease_table (conn);return 0 ; }
fs/smb/server/smb2pdu.c +25 -12
@@ -83,8 +83,6 @@ struct channel *lookup_chann_list(struct ksmbd_session *sess, struct ksmbd_connreturn chann; } -#define KSMBD_MAX_CHANNELS 32 -static int register_session_channel (struct ksmbd_session *sess,struct ksmbd_conn *conn,const char *sess_key)
@@ -914,8 +912,14 @@ static bool smb2_session_expired_cmd_allowed(struct ksmbd_work *work,static bool smb2_session_kerberos_expired (struct ksmbd_session *sess) { -return sess->kerberos_expiry && -ktime_get_real_seconds () >= sess->kerberos_expiry; +if (!sess->kerberos_expiry || +ktime_get_real_seconds () < sess->kerberos_expiry) +return false; + +if (cmpxchg (&sess->state,SMB2_SESSION_VALID , +SMB2_SESSION_EXPIRED ) ==SMB2_SESSION_VALID ) +ksmbd_counter_inc (KSMBD_COUNTER_SESSION_TIMEOUTS ); +return true; }/**
@@ -950,9 +954,8 @@ int smb2_check_user_session(struct ksmbd_work *work)if (!work->next_smb2_rcv_hdr_off && sess_id) work->sess =ksmbd_session_lookup_all_states (conn, sess_id);if (work->sess) { -if (smb2_session_kerberos_expired (work->sess)) { - work->sess->state =SMB2_SESSION_EXPIRED ; - }else if (work->sess->state !=SMB2_SESSION_VALID ) { +if (!smb2_session_kerberos_expired (work->sess) && + work->sess->state !=SMB2_SESSION_VALID ) {ksmbd_user_session_put (work->sess); work->sess =NULL ; }
@@ -977,8 +980,7 @@ int smb2_check_user_session(struct ksmbd_work *work) sess_id, work->sess->id);return -EINVAL ; } -if (smb2_session_kerberos_expired (work->sess)) - work->sess->state =SMB2_SESSION_EXPIRED ; +smb2_session_kerberos_expired (work->sess);if (work->sess->state !=SMB2_SESSION_VALID ) {pr_err ("compound request on a non-valid session (state %d)\n" , work->sess->state);
@@ -995,7 +997,6 @@ int smb2_check_user_session(struct ksmbd_work *work) work->sess =ksmbd_session_lookup_all_states (conn, sess_id);if (work->sess) {if (smb2_session_kerberos_expired (work->sess)) { - work->sess->state =SMB2_SESSION_EXPIRED ;return smb2_session_expired_cmd_allowed (work, cmd) ?1 : -EKEYEXPIRED ; }
@@ -2192,7 +2193,7 @@ int smb2_sess_setup(struct ksmbd_work *work)struct ksmbd_conn *conn = work->conn;struct smb2_sess_setup_req *req;struct smb2_sess_setup_rsp *rsp; -struct ksmbd_session *sess; +struct ksmbd_session *sess =NULL ;struct negotiate_message *negblob;unsigned int negblob_len, negblob_off;int rc =0 ;
@@ -2333,6 +2334,9 @@ int smb2_sess_setup(struct ksmbd_work *work) goto out_err; } +if (work->session_setup_reauth) +WRITE_ONCE (sess->state,SMB2_SESSION_IN_PROGRESS ); + conn->binding = false; } work->sess = sess;
@@ -2444,6 +2448,14 @@ int smb2_sess_setup(struct ksmbd_work *work) }if (rc <0 ) { +bool setup_in_progress = sess && +READ_ONCE (sess->state) ==SMB2_SESSION_IN_PROGRESS && + !(req->Flags &SMB2_SESSION_REQ_FLAG_BINDING ); + +/* Authentication errors must not leave the new session published. */ +if (setup_in_progress) +ksmbd_session_unregister (conn, sess); +if (sess && conn->dialect ==SMB311_PROT_ID && (req->Flags &SMB2_SESSION_REQ_FLAG_BINDING )) {struct preauth_session *preauth_sess;
@@ -2477,7 +2489,8 @@ int smb2_sess_setup(struct ksmbd_work *work)* For binding requests, session belongs to another * connection. Do not expire it. */ -if (!(req->Flags &SMB2_SESSION_REQ_FLAG_BINDING )) { +if (!(req->Flags &SMB2_SESSION_REQ_FLAG_BINDING ) && + !setup_in_progress) { sess->last_active = jiffies; sess->kerberos_expiry =0 ; sess->state =SMB2_SESSION_EXPIRED ;
fs/smb/server/smb2pdu.h +0 -2
@@ -61,8 +61,6 @@ struct preauth_integrity_info {#define SMB2_SESSION_IN_PROGRESS BIT(0)#define SMB2_SESSION_VALID BIT(1) -#define SMB2_SESSION_TIMEOUT (10 * HZ) -/* Apple Defined Contexts */ #define SMB2_CREATE_AAPL "AAPL"
fs/smb/server/stats.h +1 -0
@@ -15,6 +15,7 @@enum {KSMBD_COUNTER_SESSIONS =0 , +KSMBD_COUNTER_SESSION_TIMEOUTS ,KSMBD_COUNTER_TREE_CONNS ,KSMBD_COUNTER_REQUESTS ,KSMBD_COUNTER_STATUS_SUCCESS ,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 143/438 wifi: virt_wifi: dont transfer operstate before register
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zihan Xi <[email protected]>
[ Upstream commit e5c8d7acd31b27057ea42cd405d0b3ece097bc89 ]
virt_wifi_newlink() calls netif_stacked_transfer_operstate() before register_netdevice(). If the lower device is dormant, that queues the new netdev on lweventlist while it is still uninitialized. If registration fails after that, for example because of an invalid name such as "bad/name", free_netdev() immediately frees the object. A later linkwatch_fire_event() then use-after-frees the list entry.
Move the transfer to after netdev_upper_dev_link(), as macvlan and ipvlan already do.
--- drivers/net/wireless/virtual/virt_wifi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/net/wireless/virtual/virt_wifi.c +2 -1
@@ -558,7 +558,6 @@ static int virt_wifi_newlink(struct net_device *dev, }eth_hw_addr_inherit (dev, priv->lowerdev); -netif_stacked_transfer_operstate (priv->lowerdev, dev); dev->ieee80211_ptr =kzalloc_obj (*dev->ieee80211_ptr);
@@ -584,6 +583,8 @@ static int virt_wifi_newlink(struct net_device *dev, goto unregister_netdev; } +netif_stacked_transfer_operstate (priv->lowerdev, dev); + dev->priv_destructor = virt_wifi_net_device_destructor; priv->being_deleted = false; priv->is_connected = false;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 144/438 drm/xe/mmio_gem: forbid VMA split
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ilia Levi <[email protected]>
[ Upstream commit 247a82da6f563dcfd9074a68f99a0c0997d0679c ]
The fault handler assumes it always operates on a VMA spanning the entire GEM object. This does not hold when the VMA has been split, e.g. by a partial munmap or mprotect. In that case the handler may map wrong physical pages or cause SIGBUS.
Handle this by forbidding VMA split, as partial unmaps are not deemed useful for MMIO GEMs.
--- drivers/gpu/drm/xe/xe_mmio_gem.c | 10 ++++++++++ 1 file changed, 10 insertions(+)
drivers/gpu/drm/xe/xe_mmio_gem.c +10 -0
@@ -39,10 +39,20 @@ struct xe_mmio_gem {phys_addr_t phys_addr; }; +static int xe_mmio_gem_vm_may_split (struct vm_area_struct *area,unsigned long addr) +{ +/* +* Forbid splitting. Together with VM_DONTEXPAND, this keeps the VMA +* matching the GEM object exactly. +*/ +return -EINVAL ; +} +static const struct vm_operations_struct vm_ops = { .open = drm_gem_vm_open, .close = drm_gem_vm_close, .fault = xe_mmio_gem_vm_fault, + .may_split = xe_mmio_gem_vm_may_split, };static const struct drm_gem_object_funcs xe_mmio_gem_funcs = {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 145/438 drm/xe/mmio_gem: use write-back mapping for dummy page
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ilia Levi <[email protected]>
[ Upstream commit 819f189265a5955da743e78e20a713a038982e89 ]
Currently vmf_insert_pfn() maps the dummy page as UC, inheriting the VMA's page protection which was set for the real MMIO region. This conflicts with the direct map's WB mapping of the same page, creating a cache type alias which is architecturally undefined on some platforms.
Use vmf_insert_pfn_prot() with a WB pgprot instead. Also simplify to fault in the requested page instead of the whole VMA.
--- drivers/gpu/drm/xe/xe_mmio_gem.c | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-)
drivers/gpu/drm/xe/xe_mmio_gem.c +5 -14
@@ -172,14 +172,13 @@ static void xe_mmio_gem_release_dummy_page(struct drm_device *dev, void *res)__free_page ((struct page *)res); } -static vm_fault_t xe_mmio_gem_vm_fault_dummy_page (struct vm_area_struct *vma) +static vm_fault_t xe_mmio_gem_vm_fault_dummy_page (struct vm_fault *vmf) { +struct vm_area_struct *vma = vmf->vma;struct drm_gem_object *base = vma->vm_private_data;struct drm_device *dev = base->dev; -vm_fault_t ret =VM_FAULT_NOPAGE ;struct page *page;unsigned long pfn; -unsigned long i; page =alloc_page (GFP_KERNEL | __GFP_ZERO);if (!page)
@@ -190,16 +189,8 @@ static vm_fault_t xe_mmio_gem_vm_fault_dummy_page(struct vm_area_struct *vma) pfn =page_to_pfn (page); -/* Map the entire VMA to the same dummy page */ -for (i =0 ; i < base->size; i +=PAGE_SIZE ) { -unsigned long addr = vma->vm_start + i; - - ret =vmf_insert_pfn (vma, addr, pfn); -if (ret &VM_FAULT_ERROR ) -break ; - } - -return ret; +return vmf_insert_pfn_prot (vma, vmf->address, pfn, +vm_get_page_prot (vma->vm_flags)); }static vm_fault_t xe_mmio_gem_vm_fault (struct vm_fault *vmf)
@@ -219,7 +210,7 @@ static vm_fault_t xe_mmio_gem_vm_fault(struct vm_fault *vmf)* It is assumed the userspace will receive the notification via some * other channel (e.g. drm uevent). */ -return xe_mmio_gem_vm_fault_dummy_page (vma); +return xe_mmio_gem_vm_fault_dummy_page (vmf); }for (i =0 ; i < base->size; i +=PAGE_SIZE ) {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 146/438 drm/xe/mmio_gem: Revoke drm_vma_node on xe_mmio_gem destroy
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Shuicheng Lin <[email protected]>
[ Upstream commit 37fcbd7b2f8996d783932dab11bc668e169b0de6 ]
xe_mmio_gem_create() calls drm_vma_node_allow() but nothing ever calls drm_vma_node_revoke(). The drm_vma_offset_file rb-tree entry allocated by drm_vma_node_allow() is not freed by drm_gem_object_release(), so it is leaked on every create/destroy cycle.
Add a struct drm_file * parameter to xe_mmio_gem_destroy() and call drm_vma_node_revoke() from there, mirroring the drm_vma_node_allow() call in xe_mmio_gem_create().
--- drivers/gpu/drm/xe/xe_mmio_gem.c | 4 +++- drivers/gpu/drm/xe/xe_mmio_gem.h | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-)
drivers/gpu/drm/xe/xe_mmio_gem.c +3 -1
@@ -138,14 +138,16 @@ static void xe_mmio_gem_free(struct drm_gem_object *base)/** * xe_mmio_gem_destroy - Destroy the GEM object that exposes an MMIO region * @gem: the GEM object to destroy +* @file: DRM file descriptor previously passed to xe_mmio_gem_create() * * This function releases resources associated with the GEM object created by * xe_mmio_gem_create(). * * See: "Exposing MMIO regions to userspace" */ -void xe_mmio_gem_destroy (struct xe_mmio_gem *gem) +void xe_mmio_gem_destroy (struct xe_mmio_gem *gem,struct drm_file *file) { +drm_vma_node_revoke (&gem->base.vma_node, file);xe_mmio_gem_free (&gem->base); }
drivers/gpu/drm/xe/xe_mmio_gem.h +1 -1
@@ -15,6 +15,6 @@ struct xe_mmio_gem;struct xe_mmio_gem *xe_mmio_gem_create (struct xe_device *xe,struct drm_file *file,phys_addr_t phys_addr,size_t size);u64 xe_mmio_gem_mmap_offset (struct xe_mmio_gem *gem); -void xe_mmio_gem_destroy (struct xe_mmio_gem *gem); +void xe_mmio_gem_destroy (struct xe_mmio_gem *gem,struct drm_file *file);#endif /* _XE_MMIO_GEM_H_ */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 147/438 drm/xe/shrinker: Return the freed page count through a parameter
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Shuicheng Lin <[email protected]>
[ Upstream commit 3c90e42a01426262f0cd166bc01b45c05562640d ]
__xe_shrinker_walk() and xe_shrinker_walk() return either the number of pages freed or a negative error, so the two cannot be reported at once. On error the pages already freed are dropped, and since xe_shrinker_scan() only accumulates non-negative returns while *scanned is updated by pointer, the shrinker tells mm that it scanned without freeing.
Accumulate the count into a caller-provided counter and return only the status, so an error no longer discards what the walk had freed.
--- drivers/gpu/drm/xe/xe_shrinker.c | 62 ++++++++++++++------------------ 1 file changed, 26 insertions(+), 36 deletions(-)
drivers/gpu/drm/xe/xe_shrinker.c +26 -36
@@ -54,13 +54,14 @@ xe_shrinker_mod_pages(struct xe_shrinker *shrinker, long shrinkable, long purgeawrite_unlock (&shrinker->lock); } -static s64 __xe_shrinker_walk (struct xe_device *xe, +static int __xe_shrinker_walk (struct xe_device *xe,struct ttm_operation_ctx *ctx,const struct xe_bo_shrink_flags flags, -unsigned long to_scan,unsigned long *scanned) +unsigned long to_scan,unsigned long *scanned, +unsigned long *freed) {unsigned int mem_type; -s64 freed =0 , lret; +s64 lret;for (mem_type =XE_PL_SYSTEM ; mem_type <=XE_PL_TT ; ++mem_type) {struct ttm_resource_manager *man =ttm_manager_type (&xe->ttm, mem_type);
@@ -82,7 +83,7 @@ static s64 __xe_shrinker_walk(struct xe_device *xe,if (lret <0 )return lret; - freed += lret; + *freed += lret;if (*scanned >= to_scan)break ; }
@@ -90,7 +91,7 @@ static s64 __xe_shrinker_walk(struct xe_device *xe,xe_assert (xe, !IS_ERR (ttm_bo)); } -return freed; +return 0 ; }/*
@@ -99,40 +100,35 @@ static s64 __xe_shrinker_walk(struct xe_device *xe,* add writeback. This avoids stalls and explicit writebacks with light or * moderate memory pressure. */ -static s64 xe_shrinker_walk (struct xe_device *xe, +static int xe_shrinker_walk (struct xe_device *xe,struct ttm_operation_ctx *ctx,const struct xe_bo_shrink_flags flags, -unsigned long to_scan,unsigned long *scanned) +unsigned long to_scan,unsigned long *scanned, +unsigned long *freed) {bool no_wait_gpu = true;struct xe_bo_shrink_flags save_flags = flags; -s64 lret, freed; +int ret;swap (no_wait_gpu, ctx->no_wait_gpu); save_flags.writeback = false; - lret =__xe_shrinker_walk (xe, ctx, save_flags, to_scan, scanned); + ret =__xe_shrinker_walk (xe, ctx, save_flags, to_scan, scanned, freed);swap (no_wait_gpu, ctx->no_wait_gpu); -if (lret <0 || *scanned >= to_scan) -return lret; +if (ret || *scanned >= to_scan) +return ret; - freed = lret;if (!ctx->no_wait_gpu) { - lret =__xe_shrinker_walk (xe, ctx, save_flags, to_scan, scanned); -if (lret <0 ) -return lret; - freed += lret; -if (*scanned >= to_scan) -return freed; + ret =__xe_shrinker_walk (xe, ctx, save_flags, to_scan, scanned, + freed); +if (ret || *scanned >= to_scan) +return ret; } -if (flags.writeback) { - lret =__xe_shrinker_walk (xe, ctx, flags, to_scan, scanned); -if (lret <0 ) -return lret; - freed += lret; - } +if (flags.writeback) + ret =__xe_shrinker_walk (xe, ctx, flags, to_scan, scanned, + freed); -return freed; +return ret; } static unsigned long
@@ -214,7 +210,6 @@ static unsigned long xe_shrinker_scan(struct shrinker *shrink, struct shrink_conbool runtime_pm;bool purgeable;bool can_backup = !!(sc->gfp_mask & __GFP_FS); -s64 lret; nr_to_scan = sc->nr_to_scan;
@@ -225,12 +220,9 @@ static unsigned long xe_shrinker_scan(struct shrinker *shrink, struct shrink_con/* Might need runtime PM. Try to wake early if it looks like it. */ runtime_pm =xe_shrinker_runtime_pm_get (shrinker, false, nr_to_scan, can_backup); -if (purgeable && nr_scanned < nr_to_scan) { - lret =xe_shrinker_walk (shrinker->xe, &ctx, shrink_flags, - nr_to_scan, &nr_scanned); -if (lret >=0 ) - freed += lret; - } +if (purgeable && nr_scanned < nr_to_scan) +xe_shrinker_walk (shrinker->xe, &ctx, shrink_flags, + nr_to_scan, &nr_scanned, &freed); sc->nr_scanned = nr_scanned;if (nr_scanned >= nr_to_scan || !can_backup)
@@ -242,10 +234,8 @@ static unsigned long xe_shrinker_scan(struct shrinker *shrink, struct shrink_con shrink_flags.purge = false; - lret =xe_shrinker_walk (shrinker->xe, &ctx, shrink_flags, - nr_to_scan, &nr_scanned); -if (lret >=0 ) - freed += lret; +xe_shrinker_walk (shrinker->xe, &ctx, shrink_flags, + nr_to_scan, &nr_scanned, &freed); sc->nr_scanned = nr_scanned; out:
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 148/438 drm/xe/shrinker: Take a runtime PM ref before shrinking non-system memory
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Shuicheng Lin <[email protected]>
[ Upstream commit 985862be16c7e4da808c51f393d631fb60c0be5c ]
__xe_shrinker_walk() walks the SYSTEM and TT LRUs without a runtime PM reference. Shrinking a bo outside system memory invalidates its GPU mappings, which needs the device resumed, so while it is runtime suspended the page table zap trips an assert and the TLB invalidation returns -ENODEV:
WARNING: drivers/gpu/drm/xe/xe_bo.c:770 at xe_bo_move_notify+0x1fc/0x450 [xe] xe_bo_shrink+0x20f/0x2b0 [xe] __xe_shrinker_walk+0x174/0x410 [xe] xe_shrinker_scan+0x10c/0x1e0 [xe] do_shrink_slab+0x176/0x7e0 drop_caches_sysctl_handler+0x9c/0xf0
Take a reference before walking a memory type other than XE_PL_SYSTEM and stop there if it cannot be acquired. Reuse the shrinker's existing acquire path, which resumes the device directly where reclaim allows that and otherwise queues the PM worker for a later scan. Stop the walk once the scan target is met, so a satisfied scan does not wake the device. System memory is still reclaimed while the device is suspended.
Gate this on xe_device_is_l2_flush_optimized(), the same condition under which xe_bo_trigger_rebind() issues the invalidation for a non-fault-mode vm, so reclaim is unaffected elsewhere. The System CCS copy already has its own reference in xe_bo_shrink().
Only a non-fault-mode vm can reach this, since a fault-mode vm requires LR mode and that holds a runtime PM reference for the vm's lifetime.
Reproduced with igt@xe_madvise@dontneed-before-exec while the GPU is runtime suspended.
v2: simplify needs_rpm check. (Matt)
retarget Fixes tag since the issue occurs with the non-fault-mode path added by 4e7ebff69aed.
v3: handle this in xe_shrinker.c instead of xe_bo.c (Thomas)
v4: stop the walk once the scan target is met. (Sashiko)
v5: rebase on the freed page accounting fix. (Sashiko)
v6: reuse the shrinker acquire path so runtime pm can be resumed
directly instead of always queueing a worker. (Thomas)
v7: replace xe_pm_runtime_put() with xe_shrinker_runtime_pm_put(). (Thomas)
--- drivers/gpu/drm/xe/xe_shrinker.c | 78 +++++++++++++++++++++----------- 1 file changed, 51 insertions(+), 27 deletions(-)
drivers/gpu/drm/xe/xe_shrinker.c +51 -27
@@ -54,13 +54,39 @@ xe_shrinker_mod_pages(struct xe_shrinker *shrinker, long shrinkable, long purgeawrite_unlock (&shrinker->lock); } -static int __xe_shrinker_walk (struct xe_device *xe, +static bool __xe_shrinker_runtime_pm_get (struct xe_shrinker *shrinker) +{ +struct xe_device *xe = shrinker->xe; + +if (xe_pm_runtime_get_if_active (xe)) +return true; + +if (xe_rpm_reclaim_safe (xe) && !ttm_bo_shrink_avoid_wait ()) { +xe_pm_runtime_get (xe); +return true; + } + +queue_work (xe->unordered_wq, &shrinker->pm_worker); + +return false; +} + +static void xe_shrinker_runtime_pm_put (struct xe_shrinker *shrinker,bool runtime_pm) +{ +if (runtime_pm) +xe_pm_runtime_put (shrinker->xe); +} + +static int __xe_shrinker_walk (struct xe_shrinker *shrinker,struct ttm_operation_ctx *ctx,const struct xe_bo_shrink_flags flags,unsigned long to_scan,unsigned long *scanned,unsigned long *freed) { +struct xe_device *xe = shrinker->xe;unsigned int mem_type; +bool rpm = false; +int ret =0 ;s64 lret;for (mem_type =XE_PL_SYSTEM ; mem_type <=XE_PL_TT ; ++mem_type) {
@@ -75,23 +101,35 @@ static int __xe_shrinker_walk(struct xe_device *xe,if (!man || !man->use_tt)continue ; +if (mem_type !=XE_PL_SYSTEM && !rpm && +xe_device_is_l2_flush_optimized (xe)) { +if (!__xe_shrinker_runtime_pm_get (shrinker)) +break ; + rpm = true; + } +ttm_bo_lru_for_each_reserved_guarded (&curs, man, &arg, ttm_bo) {if (!ttm_bo_shrink_suitable (ttm_bo, ctx))continue ; lret =xe_bo_shrink (ctx, ttm_bo, flags, scanned); -if (lret <0 ) -return lret; +if (lret <0 ) { + ret = lret; + goto out; + } *freed += lret;if (*scanned >= to_scan) -break ; + goto out; }/* Trylocks should never error, just fail. */ xe_assert (xe, !IS_ERR (ttm_bo)); } -return 0 ; +out: +xe_shrinker_runtime_pm_put (shrinker, rpm); + +return ret; }/*
@@ -100,7 +138,7 @@ static int __xe_shrinker_walk(struct xe_device *xe,* add writeback. This avoids stalls and explicit writebacks with light or * moderate memory pressure. */ -static int xe_shrinker_walk(struct xe_device *xe, +static int xe_shrinker_walk(struct xe_shrinker *shrinker, struct ttm_operation_ctx *ctx, const struct xe_bo_shrink_flags flags, unsigned long to_scan, unsigned long *scanned,
@@ -112,20 +150,21 @@ static int xe_shrinker_walk(struct xe_device *xe,swap (no_wait_gpu, ctx->no_wait_gpu); save_flags.writeback = false; - ret =__xe_shrinker_walk (xe, ctx, save_flags, to_scan, scanned, freed); + ret =__xe_shrinker_walk (shrinker, ctx, save_flags, to_scan, scanned, + freed);swap (no_wait_gpu, ctx->no_wait_gpu);if (ret || *scanned >= to_scan)return ret;if (!ctx->no_wait_gpu) { - ret =__xe_shrinker_walk (xe, ctx, save_flags, to_scan, scanned, + ret =__xe_shrinker_walk (shrinker, ctx, save_flags, to_scan, scanned, freed);if (ret || *scanned >= to_scan)return ret; }if (flags.writeback) - ret =__xe_shrinker_walk (xe, ctx, flags, to_scan, scanned, + ret =__xe_shrinker_walk (shrinker, ctx, flags, to_scan, scanned, freed);return ret;
@@ -176,22 +215,7 @@ static bool xe_shrinker_runtime_pm_get(struct xe_shrinker *shrinker, bool force,return false; } -if (!xe_pm_runtime_get_if_active (xe)) { -if (xe_rpm_reclaim_safe (xe) && !ttm_bo_shrink_avoid_wait ()) { -xe_pm_runtime_get (xe); -return true; - } -queue_work (xe->unordered_wq, &shrinker->pm_worker); -return false; - } - -return true; -} - -static void xe_shrinker_runtime_pm_put (struct xe_shrinker *shrinker,bool runtime_pm) -{ -if (runtime_pm) -xe_pm_runtime_put (shrinker->xe); +return __xe_shrinker_runtime_pm_get (shrinker); }static unsigned long xe_shrinker_scan (struct shrinker *shrink,struct shrink_control *sc)
@@ -221,7 +245,7 @@ static unsigned long xe_shrinker_scan(struct shrinker *shrink, struct shrink_con runtime_pm =xe_shrinker_runtime_pm_get (shrinker, false, nr_to_scan, can_backup);if (purgeable && nr_scanned < nr_to_scan) -xe_shrinker_walk (shrinker->xe, &ctx, shrink_flags, +xe_shrinker_walk (shrinker, &ctx, shrink_flags, nr_to_scan, &nr_scanned, &freed); sc->nr_scanned = nr_scanned;
@@ -234,7 +258,7 @@ static unsigned long xe_shrinker_scan(struct shrinker *shrink, struct shrink_con shrink_flags.purge = false; -xe_shrinker_walk (shrinker->xe, &ctx, shrink_flags, +xe_shrinker_walk (shrinker, &ctx, shrink_flags, nr_to_scan, &nr_scanned, &freed); sc->nr_scanned = nr_scanned;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 149/438 drm/vc4: Use managed KMS polling to fix UAF on unbind
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Karl Mehltretter <[email protected]>
[ Upstream commit 073a30d75f309812ed61af134f24ffef4107b13a ]
vc4_kms_load() calls drm_kms_helper_poll_init() but the driver provides no matching drm_kms_helper_poll_fini(). The output poll work stays scheduled after unbind and runs on the freed drm_device:
# modprobe vc4; rmmod vc4; sleep 10 BUG: KASAN: slab-use-after-free in delayed_work_timer_fn BUG: KASAN: slab-use-after-free in drm_client_dev_hotplug [drm] Workqueue: events output_poll_execute [drm_kms_helper] Allocated by task 171: __devm_drm_dev_alloc Freed by task 262 (rmmod): drm_dev_put / component_del
Use drmm_kms_helper_poll_init() so polling is finalized with the device, as other drivers do.
--- drivers/gpu/drm/vc4/vc4_kms.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/gpu/drm/vc4/vc4_kms.c +1 -1
@@ -1188,7 +1188,7 @@ int vc4_kms_load(struct drm_device *dev)drm_mode_config_reset (dev); -drm_kms_helper_poll_init (dev); +drmm_kms_helper_poll_init (dev);return 0 ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 150/438 ALSA: hda: trace PCM open only after assigning a stream
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Slavin Liu <[email protected]>
[ Upstream commit c9e6e5f38bf75276605f1952b22285f5f3abcaff ]
Stream assignment can fail when hardware streams are exhausted. Move the tracepoint after the NULL check because its payload accesses the assigned stream tag.
Detected by static analysis and reviewed with AI-assisted source auditing.
--- sound/hda/common/controller.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
sound/hda/common/controller.c +1 -1
@@ -586,11 +586,11 @@ static int azx_pcm_open(struct snd_pcm_substream *substream)snd_hda_codec_pcm_get (apcm->info);mutex_lock (&chip->open_mutex); azx_dev =azx_assign_device (chip, substream); -trace_azx_pcm_open (chip, azx_dev);if (azx_dev ==NULL ) { err = -EBUSY ; goto unlock; } +trace_azx_pcm_open (chip, azx_dev); runtime->private_data = azx_dev; runtime->hw = azx_pcm_hw;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 151/438 wifi: ath11k: cleanup arsta in ath11k_mac_peer_cleanup_all()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Nicolas Escande <[email protected]>
[ Upstream commit 820b8cff81c796ba20573e04722ab62500713f97 ]
When mac80211 removes a sta, it calls .sta_state() which in turn calls ath11k_mac_station_remove(). In that function we clean up both peers & arsta related resources.
But when the firmware crashes, ath11k calls ieee80211_restart_hw(), which assumes that all driver related resources are cleaned up beforehand. This cleanup is supposedly done by ath11k_mac_peer_cleanup_all() but does not in fact free arsta->rx_stats / tx_stats.
Extract the arsta cleanup from ath11k_mac_station_remove() into a new ath11k_mac_station_cleanup() and call it from both there and ath11k_mac_peer_cleanup_all().
This should handle kmemleaks reports like:
unreferenced object 0xffffff801ae66400 (size 1024): comm "hostapd", pid 1306, jiffies 4295011565 hex dump (first 32 bytes): 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ backtrace (crc d61c08ec): kmemleak_alloc+0x3c/0x50 __kmalloc_cache_noprof+0x2b0/0x3e0 ath11k_mac_op_sta_state+0x1dc/0xb10 drv_sta_state+0xac/0x6f8 sta_info_insert_rcu+0x314/0x5e0 sta_info_insert+0x14/0x38 ieee80211_add_station+0x10c/0x1a0 nl80211_new_station+0x3e8/0x680 genl_family_rcv_msg_doit+0xc0/0x120 genl_rcv_msg+0x1b4/0x258 netlink_rcv_skb+0x4c/0x108 genl_rcv+0x38/0x60 netlink_unicast+0x190/0x278 netlink_sendmsg+0x15c/0x370 ____sys_sendmsg+0x120/0x290 ___sys_sendmsg+0x70/0xa0
Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.9.0.1-01977-QCAHKSWPL_SILICONZ-1
--- drivers/net/wireless/ath/ath11k/mac.c | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-)
drivers/net/wireless/ath/ath11k/mac.c +18 -7
@@ -873,6 +873,22 @@ static int ath11k_mac_set_kickout(struct ath11k_vif *arvif)return 0 ; } +static void ath11k_mac_station_cleanup (struct ieee80211_sta *sta) +{ +struct ath11k_sta *arsta; + +if (!sta) +return ; + + arsta =ath11k_sta_to_arsta (sta); + +kfree (arsta->tx_stats); + arsta->tx_stats =NULL ; + +kfree (arsta->rx_stats); + arsta->rx_stats =NULL ; +} +void ath11k_mac_peer_cleanup_all (struct ath11k *ar) {struct ath11k_peer *peer, *tmp;
@@ -885,6 +901,7 @@ void ath11k_mac_peer_cleanup_all(struct ath11k *ar)list_for_each_entry_safe (peer, tmp, &ab->peers, list) {ath11k_peer_rx_tid_cleanup (ar, peer);ath11k_peer_rhash_delete (ab, peer); +ath11k_mac_station_cleanup (peer->sta);list_del (&peer->list);kfree (peer); }
@@ -9892,7 +9909,6 @@ static int ath11k_mac_station_remove(struct ath11k *ar, {struct ath11k_base *ab = ar->ab;struct ath11k_vif *arvif =ath11k_vif_to_arvif (vif); -struct ath11k_sta *arsta =ath11k_sta_to_arsta (sta);int ret; if (ab->hw_params.vdev_start_delay &&
@@ -9916,12 +9932,7 @@ static int ath11k_mac_station_remove(struct ath11k *ar, sta->addr, arvif->vdev_id);ath11k_mac_dec_num_stations (arvif, sta); - -kfree (arsta->tx_stats); - arsta->tx_stats =NULL ; - -kfree (arsta->rx_stats); - arsta->rx_stats =NULL ; +ath11k_mac_station_cleanup (sta);return ret; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 152/438 btrfs: tree-checker: print dev extent offset in error message
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Filipe Manana <[email protected]>
[ Upstream commit a1167d9420474ab9ed9efca99d86aeb6217c0265 ]
If a dev extent's offset is not sector size aligned, the error message is printing the dev extent's objectid instead of the offset. This is a copy paste error, as before this check we check the objectid field.
--- fs/btrfs/tree-checker.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
fs/btrfs/tree-checker.c +1 -1
@@ -2100,7 +2100,7 @@ static int check_dev_extent_item(const struct extent_buffer *leaf, sectorsize))) {generic_err (leaf, slot,"invalid dev extent chunk offset, has %llu not aligned to %u" , -btrfs_dev_extent_chunk_objectid (leaf, de), +btrfs_dev_extent_chunk_offset (leaf, de), sectorsize);return -EUCLEAN ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 153/438 drm/msm/dsi: round the byte clock rate after reparenting to the PHY PLL
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Dmitry Baryshkov <[email protected]>
[ Upstream commit 2028280686f4fa78e2f1f6dede4b6c1fd782b9e3 ]
DSI 6G v2.9 hosts (SM8650, SM8750, Kaanapali, etc.) reparent the byte and pixel RCGs to the DSI PHY PLL at runtime from dsi_link_clk_set_rate_6g_v2_9(), after the PHY has been enabled. However dsi_calc_clk_rate_6g() runs earlier, in order to compute the bit clock request for the PHY. At that point the byte RCG still has its reset parent (XO), so clk_round_rate() returns a bogus rate, which then ends up in the PHY bit clock request and the PLL gets programmed to a wrong frequency, breaking the panel.
Move the rounding to dsi_link_clk_set_rate_6g(), which is called after the RCGs have been reparented to the PLL. Storing the rounded rate at this point still makes later link_clk_set_rate() calls no-ops in the CCF. Derive the byte interface clock rate from the rounded byte clock rate, otherwise it would keep requesting the idealized rate and retrigger the PLL on every transfer.
Patchwork: https://patchwork.freedesktop.org/patch/750496/
--- drivers/gpu/drm/msm/dsi/dsi_host.c | 36 ++++++++++++++++-------------- 1 file changed, 19 insertions(+), 17 deletions(-)
drivers/gpu/drm/msm/dsi/dsi_host.c +19 -17
@@ -129,7 +129,7 @@ struct msm_dsi_host {struct clk *dsi_pll_pixel_clk;unsigned long byte_clk_rate; -unsigned long byte_intf_clk_rate; +bool byte_intf_clk_div_2;unsigned long pixel_clk_rate;unsigned long esc_clk_rate;
@@ -381,8 +381,20 @@ int msm_dsi_runtime_resume(struct device *dev)int dsi_link_clk_set_rate_6g (struct msm_dsi_host *msm_host) { +unsigned long byte_intf_clk_rate; +long rounded_byte_clk_rate;int ret; + rounded_byte_clk_rate =clk_round_rate (msm_host->byte_clk, + msm_host->byte_clk_rate); +if (rounded_byte_clk_rate <0 ) { +pr_err ("%s: failed to round byte clock rate, %ld\n" , + __func__, rounded_byte_clk_rate); +return rounded_byte_clk_rate; + } + + msm_host->byte_clk_rate = rounded_byte_clk_rate; +DBG ("Set clk rates: pclk=%lu, byteclk=%lu" , msm_host->pixel_clk_rate, msm_host->byte_clk_rate);
@@ -400,7 +412,11 @@ int dsi_link_clk_set_rate_6g(struct msm_dsi_host *msm_host) }if (msm_host->byte_intf_clk) { - ret =clk_set_rate (msm_host->byte_intf_clk, msm_host->byte_intf_clk_rate); + byte_intf_clk_rate = msm_host->byte_clk_rate; +if (msm_host->byte_intf_clk_div_2) + byte_intf_clk_rate /=2 ; + + ret =clk_set_rate (msm_host->byte_intf_clk, byte_intf_clk_rate);if (ret) {pr_err ("%s: Failed to set rate byte intf clk, %d\n" , __func__, ret);
@@ -668,24 +684,12 @@ static void dsi_calc_pclk(struct msm_dsi_host *msm_host, bool is_bonded_dsi)int dsi_calc_clk_rate_6g (struct msm_dsi_host *msm_host,bool is_bonded_dsi) { -long rounded_byte_clk_rate; -if (!msm_host->mode) {pr_err ("%s: mode not set\n" , __func__);return -EINVAL ; }dsi_calc_pclk (msm_host, is_bonded_dsi); - - rounded_byte_clk_rate =clk_round_rate (msm_host->byte_clk, - msm_host->byte_clk_rate); -if (rounded_byte_clk_rate <0 ) { -pr_err ("%s: failed to round byte clock rate, %ld\n" , - __func__, rounded_byte_clk_rate); -return rounded_byte_clk_rate; - } - - msm_host->byte_clk_rate = rounded_byte_clk_rate; msm_host->esc_clk_rate =clk_get_rate (msm_host->esc_clk);return 0 ; }
@@ -2500,9 +2504,7 @@ int msm_dsi_host_power_on(struct mipi_dsi_host *host, goto unlock_ret; } - msm_host->byte_intf_clk_rate = msm_host->byte_clk_rate; -if (phy_shared_timings->byte_intf_clk_div_2) - msm_host->byte_intf_clk_rate /=2 ; + msm_host->byte_intf_clk_div_2 = phy_shared_timings->byte_intf_clk_div_2;msm_dsi_sfpb_config (msm_host, true);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 154/438 seg6: set IPSKB_L3SLAVE from IP6SKB_L3SLAVE on IPIP decapsulation
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Andrea Mayer <[email protected]>
[ Upstream commit 7616242a2b37883f7322aaa1d2bd6cd0fed28315 ]
When an SRv6 packet arrives on an interface enslaved to a VRF, vrf_ip6_rcv() sets IP6SKB_L3SLAVE in IP6CB, but decap_and_validate() has never set IPSKB_L3SLAVE in IPCB. The bit stayed clear in the common case, and with CONFIG_IPV6_MIP6 the leftover frag_max_size of a reassembled outer packet could even set it, with no VRF involved. Commit 44930446dde4 ("ipv6: seg6: clear IPv4 control block on IPIP decapsulation") then made the unreliable bit reliably clear.
The effect of the missing flag is visible with End.DX4 when a delivery to a local address of the node reaches the socket lookup. For example, a UDP socket bound to the enslaved ingress interface does not receive any of the decapsulated packets, while an unbound socket outside the VRF does.
This contradicts Documentation/networking/vrf.rst: by default the scope of an unbound UDP or TCP socket is limited to the default VRF.
Set IPSKB_L3SLAVE for IPv4 in decap_and_validate(), which already does the same for IPv6. The socket lookup then matches the decapsulated packet like any other packet received on that enslaved interface. Such a packet matches an unbound UDP or TCP socket only when udp_l3mdev_accept or tcp_l3mdev_accept is set.
--- net/ipv6/seg6_local.c | 3 +++ 1 file changed, 3 insertions(+)
net/ipv6/seg6_local.c +3 -0
@@ -257,10 +257,13 @@ static bool decap_and_validate(struct sk_buff *skb, int proto)return false;if (proto ==IPPROTO_IPIP ) { +bool l3slave =ipv6_l3mdev_skb (IP6CB (skb)->flags);int iif =IP6CB (skb)->iif;memset (IPCB (skb),0 ,sizeof (*IPCB (skb)));IPCB (skb)->iif = iif; +if (l3slave) +IPCB (skb)->flags |=IPSKB_L3SLAVE ; }else if (proto ==IPPROTO_IPV6 ) {bool l3slave =ipv6_l3mdev_skb (IP6CB (skb)->flags);int iif =IP6CB (skb)->iif;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 155/438 net: dsa: mxl862xx: disable the stats poll on teardown
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Daniel Golle <[email protected]>
[ Upstream commit 9e92ad4630f5dd1838ce6bbe6b1bd2c73d34de36 ]
mxl862xx_setup() arms the stats poll before mxl862xx_setup_mdio(), and nothing stops it until dsa_register_switch() has returned an error to mxl862xx_probe(). DSA frees the dsa_port list before it returns, so a poll that fires once .setup or a later step of dsa_tree_setup() has failed walks freed ports. On shutdown the user ports stay registered, and the WORK_STOPPED flag test in mxl862xx_get_stats64() is not atomic with the cancel in mxl862xx_shutdown(), so a re-arm that read the flag before it was set queues the poll after cancel_delayed_work_sync() has returned.
Arm the poll once .setup has succeeded and stop it from a .teardown op, which DSA calls on unregister and after a failed registration, in both cases before it frees the ports. Use disable_delayed_work_sync() there and in shutdown(): it drains a running poll as the cancel did and turns every later attempt to queue the work into a no-op, so the re-arm cannot bring the poll back. remove() and the probe error path only set WORK_STOPPED, which crc_err_work tests before it walks the ports.
--- drivers/net/dsa/mxl862xx/mxl862xx.c | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-)
drivers/net/dsa/mxl862xx/mxl862xx.c +16 -7
@@ -685,10 +685,22 @@ static int mxl862xx_setup(struct dsa_switch *ds)if (ret)return ret; + ret =mxl862xx_setup_mdio (ds); +if (ret) +return ret; +schedule_delayed_work (&priv->stats_work,MXL862XX_STATS_POLL_INTERVAL ); -return mxl862xx_setup_mdio (ds); +return 0 ; +} + +static void mxl862xx_teardown (struct dsa_switch *ds) +{ +struct mxl862xx_priv *priv = ds->priv; + +set_bit (MXL862XX_FLAG_WORK_STOPPED , &priv->flags); +disable_delayed_work_sync (&priv->stats_work); }static int mxl862xx_port_state (struct dsa_switch *ds,int port,bool enable)
@@ -2047,9 +2059,7 @@ static void mxl862xx_get_stats64(struct dsa_switch *ds, int port,spin_unlock_bh (&priv->ports[port].stats_lock); -/* Trigger a fresh poll so the next read sees up-to-date counters. -* No-op if the work is already pending, running, or teardown started. -*/ +/* Trigger a fresh poll so the next read sees up-to-date counters. */ if (!test_bit (MXL862XX_FLAG_WORK_STOPPED , &priv->flags))schedule_delayed_work (&priv->stats_work,0 ); }
@@ -2057,6 +2067,7 @@ static void mxl862xx_get_stats64(struct dsa_switch *ds, int port,static const struct dsa_switch_ops mxl862xx_switch_ops = { .get_tag_protocol = mxl862xx_get_tag_protocol, .setup = mxl862xx_setup, + .teardown = mxl862xx_teardown, .port_setup = mxl862xx_port_setup, .port_teardown = mxl862xx_port_teardown, .phylink_get_caps = mxl862xx_phylink_get_caps,
@@ -2131,7 +2142,6 @@ static int mxl862xx_probe(struct mdio_device *mdiodev) err =dsa_register_switch (ds);if (err) {set_bit (MXL862XX_FLAG_WORK_STOPPED , &priv->flags); -cancel_delayed_work_sync (&priv->stats_work);mxl862xx_host_shutdown (priv);for (i =0 ; i <MXL862XX_MAX_PORTS ; i++)cancel_work_sync (&priv->ports[i].host_flood_work);
@@ -2152,7 +2162,6 @@ static void mxl862xx_remove(struct mdio_device *mdiodev) priv = ds->priv;set_bit (MXL862XX_FLAG_WORK_STOPPED , &priv->flags); -cancel_delayed_work_sync (&priv->stats_work);dsa_unregister_switch (ds);
@@ -2181,7 +2190,7 @@ static void mxl862xx_shutdown(struct mdio_device *mdiodev)dsa_switch_shutdown (ds);set_bit (MXL862XX_FLAG_WORK_STOPPED , &priv->flags); -cancel_delayed_work_sync (&priv->stats_work); +disable_delayed_work_sync (&priv->stats_work);mxl862xx_host_shutdown (priv);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 156/438 net: bcmgenet: restore the hardware filters on open
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Nicolai Buchwitz <[email protected]>
[ Upstream commit 23ca4ddc4fce2c233a49e9fd34d4b5b02bd7324e ]
bcmgenet_hfb_init() runs INIT_LIST_HEAD() on priv->rxnfc_list, which drops every rule off the list, and bcmgenet_open() calls it on each ifup. Every rule the user configured is silently lost:
# ethtool -N eth0 flow-type ether dst $MAC action 0 Added rule with ID 0 # ethtool -n eth0 | grep -c Filter: 1 # ip link set eth0 down && ip link set eth0 up # ethtool -n eth0 | grep -c Filter: 0
Initialise the lists once at probe and restore the rules on open, as bcmgenet_resume() already does.
--- .../net/ethernet/broadcom/genet/bcmgenet.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-)
drivers/net/ethernet/broadcom/genet/bcmgenet.c +13 -6
@@ -749,8 +749,17 @@ static void bcmgenet_hfb_init(struct bcmgenet_priv *priv)INIT_LIST_HEAD (&priv->rxnfc_rules[i].list); priv->rxnfc_rules[i].state =BCMGENET_RXNFC_STATE_UNUSED ; } +} + +static void bcmgenet_hfb_restore (struct bcmgenet_priv *priv) +{ +struct bcmgenet_rxnfc_rule *rule;bcmgenet_hfb_clear (priv); + +list_for_each_entry (rule, &priv->rxnfc_list, list) +if (rule->state !=BCMGENET_RXNFC_STATE_UNUSED ) +bcmgenet_hfb_create_rxnfc_filter (priv, rule); }static int bcmgenet_begin (struct net_device *dev)
@@ -3376,8 +3385,8 @@ static int bcmgenet_open(struct net_device *dev)bcmgenet_set_hw_addr (priv, dev->dev_addr); -/* HFB init */ -bcmgenet_hfb_init (priv); +/* Restore the filters, the MAC was reset above */ +bcmgenet_hfb_restore (priv);/* Reinitialize TDMA and RDMA and SW housekeeping */ ret =bcmgenet_init_dma (priv, true);
@@ -4075,6 +4084,7 @@ static int bcmgenet_probe(struct platform_device *pdev)/* Mii wait queue */ init_waitqueue_head (&priv->wq); +bcmgenet_hfb_init (priv);INIT_WORK (&priv->bcmgenet_irq_work, bcmgenet_irq_task); priv->clk_wol =devm_clk_get_optional (&priv->pdev->dev,"enet-wol" );
@@ -4272,10 +4282,7 @@ static int bcmgenet_resume(struct device *d)bcmgenet_set_hw_addr (priv, dev->dev_addr);/* Restore hardware filters */ -bcmgenet_hfb_clear (priv); -list_for_each_entry (rule, &priv->rxnfc_list, list) -if (rule->state !=BCMGENET_RXNFC_STATE_UNUSED ) -bcmgenet_hfb_create_rxnfc_filter (priv, rule); +bcmgenet_hfb_restore (priv);/* Reinitialize TDMA and RDMA and SW housekeeping */ ret =bcmgenet_init_dma (priv, false);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 157/438 sysctl: Check range in proc_dointvec_ms_jiffies_minmax
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kuniyuki Iwashima <[email protected]>
[ Upstream commit 82431877d837a6c2593efd8e66ba28b35a220ca4 ]
Add the range check to do_proc_int_conv_ms_jiffies_minmax that commit d174174c6776 ("sysctl: replace SYSCTL_INT_CONV_CUSTOM macro with functions") incorrectly removed.
--- kernel/time/jiffies.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
kernel/time/jiffies.c +1 -1
@@ -181,7 +181,7 @@ static int do_proc_int_conv_ms_jiffies_minmax(bool *negp, ulong *u_ptr, int *k_ptr, int dir, const struct ctl_table *tbl) { - return proc_int_conv(negp, u_ptr, k_ptr, dir, tbl, false, + return proc_int_conv(negp, u_ptr, k_ptr, dir, tbl, true,sysctl_u2k_int_conv_ms ,sysctl_k2u_int_conv_ms ); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 158/438 ipv4: icmp: reject RTN_UNREACHABLE input routes in icmp_route_lookup
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Dong Chenchen <[email protected]>
[ Upstream commit 2998147b59c9df0a51477c7a6b3d1f0ba3127dd4 ]
When the forward output route cannot be used in icmp_route_lookup(), it enters the "reverse path" and calls ip_route_input() on fl4_dec.daddr, the original packet's source address.
ip_route_input() only returns an error for truly invalid packets. For unreachable addresses it will succeed and return an input route whose dst.output is set to ip_rt_bug(). The existing check only rejects RTN_LOCAL routes, so the RTN_UNREACHABLE route types can still be returned and later used for output, syzkaller triggering a WARN_ON_ONCE() in ip_rt_bug() as bellow:
------------[ cut here ]------------ WARNING: net/ipv4/route.c:1273 at ip_rt_bug+0x14/0x20 RIP: 0010:ip_rt_bug+0x14/0x20 Call Trace: ip_push_pending_frames+0xfa/0x100 __icmp_send+0x905/0xf10 ip_options_compile+0xc0/0xd0 ip_rcv_finish_core+0x321/0xae0 ip_rcv+0x1de/0x260 __netif_receive_skb_one_core+0x11a/0x130 netif_receive_skb+0x7b/0x260 tun_get_user+0x11bf/0x1c10 ------------[ cut here ]------------
Reject input route that is RTN_UNREACHABLE to fix it. The net warning is only printed for RTN_LOCAL, as RTN_UNREACHABLE is not the result of a race condition.
--- net/ipv4/icmp.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-)
net/ipv4/icmp.c +10 -7
@@ -581,16 +581,19 @@ static struct rtable *icmp_route_lookup(struct net *net, struct flowi4 *fl4,skb_dstref_restore (skb_in, orefdst);/* -* At this point, fl4_dec.daddr should NOT be local (we -* checked fl4_dec.saddr above). However, a race condition -* may occur if the address is added to the interface -* concurrently. In that case, ip_route_input() returns a -* LOCAL route with dst.output=ip_rt_bug, which must not -* be used for output. +* fl4_dec.daddr is not expected to be local here, but it can be +* added to an interface concurrently, in which case +* ip_route_input() returns a LOCAL route. It can also fail to +* build a forwarding route towards fl4_dec.daddr, for example, +* when forwarding is disabled, and return an UNREACHABLE route. +* Both cases will result in a route with dst.output=ip_rt_bug, +* which must not be used for output. */ -if (!err && rt2 && rt2->rt_type ==RTN_LOCAL ) { +if (!err && rt2 && rt2->rt_type ==RTN_LOCAL )net_warn_ratelimited ("detected local route for %pI4 during ICMP sending, src %pI4\n" , &fl4_dec.daddr, &fl4_dec.saddr); +if (!err && rt2 && + (rt2->rt_type ==RTN_LOCAL || rt2->rt_type ==RTN_UNREACHABLE )) {dst_release (&rt2->dst); err = -EINVAL ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 159/438 net: fddi: skfp: fix NULL deref when setting the MAC address while down
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Hohyun Sim <[email protected]>
[ Upstream commit 7c8810c2e69c3d9ca6df870b40ae9218e50b4fb1 ]
skfp_ctl_set_mac_address() calls ResetAdapter() unconditionally, without checking netif_running(). ResetAdapter() first calls card_stop(), which sets smc->hw.hw_state to STOPPED, and then mac_drv_clear_tx_queue(), which walks the two transmit queues:
for (i = QUEUE_S; i <= QUEUE_A0; i++) {
queue = smc->hw.fp.tx[i] ;
...
t = queue->tx_curr_get ;smc->hw.fp.tx[] is only populated by init_tx(), which is reached from skfp_open() through init_smt() -> init_fddi_driver() -> init_fplus() -> init_mac() -> init_tx(). The private area is allocated and zeroed by alloc_fddidev(), so on an interface that has never been brought up both queue pointers are still NULL. The hw_state test at the top of mac_drv_clear_tx_queue() does not catch this, because card_stop() has just set STOPPED; the function proceeds into the loop and dereferences NULL. ResetAdapter() does call init_smt() itself, but only after the queues have been cleared.
Setting the MAC address on a down interface therefore oopses:
ip link set dev fddi0 address 02:00:00:00:00:01
BUG: KASAN: null-ptr-deref in mac_drv_clear_tx_queue+0x68/0x2c0 [skfp] Read of size 8 at addr 0000000000000010 by task ip/302 Call Trace: <TASK> mac_drv_clear_tx_queue+0x68/0x2c0 [skfp 6c01d4bab63c36978bd0a7d7e90837adb44cc37b] ResetAdapter+0x29/0x100 [skfp 6c01d4bab63c36978bd0a7d7e90837adb44cc37b] skfp_ctl_set_mac_address+0x57/0x80 [skfp 6c01d4bab63c36978bd0a7d7e90837adb44cc37b] netif_set_mac_address+0x1e4/0x2c0 do_setlink+0x684/0x2680 </TASK>
Address 0x10 is the offset of tx_curr_get, the third pointer in struct s_smt_tx_queue, on 64-bit. mac_drv_clear_rx_queue(), which ResetAdapter() calls immediately afterwards, dereferences smc->hw.fp.rx[QUEUE_R1] in the same way behind the same ineffective hw_state test; the transmit queue merely crashes first. Both are covered by the guard below.
Skip the adapter reset when the interface is down. dev_addr_set() is left unconditional, so the new address is still recorded in dev->dev_addr. Nothing is lost by not resetting the adapter here:skfp_open() deliberately re-reads the factory address on every open,
read_address(smc, NULL); eth_hw_addr_set(dev, smc->hw.fddi_canon_addr.a);
and the comment above it states this is done to discard exactly such an address override across a close/open cycle. An address set while the interface is down could not have survived the following open even before this change, so the guard removes no working behaviour. Guarding the hardware side of ndo_set_mac_address() with netif_running() is established practice; skge_set_mac_address() has done so since commit 2eb3e621c4e0 ("skge: set mac address bonding fix").
Guarding the reset as a whole, rather than NULL-checking the queues, is also what the rest of the driver expects. After a previous open/close the queue pointers are stale but non-NULL, so there is no crash, yet ResetAdapter() goes on to call smt_online() and STI_FBI() ("Enable Board Interrupts") while skfp_close() has already called free_irq() - the adapter would be brought back online with no handler installed. The only other ResetAdapter() caller is skfp_interrupt(), which by construction runs only while the device is open.
Found by automated driver testing against an emulated SysKonnect FDDI adapter under a KASAN-enabled 7.0.0 kernel. Triggering it requires CAP_NET_ADMIN.
--- drivers/net/fddi/skfp/skfddi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/net/fddi/skfp/skfddi.c +2 -1
@@ -928,7 +928,8 @@ static int skfp_ctl_set_mac_address(struct net_device *dev, void *addr)dev_addr_set (dev, p_sockaddr->sa_data);spin_lock_irqsave (&bp->DriverLock, Flags); -ResetAdapter (smc); +if (netif_running (dev)) +ResetAdapter (smc);spin_unlock_irqrestore (&bp->DriverLock, Flags);return 0 ;/* always return zero */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 160/438 wifi: brcmfmac: fix lost 802.1x TX completion wakeup
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Karl Mehltretter <[email protected]>
[ Upstream commit 621d90169cef6c8da5b6134db5c0c4e23cdd09ce ]
brcmf_txfinalize() decrements pend_8021x_cnt before a lockless waitqueue_active() check. atomic_dec() does not order the decrement against the check.
The waiter can therefore observe a nonzero count while the waker observes an empty queue, losing the final wakeup and delaying key installation until the 950 ms timeout.
Add smp_mb__after_atomic() to order the decrement before the queue check. wait_event_timeout() provides the matching barrier. LKMM confirms that this forbids the lost-wakeup outcome.
--- drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c | 2 ++ 1 file changed, 2 insertions(+)
drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c +2 -0
@@ -555,6 +555,8 @@ void brcmf_txfinalize(struct brcmf_if *ifp, struct sk_buff *txp, bool success)if (type ==ETH_P_PAE ) {atomic_dec (&ifp->pend_8021x_cnt); +/* Order the decrement before waitqueue_active() */ +smp_mb__after_atomic ();if (waitqueue_active (&ifp->pend_8021x_wait))wake_up (&ifp->pend_8021x_wait); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 161/438 net: bridge: mst: move switchdev call outside rcu
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Nikolay Aleksandrov <[email protected]>
[ Upstream commit 18a6fe05fb6e18de29fa90d388bb34044114b3d8 ]
This is a follow-up of one of sashiko's pre-existing bug reports. br_mst_set_state() calls switchdev_port_attr_set() for nonzero MSTIs while holding rcu_read_lock() which invokes the blocking switchdev notifier chain and may sleep. Nonzero MSTI changes come from netlink with rtnl held. Move the switchdev call before entering the rcu section and assert that rtnl is held.
The call cannot be deferred because netlink needs its error and extack. Also DSA reads the old bridge MST state during the callback and checks it. A deferred callback will be late and will see the updated state.
--- net/bridge/br_mst.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-)
net/bridge/br_mst.c +12 -8
@@ -107,21 +107,24 @@ int br_mst_set_state(struct net_bridge_port *p, u16 msti, u8 state,struct net_bridge_vlan *v;int err =0 ; -rcu_read_lock (); - vg =nbp_vlan_group_rcu (p); -if (!vg) - goto out; -/* MSTI 0 (CST) state changes are notified via the regular -* SWITCHDEV_ATTR_ID_PORT_STP_STATE. +* SWITCHDEV_ATTR_ID_PORT_STP_STATE. All other MSTIs are handled via +* netlink with RTNL held */ if (msti) { +ASSERT_RTNL (); + err =switchdev_port_attr_set (p->dev, &attr, extack);if (err && err != -EOPNOTSUPP ) goto out; + err =0 ; } - err =0 ; +rcu_read_lock (); + vg =nbp_vlan_group_rcu (p); +if (!vg) + goto out_rcu_unlock; +list_for_each_entry_rcu (v, &vg->vlan_list, vlist) {if (v->brvlan->msti != msti)continue ;
@@ -129,8 +132,9 @@ int br_mst_set_state(struct net_bridge_port *p, u16 msti, u8 state,br_mst_vlan_set_state (vg, v, state); } -out: +out_rcu_unlock:rcu_read_unlock (); +out:return err; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 162/438 tcp: Dont call skb_clone_and_charge_r() for close()d listener in tcp_v6_do_rcv().
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kuniyuki Iwashima <[email protected]>
[ Upstream commit 8e759cd1f6444a946bd1fd2b2b29eea582eea1d5 ]
tcp_v6_do_rcv() no longer calls skb_clone_and_charge_r() for TCP_LISTEN since commit 073d89808c06 ("net: fix data-races around sk->sk_forward_alloc").
However, there is still a small race window between tcp_v6_rcv() and tcp_v6_do_rcv(), where concurrent close() changes TCP_LISTEN to TCP_CLOSE, causing skb_clone_and_charge_r() to be called locklessly and resulting in the splat below. [0]
Let's avoid calling skb_clone_and_charge_r() for TCP_CLOSE as well.
This is fine for non-listeners because tcp_rcv_state_process() drops skb for TCP_CLOSE and opt_skb was freed immediately anyway.
[0]:sk->sk_forward_alloc
WARNING: net/ipv4/af_inet.c:162 at inet_sock_destruct+0x64d/0x810 net/ipv4/af_inet.c:162, CPU#1: ksoftirqd/1/28 Modules linked in: CPU: 1 UID: 0 PID: 28 Comm: ksoftirqd/1 Not tainted 7.2.0 #17 PREEMPT(full) Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.17.0-debian-1.17.0-1 04/01/2014 RIP: 0010:inet_sock_destruct+0x64d/0x810 net/ipv4/af_inet.c:162 Code: 3d 49 ff e9 06 fd ff ff e8 d0 5b 83 f8 90 0f 0b 90 e9 35 fe ff ff e8 c2 5b 83 f8 90 0f 0b 90 e9 c5 fe ff ff e8 b4 5b 83 f8 90 <0f> 0b 90 e9 04 ff ff ff e8 a6 5b 83 f8 90 0f 0b 90 e9 65 fe ff ff RSP: 0018:ffffc90000677bb8 EFLAGS: 00010246 RAX: 0000000000000000 RBX: ffff8880117bde80 RCX: ffffffff8957eb41 RDX: ffff88801dad5d00 RSI: ffffffff8957ec3c RDI: 0000000000000005 RBP: 00000000fffff000 R08: ffffffff8957eb41 R09: 00000000fffff000 R10: 0000000000000005 R11: 0000000000000000 R12: dffffc0000000000 R13: ffff8880117bdf10 R14: ffffffff81c08eb7 R15: 0000000000000003 FS: 0000000000000000(0000) GS:ffff8880d7ae5000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00007f93a1021138 CR3: 00000000207a9000 CR4: 0000000000350ef0 Call Trace: <TASK> __sk_destruct+0x82/0xae0 net/core/sock.c:2356 rcu_do_batch kernel/rcu/tree.c:2645 [inline] rcu_core+0x59c/0x1100 kernel/rcu/tree.c:2897 handle_softirqs+0x1e4/0x9b0 kernel/softirq.c:622 run_ksoftirqd kernel/softirq.c:1076 [inline] run_ksoftirqd+0x38/0x60 kernel/softirq.c:1068 smpboot_thread_fn+0x458/0xc80 kernel/smpboot.c:160 kthread+0x396/0x4a0 kernel/kthread.c:436 ret_from_fork+0x8e0/0xe40 arch/x86/kernel/process.c:158 ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245 </TASK>
--- net/ipv6/tcp_ipv6.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
net/ipv6/tcp_ipv6.c +2 -1
@@ -1604,7 +1604,8 @@ int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb)by tcp. Feel free to propose better solution. --ANK (980728) */ -if (np->rxopt.all && sk->sk_state !=TCP_LISTEN ) +if (np->rxopt.all && + !((1 << sk->sk_state) & (TCPF_LISTEN |TCPF_CLOSE ))) opt_skb =skb_clone_and_charge_r (skb, sk);if (sk->sk_state ==TCP_ESTABLISHED ) {/* Fast path */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 163/438 tcp: do not let tcp_rmem be set below 4096
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit 83a945a529d6e002dd7339c532288a931f463dba ]
We can hit a division by zero crash in tcp_rcvbuf_grow() and tcp_rcv_space_adjust():
divide error: 0000 [#1] PREEMPT SMP
RIP: 0010:tcp_rcvbuf_grow+0x187/0x450 net/ipv4/tcp_input.c:939
...
grow = div_u64(((u64)rcvwin << 1) * (newval - oldval), oldval);
The division uses oldval = tp->rcvq_space.space as divisor. When tp->rcvq_space.space is zero, this leads to a divide-by-zero exception.
tp->rcvq_space.space is initialized in tcp_init_buffer_space():
tp->rcvq_space.space = min3(tp->rcv_ssthresh, tp->rcv_wnd,
(u32)TCP_INIT_CWND * tp->advmss);If tcp_rmem[1] is configured to very small values (such as 1), sk->sk_rcvbuf is initialized to 1. Then tcp_full_space(sk), which computes (sk->sk_rcvbuf * scaling_ratio) >> 8, truncates to 0. This sets tp->window_clamp = 0, tp->rcv_ssthresh = 0, and tp->rcvq_space.space = 0. Later, when data arrives and DRS is invoked, tcp_rcvbuf_grow() divides by oldval == 0.
Back in 2015, commit b1cb59cf2efe ("net: sysctl_net_core: check SNDBUF and RCVBUF for min length") ensured that net.core.rmem_default and net.core.rmem_max cannot be set below SOCK_MIN_RCVBUF. Similarly, SO_RCVBUF setsockopt enforces max_t(int, val * 2, SOCK_MIN_RCVBUF).
However, net.ipv4.tcp_rmem still had .extra1 = SYSCTL_ONE, allowing arbitrarily small values.
Because SOCK_MIN_RCVBUF depends on sizeof(struct sk_buff) and cacheline alignment, its value varies across architectures and configuration options. Using a fixed constant of 4096 ensures a predictable, architecture- independent lower bound that is safely above SOCK_MIN_RCVBUF everywhere and matches the documented 4K default.
Fix this by setting tcp_rmem.extra1 to 4096 and updating the documentation.
--- Documentation/networking/ip-sysctl.rst | 2 ++ net/ipv4/sysctl_net_ipv4.c | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-)
Documentation/networking/ip-sysctl.rst +2 -0
@@ -874,6 +874,8 @@ tcp_rmem - vector of 3 INTEGERs: min, default, max case this value is ignored. Default: between 131072 and 32MB, depending on RAM size. + Each of the three values cannot be set below 4096. + tcp_sack - BOOLEAN Enable select acknowledgments (SACKS).
net/ipv4/sysctl_net_ipv4.c +3 -1
@@ -51,6 +51,8 @@ static int tcp_ecn_mode_max = 5;static u32 icmp_errors_extension_mask_all =GENMASK_U8 (ICMP_ERR_EXT_COUNT -1 ,0 ); +static int tcp_min_rcvbuf =4096 ; +/* obsolete */ static int sysctl_tcp_low_latency__read_mostly ;
@@ -1462,7 +1464,7 @@ static struct ctl_table ipv4_net_table[] = { .maxlen = sizeof(init_net.ipv4.sysctl_tcp_rmem), .mode = 0644, .proc_handler = proc_dointvec_minmax, - .extra1 = SYSCTL_ONE, + .extra1 = &tcp_min_rcvbuf, }, { .procname = "tcp_comp_sack_delay_ns",
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 164/438 ksmbd: return buffer overflow for partial filesystem info
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Namjae Jeon <[email protected]>
[ Upstream commit 0ecd35fac4b4f2828490689b46039744d201dcb0 ]
The query-info buffer check returns STATUS_INFO_LENGTH_MISMATCH for every output buffer smaller than the complete response. Variable-length filesystem information instead requires STATUS_BUFFER_OVERFLOW when the fixed portion fits but the complete data does not.
Pass the fixed size for each filesystem information class to the buffer checker. Keep INFO_LENGTH_MISMATCH for buffers below that size, and return BUFFER_OVERFLOW with a response truncated to the requested length for larger partial buffers.
This fixes smb2.getinfo.qfs_buffercheck.
Stable-dep-of: 9fa26285ae70 ("ksmbd: keep compound responses on query info errors")
--- fs/smb/server/smb2pdu.c | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-)
fs/smb/server/smb2pdu.c +25 -1
@@ -5622,21 +5622,30 @@ int smb2_query_dir(struct ksmbd_work *work)/** * buffer_check_err() - helper function to check buffer errors * @reqOutputBufferLength: max buffer length expected in command response +* @fixed_len: minimum fixed response length * @rsp: query info response buffer contains output buffer length * @rsp_org: base response buffer pointer in case of chained response * * Return: 0 on success, otherwise error */ static int buffer_check_err (int reqOutputBufferLength, +unsigned int fixed_len,struct smb2_query_info_rsp *rsp,void *rsp_org) { -if (reqOutputBufferLength <le32_to_cpu (rsp->OutputBufferLength)) { +unsigned int output_len =le32_to_cpu (rsp->OutputBufferLength); + +if (reqOutputBufferLength < fixed_len) {pr_err ("Invalid Buffer Size Requested\n" ); rsp->hdr.Status =STATUS_INFO_LENGTH_MISMATCH ; *(__be32 *)rsp_org =cpu_to_be32 (sizeof (struct smb2_hdr ));return -EINVAL ; } + +if (reqOutputBufferLength < output_len) { + rsp->hdr.Status =STATUS_BUFFER_OVERFLOW ; + rsp->OutputBufferLength =cpu_to_le32 (reqOutputBufferLength); + }return 0 ; }
@@ -5699,11 +5708,13 @@ static int smb2_get_info_file_pipe(struct ksmbd_session *sess,case FILE_STANDARD_INFORMATION :get_standard_info_pipe (rsp, rsp_org); rc =buffer_check_err (le32_to_cpu (req->OutputBufferLength), +le32_to_cpu (rsp->OutputBufferLength), rsp, rsp_org);break ;case FILE_INTERNAL_INFORMATION :get_internal_info_pipe (rsp, id, rsp_org); rc =buffer_check_err (le32_to_cpu (req->OutputBufferLength), +le32_to_cpu (rsp->OutputBufferLength), rsp, rsp_org);break ;default :
@@ -6524,6 +6535,7 @@ static int smb2_get_info_file(struct ksmbd_work *work, }if (!rc) rc =buffer_check_err (le32_to_cpu (req->OutputBufferLength), +le32_to_cpu (rsp->OutputBufferLength), rsp, work->response_buf);ksmbd_fd_put (work, fp);
@@ -6545,6 +6557,7 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work,struct kstatfs stfs;struct path path;int rc =0 , len; +unsigned int fixed_len =0 ;if (!share->path)return -EIO ;
@@ -6579,6 +6592,7 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, info->DeviceCharacteristics |=cpu_to_le32 (FILE_READ_ONLY_DEVICE ); rsp->OutputBufferLength =cpu_to_le32 (8 ); + fixed_len =8 ;break ; }case FS_ATTRIBUTE_INFORMATION :
@@ -6631,6 +6645,7 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, info->FileSystemNameLen =cpu_to_le32 (len); sz =sizeof (FILE_SYSTEM_ATTRIBUTE_INFO ) + len; rsp->OutputBufferLength =cpu_to_le32 (sz); + fixed_len =16 ;break ; }case FS_VOLUME_INFORMATION :
@@ -6658,6 +6673,7 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, info->SupportsObjects =0 ; sz =sizeof (struct filesystem_vol_info ) + len; rsp->OutputBufferLength =cpu_to_le32 (sz); + fixed_len =24 ;break ; }case FS_SIZE_INFORMATION :
@@ -6670,6 +6686,7 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, info->SectorsPerAllocationUnit =cpu_to_le32 (1 ); info->BytesPerSector =cpu_to_le32 (stfs.f_bsize); rsp->OutputBufferLength =cpu_to_le32 (24 ); + fixed_len =24 ;break ; }case FS_FULL_SIZE_INFORMATION :
@@ -6685,6 +6702,7 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, info->SectorsPerAllocationUnit =cpu_to_le32 (1 ); info->BytesPerSector =cpu_to_le32 (stfs.f_bsize); rsp->OutputBufferLength =cpu_to_le32 (32 ); + fixed_len =32 ;break ; }case FS_OBJECT_ID_INFORMATION :
@@ -6705,6 +6723,7 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, info->extended_info.rel_date =0 ;memcpy (info->extended_info.version_string,"1.1.0" ,strlen ("1.1.0" )); rsp->OutputBufferLength =cpu_to_le32 (64 ); + fixed_len =64 ;break ; }case FS_SECTOR_SIZE_INFORMATION :
@@ -6726,6 +6745,7 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, info->ByteOffsetForSectorAlignment =0 ; info->ByteOffsetForPartitionAlignment =0 ; rsp->OutputBufferLength =cpu_to_le32 (28 ); + fixed_len =28 ;break ; }case FS_CONTROL_INFORMATION :
@@ -6746,6 +6766,7 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, info->DefaultQuotaLimit =cpu_to_le64 (SMB2_NO_FID ); info->Padding =0 ; rsp->OutputBufferLength =cpu_to_le32 (48 ); + fixed_len =48 ;break ; }case FS_POSIX_INFORMATION :
@@ -6766,6 +6787,7 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, info->TotalFileNodes =cpu_to_le64 (stfs.f_files); info->FreeFileNodes =cpu_to_le64 (stfs.f_ffree); rsp->OutputBufferLength =cpu_to_le32 (56 ); + fixed_len =56 ; }break ; }
@@ -6774,6 +6796,7 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work,return -EOPNOTSUPP ; } rc =buffer_check_err (le32_to_cpu (req->OutputBufferLength), + fixed_len, rsp, work->response_buf);path_put (&path);
@@ -6875,6 +6898,7 @@ static int smb2_get_info_sec(struct ksmbd_work *work, rsp->OutputBufferLength =cpu_to_le32 (secdesclen); rc =buffer_check_err (le32_to_cpu (req->OutputBufferLength), +le32_to_cpu (rsp->OutputBufferLength), rsp, work->response_buf);if (rc) goto err_out;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 165/438 ksmbd: fix partial file information responses
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Namjae Jeon <[email protected]>
[ Upstream commit 6b8b79226bc3e0ac3fdd4e91836241af712e8cd1 ]
Variable-length file information handlers use the client output length while constructing the response. FILE_ALL_INFORMATION can consequently return -EINVAL before the common buffer check, while stream information can stop building the complete result too early.
Build the complete response within the available server response buffer and apply the client output length only when selecting the final status and transmitted length. Use the protocol-defined fixed sizes for all, alternate-name, and stream information to distinguish STATUS_INFO_LENGTH_MISMATCH from STATUS_BUFFER_OVERFLOW.
This fixes smb2.getinfo.qfile_buffercheck.
Stable-dep-of: 9fa26285ae70 ("ksmbd: keep compound responses on query info errors")
--- fs/smb/server/smb2pdu.c | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-)
fs/smb/server/smb2pdu.c +20 -11
@@ -5997,7 +5997,6 @@ static int get_file_all_info(struct ksmbd_work *work,char *filename;u64 time;int ret, buf_free_len, filename_len; -struct smb2_query_info_req *req =ksmbd_req_buf_next (work);if (!(fp->daccess &FILE_READ_ATTRIBUTES_LE )) { ksmbd_debug(SMB, "no right to read the attributes : 0x%x\n",
@@ -6010,10 +6009,9 @@ static int get_file_all_info(struct ksmbd_work *work,return PTR_ERR (filename); filename_len =strlen (filename); - buf_free_len =smb2_calc_max_out_buf_len (work, + buf_free_len =smb2_resp_buf_len (work, offsetof(struct smb2_query_info_rsp , Buffer) + - offsetof(struct smb2_file_all_info , FileName), -le32_to_cpu (req->OutputBufferLength)); + offsetof(struct smb2_file_all_info , FileName));if (buf_free_len < (filename_len +1 ) *2 ) {kfree (filename);return -EINVAL ;
@@ -6104,7 +6102,6 @@ static int get_file_stream_info(struct ksmbd_work *work,ssize_t xattr_list_len;int nbytes =0 , streamlen, stream_name_len, next, idx =0 ;int buf_free_len; -struct smb2_query_info_req *req =ksmbd_req_buf_next (work);int ret; ret = vfs_getattr(&fp->filp->f_path, &stat, STATX_BASIC_STATS,
@@ -6114,10 +6111,8 @@ static int get_file_stream_info(struct ksmbd_work *work, file_info = (struct smb2_file_stream_info *)rsp->Buffer; - buf_free_len = -smb2_calc_max_out_buf_len (work, - offsetof(struct smb2_query_info_rsp , Buffer), -le32_to_cpu (req->OutputBufferLength)); + buf_free_len =smb2_resp_buf_len (work, + offsetof(struct smb2_query_info_rsp , Buffer));if (buf_free_len <0 ) goto out;
@@ -6430,6 +6425,7 @@ static int smb2_get_info_file(struct ksmbd_work *work,struct ksmbd_file *fp;int fileinfoclass =0 ;int rc =0 ; +unsigned int fixed_len;unsigned int id =KSMBD_NO_FID , pid =KSMBD_NO_FID ; if (test_share_config_flag(work->tcon->share_conf,
@@ -6533,10 +6529,23 @@ static int smb2_get_info_file(struct ksmbd_work *work, fileinfoclass); rc = -EOPNOTSUPP ; } -if (!rc) +if (!rc) { + fixed_len =le32_to_cpu (rsp->OutputBufferLength); +switch (fileinfoclass) { +case FILE_ALL_INFORMATION : + fixed_len =FILE_ALL_INFORMATION_SIZE ; +break ; +case FILE_ALTERNATE_NAME_INFORMATION : + fixed_len =FILE_ALTERNATE_NAME_INFORMATION_SIZE ; +break ; +case FILE_STREAM_INFORMATION : + fixed_len =FILE_STREAM_INFORMATION_SIZE ; +break ; + } rc =buffer_check_err (le32_to_cpu (req->OutputBufferLength), -le32_to_cpu (rsp->OutputBufferLength), + fixed_len, rsp, work->response_buf); + }ksmbd_fd_put (work, fp); iov_pin_out:
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 166/438 ksmbd: keep compound responses on query info errors
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Namjae Jeon <[email protected]>
[ Upstream commit 9fa26285ae70ac2d3d1b47459a6b4463ab053e1c ]
Do not reset the RFC1002 length of the complete response when a query info buffer is too small. The current command will add its error response through ksmbd_iov_pin_rsp(), while resetting the base length can truncate earlier responses in a compound request.
This lets ksmbd return the earlier responses and the query-info error response together. Remove the now-unused rsp_org parameter from the pipe query-info helpers.
--- fs/smb/server/smb2pdu.c | 31 ++++++++++++------------------- 1 file changed, 12 insertions(+), 19 deletions(-)
fs/smb/server/smb2pdu.c +12 -19
@@ -5624,21 +5624,18 @@ int smb2_query_dir(struct ksmbd_work *work)* @reqOutputBufferLength: max buffer length expected in command response * @fixed_len: minimum fixed response length * @rsp: query info response buffer contains output buffer length -* @rsp_org: base response buffer pointer in case of chained response * * Return: 0 on success, otherwise error */ static int buffer_check_err (int reqOutputBufferLength,unsigned int fixed_len, -struct smb2_query_info_rsp *rsp, -void *rsp_org) +struct smb2_query_info_rsp *rsp) {unsigned int output_len =le32_to_cpu (rsp->OutputBufferLength);if (reqOutputBufferLength < fixed_len) {pr_err ("Invalid Buffer Size Requested\n" ); rsp->hdr.Status =STATUS_INFO_LENGTH_MISMATCH ; - *(__be32 *)rsp_org =cpu_to_be32 (sizeof (struct smb2_hdr ));return -EINVAL ; }
@@ -5649,8 +5646,7 @@ static int buffer_check_err(int reqOutputBufferLength,return 0 ; } -static void get_standard_info_pipe (struct smb2_query_info_rsp *rsp, -void *rsp_org) +static void get_standard_info_pipe (struct smb2_query_info_rsp *rsp) {struct smb2_file_standard_info *sinfo;
@@ -5665,8 +5661,7 @@ static void get_standard_info_pipe(struct smb2_query_info_rsp *rsp,cpu_to_le32 (sizeof (struct smb2_file_standard_info )); } -static void get_internal_info_pipe (struct smb2_query_info_rsp *rsp,u64 num, -void *rsp_org) +static void get_internal_info_pipe (struct smb2_query_info_rsp *rsp,u64 num) {struct smb2_file_internal_info *file_info;
@@ -5680,8 +5675,7 @@ static void get_internal_info_pipe(struct smb2_query_info_rsp *rsp, u64 num,static int smb2_get_info_file_pipe (struct ksmbd_session *sess,struct smb2_query_info_req *req, -struct smb2_query_info_rsp *rsp, -void *rsp_org) +struct smb2_query_info_rsp *rsp) {u64 id;int rc;
@@ -5706,16 +5700,16 @@ static int smb2_get_info_file_pipe(struct ksmbd_session *sess,switch (req->FileInfoClass) {case FILE_STANDARD_INFORMATION : -get_standard_info_pipe (rsp, rsp_org); +get_standard_info_pipe (rsp); rc =buffer_check_err (le32_to_cpu (req->OutputBufferLength),le32_to_cpu (rsp->OutputBufferLength), - rsp, rsp_org); + rsp);break ;case FILE_INTERNAL_INFORMATION : -get_internal_info_pipe (rsp, id, rsp_org); +get_internal_info_pipe (rsp, id); rc =buffer_check_err (le32_to_cpu (req->OutputBufferLength),le32_to_cpu (rsp->OutputBufferLength), - rsp, rsp_org); + rsp);break ;default : ksmbd_debug(SMB, "smb2_info_file_pipe for %u not supported\n",
@@ -6431,8 +6425,7 @@ static int smb2_get_info_file(struct ksmbd_work *work,if (test_share_config_flag (work->tcon->share_conf,KSMBD_SHARE_FLAG_PIPE )) {/* smb2 info file called for pipe */ - rc =smb2_get_info_file_pipe (work->sess, req, rsp, - work->response_buf); + rc =smb2_get_info_file_pipe (work->sess, req, rsp); goto iov_pin_out; }
@@ -6544,7 +6537,7 @@ static int smb2_get_info_file(struct ksmbd_work *work, } rc =buffer_check_err (le32_to_cpu (req->OutputBufferLength), fixed_len, - rsp, work->response_buf); + rsp); }ksmbd_fd_put (work, fp);
@@ -6806,7 +6799,7 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work, } rc =buffer_check_err (le32_to_cpu (req->OutputBufferLength), fixed_len, - rsp, work->response_buf); + rsp);path_put (&path);if (!rc)
@@ -6908,7 +6901,7 @@ static int smb2_get_info_sec(struct ksmbd_work *work, rsp->OutputBufferLength =cpu_to_le32 (secdesclen); rc =buffer_check_err (le32_to_cpu (req->OutputBufferLength),le32_to_cpu (rsp->OutputBufferLength), - rsp, work->response_buf); + rsp);if (rc) goto err_out;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 167/438 dmaengine: mmp_pdma: fix wrong sg length in mmp_pdma_prep_slave_sg()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Baineng Shou <[email protected]>
[ Upstream commit 075bc7b1d3dde5ed43fbaabbc1a69f09b7fc3a47 ]
In mmp_pdma_prep_slave_sg(), for_each_sg() iterates the scatterlist putting each entry into 'sg', but the entry length is read from 'sgl' (the list head) instead of 'sg' (the current entry):
for_each_sg(sgl, sg, sg_len, i) {
addr = sg_dma_address(sg);
avail = sg_dma_len(sgl); /* should be 'sg' */Consequently 'avail' is always the length of the first entry. For multi-sg lists this causes out-of-bounds reads when a later entry is shorter than the first, and silent data loss when it is longer. Single-sg or uniformly-sized lists happen to mask the issue.
--- drivers/dma/mmp_pdma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/dma/mmp_pdma.c +1 -1
@@ -712,7 +712,7 @@ mmp_pdma_prep_slave_sg(struct dma_chan *dchan, struct scatterlist *sgl,for_each_sg (sgl, sg, sg_len, i) { addr =sg_dma_address (sg); - avail =sg_dma_len (sgl); + avail =sg_dma_len (sg); do { len = min_t(size_t, avail, PDMA_MAX_DESC_BYTES);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 168/438 Bluetooth: hci_core: Fix queuing tx_work after workqueue is drained
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: ThangNN99 <[email protected]>
[ Upstream commit 6610c6fe4b8936c232048e6049bf77c70a6f759c ]
hci_send_acl(), hci_send_sco() and hci_send_iso() queue hdev->tx_work unconditionally. They can run from the L2CAP/SCO/ISO socket send path while hci_dev_close_sync() is draining hdev->workqueue (HCIDEVDOWN racing with a socket write). Since that queue_work() is not chained work from the tx_work worker itself, __queue_work() sees the queue marked __WQ_DRAINING, warns "cannot queue %ps on wq %s", and drops the work:
WARNING: CPU: 1 PID: 5985 at kernel/workqueue.c:2352 __queue_work Call Trace: queue_work_on l2cap_chan_send l2cap_sock_sendmsg ...
hci_dev_close_sync() already sets HCI_CMD_DRAIN_WORKQUEUE before draining, but only hci_cmd_work() and handle_cmd_cnt_and_timer() check it before queuing. Route the tx_work producers through the same guard via a shared hci_sched_tx() helper.
--- net/bluetooth/hci_core.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-)
net/bluetooth/hci_core.c +14 -3
@@ -3234,6 +3234,17 @@ static void hci_queue_acl(struct hci_chan *chan, struct sk_buff_head *queue,bt_dev_dbg (hdev,"chan %p queued %d" , chan,skb_queue_len (queue)); } +/* Queue hdev->tx_work, unless hdev->workqueue is being drained by +* hci_dev_close_sync(), which would otherwise WARN and drop the work. +*/ +static void hci_sched_tx (struct hci_dev *hdev) +{ +rcu_read_lock (); +if (!hci_dev_test_flag (hdev,HCI_CMD_DRAIN_WORKQUEUE )) +queue_work (hdev->workqueue, &hdev->tx_work); +rcu_read_unlock (); +} +void hci_send_acl (struct hci_chan *chan,struct sk_buff *skb,__u16 flags) {struct hci_dev *hdev = chan->conn->hdev;
@@ -3242,7 +3253,7 @@ void hci_send_acl(struct hci_chan *chan, struct sk_buff *skb, __u16 flags)hci_queue_acl (chan, &chan->data_q, skb, flags); -queue_work (hdev->workqueue, &hdev->tx_work); +hci_sched_tx (hdev); }/* Send SCO data */
@@ -3267,7 +3278,7 @@ void hci_send_sco(struct hci_conn *conn, struct sk_buff *skb)bt_dev_dbg (hdev,"hcon %p queued %d" , conn,skb_queue_len (&conn->data_q)); -queue_work (hdev->workqueue, &hdev->tx_work); +hci_sched_tx (hdev); }/* Send ISO data */
@@ -3338,7 +3349,7 @@ void hci_send_iso(struct hci_conn *conn, struct sk_buff *skb)hci_queue_iso (conn, &conn->data_q, skb); -queue_work (hdev->workqueue, &hdev->tx_work); +hci_sched_tx (hdev); }/* ---- HCI TX task (outgoing data) ---- */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 169/438 Bluetooth: btintel_pcie: validate TX skb length in send_sync
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chandrashekar Devegowda <[email protected]>
[ Upstream commit 4b837ebd0ea21ae5cc26f02dc042edc6fe7b46b9 ]
btintel_pcie_prepare_tx() copies skb->len bytes into a fixed BTINTEL_PCIE_BUFFER_SIZE (4096) DMA slot via an unchecked memcpy. Oversized packets are currently rejected only in btintel_pcie_send_frame(); any future caller of btintel_pcie_send_sync() would silently overflow the DMA buffer.
Add the bounds check in btintel_pcie_send_sync() itself, right before skb_push() and the DMA copy.
--- drivers/bluetooth/btintel_pcie.c | 6 ++++++ 1 file changed, 6 insertions(+)
drivers/bluetooth/btintel_pcie.c +6 -0
@@ -404,6 +404,12 @@ static int btintel_pcie_send_sync(struct btintel_pcie_data *data,if (tfd_index > txq->count)return -ERANGE ; +if (skb->len >BTINTEL_PCIE_BUFFER_SIZE -BTINTEL_PCIE_HCI_TYPE_LEN ) { +bt_dev_err (hdev,"TX skb too large (%u > %u)" , skb->len, +BTINTEL_PCIE_BUFFER_SIZE -BTINTEL_PCIE_HCI_TYPE_LEN ); +return -EMSGSIZE ; + } +/* Firmware raises alive interrupt on HCI_OP_RESET or * BTINTEL_HCI_OP_RESET */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 170/438 Bluetooth: coredump: Quiesce dump work on unregister
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Weiming Shi <[email protected]>
[ Upstream commit d236517c264e41dc09833c708ef23bccb7a91219 ]
hci_devcd_handle_pkt_init() arms dump_timeout and coredump producers queue dump_rx without holding an hdev reference. Unregister leaves both works live, so disconnecting during an active dump lets them access hdev after hci_release_dev() frees it.
Shut down coredump processing during unregister. Close the producer gate under dump_q.lock before disabling both works, then free the active buffer and queued packets under hci_dev_lock. Serializing the gate with enqueue prevents controller-specific workers from adding packets after the final purge.
--- include/net/bluetooth/coredump.h | 2 + net/bluetooth/coredump.c | 65 +++++++++++++++++++++----------- net/bluetooth/hci_core.c | 1 + 3 files changed, 47 insertions(+), 21 deletions(-)
include/net/bluetooth/coredump.h +2 -0
@@ -61,6 +61,7 @@ struct hci_devcoredump {#ifdef CONFIG_DEV_COREDUMP void hci_devcd_reset (struct hci_dev *hdev); +void hci_devcd_shutdown (struct hci_dev *hdev);void hci_devcd_rx (struct work_struct *work);void hci_devcd_timeout (struct work_struct *work);
@@ -75,6 +76,7 @@ int hci_devcd_abort(struct hci_dev *hdev);#else static inline void hci_devcd_reset (struct hci_dev *hdev) {} +static inline void hci_devcd_shutdown (struct hci_dev *hdev) {}static inline void hci_devcd_rx (struct work_struct *work) {}static inline void hci_devcd_timeout (struct work_struct *work) {}
net/bluetooth/coredump.c +44 -21
106 lines not on this page · raw message
net/bluetooth/hci_core.c +1 -0
@@ -2670,6 +2670,7 @@ void hci_unregister_dev(struct hci_dev *hdev)disable_work_sync (&hdev->error_reset);disable_delayed_work_sync (&hdev->cmd_timer);disable_delayed_work_sync (&hdev->ncmd_timer); +hci_devcd_shutdown (hdev);hci_cmd_sync_clear (hdev);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 171/438 Bluetooth: put the peers on-air address on air when we cannot resolve
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Radek Podgorny <[email protected]>
[ Upstream commit 4914c499896121ae8b9d5b90f0abc5c8287ff396 ]
An identity address only reaches a peer that is advertising an RPA if the controller resolves it on our behalf. Where it cannot, the host has to put the peer's on-air address on air itself.
hci_connect_le() still swaps the caller's identity address for the peer's cached RPA before creating the connection, but __hci_conn_add() resolves the RPA back to the identity address when it stores it, so the identity is what goes out. Storing the identity is right when the controller translates it on the way to the radio; without LL Privacy, or with this peer absent from the resolving list, nothing does.
A peer advertising an RPA cannot answer its identity address, so the attempt burns a full create-connection timeout. That is not merely a slow
connect: a controller without extended scanning cannot scan while it is initiating, so every dead attempt also takes the scanner off the air for the whole timeout.
Measured on a CYW43438, which reports neither LL Privacy nor extended advertising (LE features 3f 00 00 08 00 00 00 00), against a peer advertising a resolvable private address the host holds the IRK for, with the connection requested on the peer's identity address:
before: LE Create Connection to the identity address, public type
1.61s -> 22.07s, then LE Create Connection Cancel
LE Connection Complete: Unknown Connection Identifier (0x02) after: LE Create Connection to the peer's RPA, random type
LE Connection Complete: Success
Advertising reports reaching the host per second, same window, same five unrelated devices on the adapter:
before 1s:2 [nothing from 2s through 21s] 22s:5 23s:3
after 0s:11 1s:5 2s:2 3s:5 4s:3 5s:4 ... 21s:2 22s:1 23s:2
One dead connect costs twenty seconds of scanning for every device on the adapter, not just the one being dialled.
Keep the RPA in conn->dst unless the controller will translate the identity address: address resolution enabled and the peer's identity actually programmed into the resolving list. Testing ll_privacy_capable() alone would not be enough: it reports the feature bit, not whether resolution is switched on and not whether this peer is in the list. Resolution is cleared with the other volatile flags on power-off and switched off again while suspend pauses scanning, and a peer's IRK is only programmed along the accept list path, so a direct-connect target, a peer without HCI_CONN_FLAG_ADDRESS_RESOLUTION, and one that did not fit in a full list are all absent from it.
With the peer programmed, the identity address stays in conn->dst and the controller translates it: measured on an Intel controller, the host dials the identity and LE Enhanced Connection Complete reports Resolved Public with the peer's RPA in the separate peer resolvable private address field. With the peer absent from the list the same setup dials the RPA itself.
Everything downstream already copes with an RPA in conn->dst: it is what every outgoing LE connection stored before 14b06c3a88f7, the connection complete event names the address that was dialled, and le_conn_complete_evt() resolves it back to the identity once the link is up. ISO links keep the unconditional conversion: they are created from an existing ACL or a periodic sync and never dial this address themselves.
Keeping the RPA is only right while the peer is still using it, which is why the preceding patch drops the cached RPA as soon as the peer is seen advertising its identity address. Without that, a peer that turns privacy off would be dialled on the address it abandoned rather than the one it is answering on.
--- net/bluetooth/hci_conn.c | 13 +++++++++++++ 1 file changed, 13 insertions(+)
net/bluetooth/hci_conn.c +13 -0
@@ -1023,6 +1023,19 @@ static struct hci_conn *__hci_conn_add(struct hci_dev *hdev, int type,if (!hdev->le_mtu && hdev->acl_mtu <HCI_MIN_LE_MTU )return ERR_PTR (-ECONNREFUSED ); irk =hci_get_irk (hdev, dst, dst_type); +/* An identity address only reaches a peer advertising an RPA +* if the controller translates it. Unless address resolution +* is enabled and this peer is programmed into the resolving +* list, keep the RPA the peer is on air with; +* le_conn_complete_evt() resolves it back once the link is +* up. +*/ +if (irk && + (!hci_dev_test_flag (hdev,HCI_LL_RPA_RESOLUTION ) || + !hci_bdaddr_list_lookup_with_irk (&hdev->le_resolv_list, + &irk->bdaddr, + irk->addr_type))) + irk =NULL ;break ;case SCO_LINK :case ESCO_LINK :
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 172/438 Bluetooth: hci_qca: Do not write to the serial port after it is closed
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ibrahim Abdelkader <[email protected]>
[ Upstream commit 4e93c65f87825e1e012bce56615320aeb123815d ]
hci_uart_close() closes the serdev port if HCI_QUIRK_NON_PERSISTENT_SETUP is set (for example, for the WCN399x family). A failed hci_dev_open_sync() following a successful qca_setup() calls hdev->close() but not hdev->shutdown(), so the port is closed while power->vregs_on is left true. qca_serdev_remove() then passes its power->vregs_on test and calls qca_power_off(), which writes to the closed port unconditionally.
Seen on a WCN3988 by unbinding the driver after a controller failure. The trace below is from a 7.0.0 based kernel, where qca_power_off() was still named qca_power_shutdown():
Unable to handle kernel NULL pointer dereference at virtual address 0000000000000038 Call trace: tty_set_termios+0x50/0x238 (P) ttyport_set_baudrate+0x84/0xc0 serdev_device_set_baudrate+0x24/0x40 qca_power_shutdown+0x158/0x1fc [hci_uart] qca_serdev_remove+0x54/0x68 [hci_uart] serdev_drv_remove+0x1c/0x2c device_remove+0x4c/0x80 device_release_driver_internal+0x1cc/0x224 device_driver_detach+0x18/0x24 unbind_store+0xb4/0xc0
Check HCI_UART_PROTO_READY, which hci_uart_close() clears in the same place it closes the port, before writing to it. The regulator disable is left unconditional so the controller is still powered down.
The dangling serport->tty that turns this into a use-after-free is addressed in a separate patch.
--- drivers/bluetooth/hci_qca.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-)
drivers/bluetooth/hci_qca.c +10 -4
@@ -2228,8 +2228,8 @@ static void qca_power_off(struct hci_uart *hu)bool sw_ctrl_state;struct qca_power *power; -/* From this point we go into power off state. But serial port is -* still open, stop queueing the IBS data and flush all the buffered +/* From this point we go into power off state. But serial port may +* still be open, stop queueing the IBS data and flush all the buffered * data in skb's. */ spin_lock_irqsave (&qca->hci_ibs_lock, flags);
@@ -2251,8 +2251,14 @@ static void qca_power_off(struct hci_uart *hu)case QCA_WCN3990 :case QCA_WCN3991 :case QCA_WCN3998 : -host_set_baudrate (hu,2400 ); -qca_send_power_pulse (hu, false); +/* Both of these write to the serial port which may have +* already been closed by hci_uart_close(), which closes +* the port if HCI_QUIRK_NON_PERSISTENT_SETUP is set. +*/ +if (test_bit (HCI_UART_PROTO_READY , &hu->flags)) { +host_set_baudrate (hu,2400 ); +qca_send_power_pulse (hu, false); + }break ;default :break ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 173/438 Bluetooth: ISO: Fix parent socket leak in iso_conn_ready()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Luiz Augusto von Dentz <[email protected]>
[ Upstream commit ca18ee413a7cb6f09885778039225e58bae0d607 ]
iso_get_sock() returns the parent socket with a reference held, which is dropped by sock_put() once the child socket has been set up. The error path taken when iso_sock_alloc() fails only calls release_sock() and returns, leaking the reference and thus the parent socket itself.
Drop the reference on that path as well.
--- net/bluetooth/iso.c | 1 + 1 file changed, 1 insertion(+)
net/bluetooth/iso.c +1 -0
@@ -2289,6 +2289,7 @@ static void iso_conn_ready(struct iso_conn *conn) BTPROTO_ISO, GFP_ATOMIC, 0);if (!sk) {release_sock (parent); +sock_put (parent);return ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 174/438 Bluetooth: ISO: set BT_LISTEN before requesting a BIG sync
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Luiz Augusto von Dentz <[email protected]>
[ Upstream commit 296e7f3c5071cc02dc22e1566e759179fa1792ae ]
A BIS connection is matched to its parent socket by looking for a socket in BT_LISTEN state with the same BIG handle:
iso_conn_ready()
if (test_bit(HCI_CONN_BIG_SYNC, &hcon->flags))
parent = iso_get_sock(hdev, &hcon->src, &hcon->dst,
BT_LISTEN, iso_match_big_hcon, hcon);The socket was only moved to BT_LISTEN after iso_conn_big_sync() returned, while the LE BIG Create Sync command has already been queued by then. If the BIG sync is established before the state is updated, which is easy to hit with an emulated controller as the command may complete in a few hundred microseconds, no parent is found and the BIS connections are never notified to the listening socket.
The user space is then left waiting for connections that never arrive, e.g. bluetoothd never completes a MediaTransport1.Acquire of a Broadcast Sink transport.
Move the socket to BT_LISTEN before requesting the BIG sync, so the state is visible by the time the command is queued, and restore the previous state if the request could not be started. Since the socket is briefly visible as a listening socket, child sockets may have been queued in the meantime, so drain the accept queue before restoring the
state: the cleanup paths of BT_CONNECT2/BT_CONNECTED don't do it and the children would be left with a dangling parent pointer.
--- net/bluetooth/iso.c | 52 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 41 insertions(+), 11 deletions(-)
net/bluetooth/iso.c +41 -11
@@ -819,19 +819,24 @@ static void iso_sock_destruct(struct sock *sk)skb_queue_purge (&sk->sk_error_queue); } -static void iso_sock_cleanup_listen (struct sock *parent) +/* Close not yet accepted channels */ +static void iso_sock_flush_accept_q (struct sock *parent) {struct sock *sk; -BT_DBG ("parent %p" , parent); - -/* Close not yet accepted channels */ while ((sk =bt_accept_dequeue (parent,NULL ))) {iso_sock_close (sk);iso_sock_kill (sk);/* Drop the reference handed back by bt_accept_dequeue(). */ sock_put (sk); } +} + +static void iso_sock_cleanup_listen (struct sock *parent) +{ +BT_DBG ("parent %p" , parent); + +iso_sock_flush_accept_q (parent);/* If listening socket has a hcon, properly disconnect it */ if (iso_pi (parent)->conn &&iso_pi (parent)->conn->hcon) {
@@ -1737,6 +1742,13 @@ static int iso_sock_recvmsg(struct socket *sock, struct msghdr *msg,switch (sk->sk_state) {case BT_CONNECT2 :if (test_bit (BT_SK_PA_SYNC , &pi->flags)) { +/* Move to BT_LISTEN before requesting the BIG +* sync: the BIS connections are matched to a +* parent socket in BT_LISTEN state, and they +* may be notified before the request returns. +*/ + sk->sk_state =BT_LISTEN ; +release_sock (sk); err =iso_conn_big_sync (sk);lock_sock (sk);
@@ -1745,12 +1757,20 @@ static int iso_sock_recvmsg(struct socket *sock, struct msghdr *msg,* connection may have been torn down * meanwhile and iso_chan_del() may have * already moved the socket to BT_CLOSED. -* Only move on to BT_LISTEN if the BIG sync -* was actually started and nothing else has -* changed the state. +* Only move back if the BIG sync could not be +* started and nothing else has changed the +* state. */ -if (!err && sk->sk_state ==BT_CONNECT2 ) - sk->sk_state =BT_LISTEN ; +if (err && sk->sk_state ==BT_LISTEN ) { +/* Discard any child socket that may +* have been queued while the socket +* was in BT_LISTEN, as the cleanup of +* BT_CONNECT2 doesn't drain the +* accept queue. +*/ +iso_sock_flush_accept_q (sk); + sk->sk_state =BT_CONNECT2 ; + } }else {iso_conn_defer_accept (pi->conn->hcon); sk->sk_state =BT_CONFIG ;
@@ -1760,12 +1780,22 @@ static int iso_sock_recvmsg(struct socket *sock, struct msghdr *msg,break ;case BT_CONNECTED :if (test_bit (BT_SK_PA_SYNC , &iso_pi (sk)->flags)) { +/* As above, the BIS connections may be +* notified before the request returns. +*/ + sk->sk_state =BT_LISTEN ; +release_sock (sk); err =iso_conn_big_sync (sk);lock_sock (sk); -if (!err && sk->sk_state ==BT_CONNECTED ) - sk->sk_state =BT_LISTEN ; +if (err && sk->sk_state ==BT_LISTEN ) { +/* As above, don't leave any child +* socket behind in the accept queue. +*/ +iso_sock_flush_accept_q (sk); + sk->sk_state =BT_CONNECTED ; + } early_ret = true; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 175/438 Bluetooth: btmtk: fix wrong status for short WMT FUNC_CTRL events
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chris Lu <[email protected]>
[ Upstream commit 78b6abd6c7a7591aacdae657f813214dae4fcd3b ]
A too-short BTMTK_WMT_FUNC_CTRL event (WMT header only, no trailing 2-byte status word) is always treated as BTMTK_WMT_ON_UNDONE. This short form is how firmware acks a plain enable/disable request, and the actual result is carried in the header's own flag byte (0 = success), not a separate status word. Decode it from there instead of assuming failure.
Verified setup on MT7920, MT7921, MT7922 and MT7925: no regression.
--- drivers/bluetooth/btmtk.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-)
drivers/bluetooth/btmtk.c +6 -1
@@ -721,7 +721,12 @@ static int btmtk_usb_hci_wmt_sync(struct hci_dev *hdev,case BTMTK_WMT_FUNC_CTRL :if (!skb_pull_data (data->evt_skb,sizeof (wmt_evt_funcc->status))) { - status =BTMTK_WMT_ON_UNDONE ; +/* A plain enable/disable request is acked with just +* the WMT header and no trailing status word; the +* result is carried in the header's own flag byte. +*/ + status = wmt_evt->whdr.flag ?BTMTK_WMT_ON_UNDONE : +BTMTK_WMT_ON_DONE ;break ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 176/438 Bluetooth: btmtksdio, btmtkuart: validate WMT event length before struct access
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chris Lu <[email protected]>
[ Upstream commit 8879e3e0a84a86954c855caceead4867e74a9a27 ]
btmtksdio.c and btmtkuart.c cast a received WMT event straight to struct btmtk_hci_wmt_evt and read its op/flag fields without checking the event is long enough to contain them, unlike btmtk.c. The FUNC_CTRL case then further casts to struct btmtk_hci_wmt_evt_funcc and reads its 2-byte status field, again without a length check. Firmware that sends a short or malformed WMT event makes both drivers read past the end of the received SKB.
Mirror btmtk.c: validate the base WMT header with skb_pull_data() before touching any of its fields, and when a FUNC_CTRL event turns out to be the short, header-only form (a plain enable/disable ack with no status word), decode the result from the header's own flag byte instead (0 = success, otherwise failure).
Verified setup on MT7920, MT7921, MT7922 and MT7925: no regression.
--- drivers/bluetooth/btmtksdio.c | 20 +++++++++++++++++++- drivers/bluetooth/btmtkuart.c | 20 +++++++++++++++++++- 2 files changed, 38 insertions(+), 2 deletions(-)
drivers/bluetooth/btmtksdio.c +19 -1
@@ -217,7 +217,14 @@ static int mtk_hci_wmt_sync(struct hci_dev *hdev, }/* Parse and handle the return WMT event */ - wmt_evt = (struct btmtk_hci_wmt_evt *)bdev->evt_skb->data; + wmt_evt =skb_pull_data (bdev->evt_skb,sizeof (*wmt_evt)); +if (!wmt_evt) { +bt_dev_err (hdev,"WMT event too short (%u bytes)" , + bdev->evt_skb->len); + err = -EINVAL ; + goto err_free_skb; + } +if (wmt_evt->whdr.op != hdr->op) {bt_dev_err (hdev,"Wrong op received %d expected %d" , wmt_evt->whdr.op, hdr->op);
@@ -233,6 +240,17 @@ static int mtk_hci_wmt_sync(struct hci_dev *hdev, status =BTMTK_WMT_PATCH_DONE ;break ;case BTMTK_WMT_FUNC_CTRL : +if (!skb_pull_data (bdev->evt_skb, +sizeof (wmt_evt_funcc->status))) { +/* A plain enable/disable request is acked with just +* the WMT header and no trailing status word; the +* result is carried in the header's own flag byte. +*/ + status = wmt_evt->whdr.flag ?BTMTK_WMT_ON_UNDONE : +BTMTK_WMT_ON_DONE ; +break ; + } + wmt_evt_funcc = (struct btmtk_hci_wmt_evt_funcc *)wmt_evt;if (be16_to_cpu (wmt_evt_funcc->status) ==0x404 ) status =BTMTK_WMT_ON_DONE ;
drivers/bluetooth/btmtkuart.c +19 -1
@@ -151,7 +151,14 @@ static int mtk_hci_wmt_sync(struct hci_dev *hdev, }/* Parse and handle the return WMT event */ - wmt_evt = (struct btmtk_hci_wmt_evt *)bdev->evt_skb->data; + wmt_evt =skb_pull_data (bdev->evt_skb,sizeof (*wmt_evt)); +if (!wmt_evt) { +bt_dev_err (hdev,"WMT event too short (%u bytes)" , + bdev->evt_skb->len); + err = -EINVAL ; + goto err_free_wc; + } +if (wmt_evt->whdr.op != hdr->op) {bt_dev_err (hdev,"Wrong op received %d expected %d" , wmt_evt->whdr.op, hdr->op);
@@ -167,6 +174,17 @@ static int mtk_hci_wmt_sync(struct hci_dev *hdev, status =BTMTK_WMT_PATCH_DONE ;break ;case BTMTK_WMT_FUNC_CTRL : +if (!skb_pull_data (bdev->evt_skb, +sizeof (wmt_evt_funcc->status))) { +/* A plain enable/disable request is acked with just +* the WMT header and no trailing status word; the +* result is carried in the header's own flag byte. +*/ + status = wmt_evt->whdr.flag ?BTMTK_WMT_ON_UNDONE : +BTMTK_WMT_ON_DONE ; +break ; + } + wmt_evt_funcc = (struct btmtk_hci_wmt_evt_funcc *)wmt_evt;if (be16_to_cpu (wmt_evt_funcc->status) ==0x404 ) status =BTMTK_WMT_ON_DONE ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 177/438 Bluetooth: btmtksdio: Fix PM runtime reference leak in shutdown
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Tzung-Bi Shih <[email protected]>
[ Upstream commit 7b60ee5f46f2ee329de661f7c68b6818d8136220 ]
In btmtksdio_shutdown(), pm_runtime_get_sync() is called at the beginning of the function. However, if sending the WMT function control command fails later, the driver returns early.
It bypasses the corresponding pm_runtime_put_noidle() and pm_runtime_disable() calls, leaking the PM usage counter and leaving PM runtime enabled indefinitely.
Fall through to execute the PM runtime cleanup block even if WMT errors.
--- drivers/bluetooth/btmtksdio.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-)
drivers/bluetooth/btmtksdio.c +1 -3
@@ -1262,10 +1262,8 @@ static int btmtksdio_shutdown(struct hci_dev *hdev) wmt_params.status =NULL ; err =mtk_hci_wmt_sync (hdev, &wmt_params); -if (err <0 ) { +if (err <0 )bt_dev_err (hdev,"Failed to send wmt func ctrl (%d)" , err); -return err; - } ignore_wmt_cmd:pm_runtime_put_noidle (bdev->dev);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 178/438 Bluetooth: btintel_pcie: fix off-by-one bounds check in RX submit
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sai Teja Aluvala <[email protected]>
[ Upstream commit 2ea5a87a5a7ae58cb2662b8a7d06f209383e1765 ]
btintel_pcie_submit_rx() used frbd_index > rxq->count to guard the FRBD array access, allowing frbd_index == rxq->count to pass through and index one element past the end of the array. Change the check to
Quoted 1 line
=
rxq->countso every out-of-range index is rejected.
This issue was reported by Claude Mythos.
--- drivers/bluetooth/btintel_pcie.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/bluetooth/btintel_pcie.c +1 -1
@@ -508,7 +508,7 @@ static int btintel_pcie_submit_rx(struct btintel_pcie_data *data) frbd_index = data->ia.tr_hia[BTINTEL_PCIE_RXQ_NUM ]; -if (frbd_index > rxq->count) +if (frbd_index >= rxq->count)return -ERANGE ;/* Prepare for RX submit. It updates the FRBD with the address of DMA
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 179/438 Bluetooth: RFCOMM: avoid socket lock inversion in listener cleanup
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Juan Perdomo <[email protected]>
[ Upstream commit 801fb950cae7048eb7d83b18857d1ca37b8cd5a4 ]
rfcomm_sock_cleanup_listen() closes unaccepted child sockets through rfcomm_sock_close(), which takes the child socket lock before rfcomm_dlc_close() acquires rfcomm_mutex. The RFCOMM worker takes these locks in reverse order while handling connections and DLC state changes, so lockdep reports a possible deadlock.
Close dequeued children without taking their socket lock. The accept queue owns a reference to each child, and bt_accept_dequeue() locks the child while unlinking it and clearing its parent pointer.
Dropping the child lock makes it important to prevent a concurrent rfcomm_connect_ind() from enqueueing a new child after cleanup observes an empty queue. Set a listening socket to BT_CLOSED while its lock is still held, before dropping the lock and draining the queue. The state check in rfcomm_connect_ind() then rejects new children once cleanup starts.
--- net/bluetooth/rfcomm/sock.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-)
net/bluetooth/rfcomm/sock.c +10 -3
@@ -241,9 +241,7 @@ static void __rfcomm_sock_close(struct sock *sk)*/ static void rfcomm_sock_close (struct sock *sk) { -lock_sock (sk);__rfcomm_sock_close (sk); -release_sock (sk); }static void rfcomm_sock_init (struct sock *sk,struct sock *parent)
@@ -904,6 +902,7 @@ static int rfcomm_sock_compat_ioctl(struct socket *sock, unsigned int cmd, unsigstatic int rfcomm_sock_shutdown (struct socket *sock,int how) {struct sock *sk = sock->sk; +bool cleanup_listen = false;int err =0 ;BT_DBG ("sock %p, sk %p" , sock, sk);
@@ -914,9 +913,17 @@ static int rfcomm_sock_shutdown(struct socket *sock, int how)lock_sock (sk);if (!sk->sk_shutdown) { sk->sk_shutdown =SHUTDOWN_MASK ; +if (sk->sk_state ==BT_LISTEN ) { +/* Block new children before cleaning up without sk lock. */ + sk->sk_state =BT_CLOSED ; + cleanup_listen = true; + }release_sock (sk); -__rfcomm_sock_close (sk); +if (cleanup_listen) +rfcomm_sock_cleanup_listen (sk); +else +__rfcomm_sock_close (sk);lock_sock (sk); if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime &&
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 180/438 af_unix: Unify scc_index when finalising SCC in __unix_walk_scc().
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kuniyuki Iwashima <[email protected]>
[ Upstream commit 4a4263dfeabad72f95e8ab6e15146861fa4144dd ]
Commit bfdb01283ee8 ("af_unix: Assign a unique index to SCC.") changed Tarjan's algorithm to update lowlink with lowlink, which is called lowpoint (unix_vertex.scc_index).
unix_vertex_dead() assumes all vertices in an SCC share the same lowpoint, but this is not always true if an SCC has two or more back edges, depending on the order of DFS.
For example, the graph below has two back edges from B to A and from C to B.
A --> B --> C
^ | ^ |
----' ----'
If DFS walks through A -> B -> C -> B (-> C -> B) -> A (-> B -> A), each index and scc_index will be updated as follows.
A --> B --> C C = (3, 3) (index, scc_index)
B = (2, 2)
A = (1, 1) A ... B ... C C = (3, 2)<-.
^ | B = (2, 2) -'
`----' A = (1, 1)A ... B ... C C = (3, 2) ^ | . . B = (2, 1)<-. `----' .... A = (1, 1) -'
Then, unix_vertex_dead() thinks that B is passed to another SCC with scc_index 2, and the SCC is not garbage-collected.
This does not happen if DFS walks in a different order below or starts from B.
1 3
A --> B --> C
^ | ^ |
----' ----'
2 4
Let's unify scc_index across the SCC when finalising it.
Note that updating v->index was previously done in unix_scc_dead(), when called from __unix_walk_scc(), just to save one loop. Since __unix_walk_scc() now iterates over the SCC anyway, the update is moved back to __unix_walk_scc() and 'fast' argument is dropped.
--- net/unix/garbage.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-)
net/unix/garbage.c +10 -7
@@ -374,7 +374,7 @@ static bool unix_vertex_dead(struct unix_vertex *vertex)static LIST_HEAD (unix_visited_vertices);static unsigned long unix_vertex_grouped_index =UNIX_VERTEX_INDEX_MARK2 ; -static bool unix_scc_dead (struct list_head *scc,bool fast) +static bool unix_scc_dead (struct list_head *scc) {struct unix_vertex *vertex;bool scc_dead = true;
@@ -386,10 +386,6 @@ static bool unix_scc_dead(struct list_head *scc, bool fast)/* Don't restart DFS from this vertex. */ list_move_tail (&vertex->entry, &unix_visited_vertices); -/* Mark vertex as off-stack for __unix_walk_scc(). */ -if (!fast) - vertex->index = unix_vertex_grouped_index; -if (scc_dead) scc_dead =unix_vertex_dead (vertex); }
@@ -521,6 +517,7 @@ static unsigned long __unix_walk_scc(struct unix_vertex *vertex, }if (vertex->index == vertex->scc_index) { +struct unix_vertex *v;struct list_head scc;/* SCC finalised.
@@ -530,7 +527,13 @@ static unsigned long __unix_walk_scc(struct unix_vertex *vertex,*/ __list_cut_position (&scc, &vertex_stack, &vertex->scc_entry); -if (unix_scc_dead (&scc, false)) { +list_for_each_entry_reverse (v, &scc, scc_entry) { +/* Mark vertex as off-stack and assign a unique ID. */ + v->index = unix_vertex_grouped_index; + v->scc_index = vertex->scc_index; + } + +if (unix_scc_dead (&scc)) {unix_collect_skb (&scc, hitlist); }else {if (unix_vertex_max_scc_index < vertex->scc_index)
@@ -588,7 +591,7 @@ static void unix_walk_scc_fast(struct sk_buff_head *hitlist) vertex =list_first_entry (&unix_unvisited_vertices,typeof (*vertex), entry);list_add (&scc, &vertex->scc_entry); -if (unix_scc_dead (&scc, true)) { +if (unix_scc_dead (&scc)) { cyclic_sccs--;unix_collect_skb (&scc, hitlist); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 181/438 net: stmmac: fix TSO header length truncation
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Lorenzo Bianconi <[email protected]>
[ Upstream commit 15989abd74f16f44bf953d056b95f1d2fda9b0cd ]
stmmac_tso_xmit() stores the protocol header length returned by stmmac_tso_header_size() in a u8. stmmac_tso_valid_packet() admits headers up to 1023 bytes, so a header longer than 255 bytes wraps modulo 256 (486 becomes 230, 256 becomes 0).
A TCP over IPv6 socket carrying a few hundred bytes of sticky destination/hop-by-hop options makes skb_tcp_all_headers() exceed 255 while staying below the 1023-byte limit, so such an skb reaches stmmac_tso_xmit().
Widen proto_hdr_len to unsigned int, which is sufficient since the value is bounded by the hardware limit, and adjust the debug print specifier accordingly.
--- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +3 -3
@@ -4513,16 +4513,16 @@ static int stmmac_tso_get_num_desc(struct stmmac_tx_queue *tx_q,*/ static netdev_tx_t stmmac_tso_xmit (struct sk_buff *skb,struct net_device *dev) { +unsigned int first_entry, entry, tx_packets, proto_hdr_len;struct dma_desc *desc, *first, *mss_desc =NULL ;struct stmmac_priv *priv =netdev_priv (dev); -unsigned int first_entry, entry, tx_packets;struct stmmac_txq_stats *txq_stats;int i, first_tx, nfrags, ndesc;struct stmmac_tx_queue *tx_q;bool set_ic, is_last_segment;u32 pay_len, mss, queue; -u8 proto_hdr_len, hdr;dma_addr_t des; +u8 hdr; nfrags =skb_shinfo (skb)->nr_frags; queue =skb_get_queue_mapping (skb);
@@ -4570,7 +4570,7 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev) }if (netif_msg_tx_queued (priv)) { -pr_info ("%s: hdrlen %d, hdr_len %d, pay_len %d, mss %d\n" , +pr_info ("%s: hdrlen %d, hdr_len %u, pay_len %d, mss %d\n" , __func__, hdr, proto_hdr_len, pay_len, mss);pr_info ("\tskb->len %d, skb->data_len %d\n" , skb->len, skb->data_len);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 182/438 pppoatm: ensure a writable skb header and linear data
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit ecc7253683a3c55caa868ce0ee530fcb0044bd3c ]
In pppoatm_send(), LLC encapsulation checks whether there is sufficient headroom for the 4-byte LLC header, but does not ensure that the skb header is writable.
Normal transmit packets passing through ppp_start_xmit() have their header unshared via skb_cow_head(). However, packets can also reach pppoatm_send() via PPP channel bridging (PPPIOCBRIDGECHAN) without going through ppp_start_xmit().
Use skb_cow_head() to ensure both sufficient headroom and a writable header before pushing the LLC header.
While at it:
- Call pskb_may_pull(skb, 1) before inspecting skb->data[0] to prevent out-of-bounds reads on zero-length or non-linear frames (e.g. from bridging).
- Defer SC_COMP_PROT protocol compression until after pppoatm_may_send() succeeds. This eliminates the temporary skb allocation on admission failure and completely removes the fragile "undo" heuristic at the nospace label, avoiding any risk of reading uninitialized headroom or performing an unbalanced skb_push().
--- net/atm/pppoatm.c | 42 +++++++++++++++++------------------------- 1 file changed, 17 insertions(+), 25 deletions(-)
net/atm/pppoatm.c +17 -25
@@ -292,10 +292,13 @@ static int pppoatm_send(struct ppp_channel *chan, struct sk_buff *skb)struct atm_vcc *vcc;int ret; +if (!pskb_may_pull (skb,1 )) { +kfree_skb (skb); +return DROP_PACKET ; + } +ATM_SKB (skb)->vcc = pvcc->atmvcc;pr_debug ("(skb=0x%p, vcc=0x%p)\n" , skb, pvcc->atmvcc); -if (skb->data[0 ] =='\0' && (pvcc->flags &SC_COMP_PROT )) - (void )skb_pull (skb,1 ); vcc =ATM_SKB (skb)->vcc;bh_lock_sock (sk_atm (vcc));
@@ -317,23 +320,13 @@ static int pppoatm_send(struct ppp_channel *chan, struct sk_buff *skb)switch (pvcc->encaps) {/* LLC encapsulation needed */ case e_llc: -if (skb_headroom (skb) <LLC_LEN ) { -struct sk_buff *n; - n =skb_realloc_headroom (skb,LLC_LEN ); -if (n !=NULL && - !pppoatm_may_send (pvcc, n->truesize)) { -kfree_skb (n); - goto nospace; - } -consume_skb (skb); - skb = n; -if (skb ==NULL ) { -bh_unlock_sock (sk_atm (vcc)); -return DROP_PACKET ; - } - }else if (!pppoatm_may_send (pvcc, skb->truesize)) +if (skb_cow_head (skb,LLC_LEN )) { +bh_unlock_sock (sk_atm (vcc)); +kfree_skb (skb); +return DROP_PACKET ; + } +if (!pppoatm_may_send (pvcc, skb->truesize)) goto nospace; -memcpy (skb_push (skb,LLC_LEN ), pppllc,LLC_LEN );break ;case e_vc:if (!pppoatm_may_send (pvcc, skb->truesize))
@@ -346,6 +339,12 @@ static int pppoatm_send(struct ppp_channel *chan, struct sk_buff *skb)return 1 ; } +if (skb->data[0 ] =='\0' && (pvcc->flags &SC_COMP_PROT )) +skb_pull (skb,1 ); + +if (pvcc->encaps == e_llc) +memcpy (skb_push (skb,LLC_LEN ), pppllc,LLC_LEN ); +atm_account_tx (vcc, skb);pr_debug ("atm_skb(%p)->vcc(%p)->dev(%p)\n" , skb,ATM_SKB (skb)->vcc,ATM_SKB (skb)->vcc->dev);
@@ -355,13 +354,6 @@ static int pppoatm_send(struct ppp_channel *chan, struct sk_buff *skb)return ret; nospace:bh_unlock_sock (sk_atm (vcc)); -/* -* We don't have space to send this SKB now, but we might have -* already applied SC_COMP_PROT compression, so may need to undo -*/ -if ((pvcc->flags &SC_COMP_PROT ) &&skb_headroom (skb) >0 && - skb->data[-1 ] =='\0' ) - (void )skb_push (skb,1 );return 0 ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 183/438 drop_monitor: synchronize tracepoint unregistration on error path
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit 6a038ef2b57922b6d9ca98ddac0df0681849b704 ]
If register_trace_napi_poll() fails in net_dm_trace_on_set(), unregister_trace_kfree_skb() is called to roll back the kfree_skb tracepoint registration.
However, tracepoint_synchronize_unregister() is omitted before calling cancel_work_sync() and module_put(). An in-flight probe executing concurrently on another CPU could call schedule_work() after cancel_work_sync() has already returned, leaving a pending work item scheduled after the module reference is dropped. If the module is then unloaded, executing the work item triggers a kernel panic.
Add tracepoint_synchronize_unregister() after unregister_trace_kfree_skb() in the error path, matching net_dm_trace_off_set() and net_dm_hw_probe_unregister().
--- net/core/drop_monitor.c | 1 + 1 file changed, 1 insertion(+)
net/core/drop_monitor.c +1 -0
@@ -1173,6 +1173,7 @@ static int net_dm_trace_on_set(struct netlink_ext_ack *extack) err_unregister_trace:unregister_trace_kfree_skb (ops->kfree_skb_probe,NULL ); +tracepoint_synchronize_unregister (); err_module_put:for_each_possible_cpu (cpu) {struct per_cpu_dm_data *data = &per_cpu (dm_cpu_data, cpu);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 184/438 drop_monitor: use timer_shutdown_sync() to prevent timer rearming during teardown
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit c391a40f71886b28c082b47270f0e856fa3e1150 ]
In drop_monitor teardown paths (net_dm_trace_off_set(), net_dm_hw_monitor_stop(), and error unwind paths in net_dm_trace_on_set() and net_dm_hw_monitor_start()), per-CPU timers are stopped using timer_delete_sync() followed by cancel_work_sync().
However, there is a circular dependency between send_timer and dm_alert_work:
1) sched_send_work() (timer callback) schedules dm_alert_work.
2) send_dm_alert() / net_dm_hw_summary_work() calls reset_per_cpu_data() or net_dm_hw_reset_per_cpu_data().
3) If memory allocation fails under memory pressure in the reset function, it re-arms the timer via mod_timer(&data->send_timer, ...).
If dm_alert_work is running concurrently while timer_delete_sync() executes on another CPU, an allocation failure in the worker will re-arm the timer after timer_delete_sync() has already returned. Once cancel_work_sync() completes and module_put() is called, the timer remains active in the timer wheel. If the module is then unloaded, the timer will fire and execute sched_send_work() in freed memory, triggering a kernel panic / use-after-free.
Switch from timer_delete_sync() to timer_shutdown_sync(). This guarantees that any in-flight timer handler has finished and prevents subsequent re-arming attempts from running workers from succeeding. When monitoring is restarted later, timer_setup() is invoked, which cleanly re-initializes the timer.
--- net/core/drop_monitor.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
net/core/drop_monitor.c +4 -4
@@ -1083,7 +1083,7 @@ static int net_dm_hw_monitor_start(struct netlink_ext_ack *extack)struct per_cpu_dm_data *hw_data = &per_cpu (dm_hw_cpu_data, cpu);struct sk_buff *skb; -timer_delete_sync (&hw_data->send_timer); +timer_shutdown_sync (&hw_data->send_timer);cancel_work_sync (&hw_data->dm_alert_work);while ((skb =__skb_dequeue (&hw_data->drop_queue))) {struct devlink_trap_metadata *hw_metadata;
@@ -1117,7 +1117,7 @@ static void net_dm_hw_monitor_stop(struct netlink_ext_ack *extack)struct per_cpu_dm_data *hw_data = &per_cpu (dm_hw_cpu_data, cpu);struct sk_buff *skb; -timer_delete_sync (&hw_data->send_timer); +timer_shutdown_sync (&hw_data->send_timer);cancel_work_sync (&hw_data->dm_alert_work);while ((skb =__skb_dequeue (&hw_data->drop_queue))) {struct devlink_trap_metadata *hw_metadata;
@@ -1179,7 +1179,7 @@ static int net_dm_trace_on_set(struct netlink_ext_ack *extack)struct per_cpu_dm_data *data = &per_cpu (dm_cpu_data, cpu);struct sk_buff *skb; -timer_delete_sync (&data->send_timer); +timer_shutdown_sync (&data->send_timer);cancel_work_sync (&data->dm_alert_work);while ((skb =__skb_dequeue (&data->drop_queue)))consume_skb (skb);
@@ -1207,7 +1207,7 @@ static void net_dm_trace_off_set(void)struct per_cpu_dm_data *data = &per_cpu (dm_cpu_data, cpu);struct sk_buff *skb; -timer_delete_sync (&data->send_timer); +timer_shutdown_sync (&data->send_timer);cancel_work_sync (&data->dm_alert_work);while ((skb =__skb_dequeue (&data->drop_queue)))consume_skb (skb);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 185/438 drop_monitor: use raw_cpu_ptr() in tracepoint probes
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit c19b7d35086b7d240f1ca3088b0079d2bd39ffb9 ]
syzbot reported a preemption warning in sk_skb_reason_drop():
BUG: using smp_processor_id() in preemptible [00000000] code: syz.0.17/5917 caller is net_dm_packet_trace_kfree_skb_hit+0x119/0x350 net/core/drop_monitor.c:519
In net_dm_packet_trace_kfree_skb_hit(), data = this_cpu_ptr(&dm_cpu_data) is evaluated before spin_lock_irqsave(&data->drop_queue.lock, flags). When kfree_skb() is called from preemptible context (e.g. process context during close() on /dev/net/tun), preemption is enabled, triggering the CONFIG_DEBUG_PREEMPT warning in smp_processor_id().
The same pattern exists in net_dm_hw_trap_summary_probe() and net_dm_hw_trap_packet_probe() for dm_hw_cpu_data.
This is a false positive because each per-cpu structure is protected by its own spinlock. If the task migrates to another CPU right after reading the per-cpu pointer, the lock still safely synchronizes access to that queue.
Use raw_cpu_ptr() instead of this_cpu_ptr() to silence CONFIG_DEBUG_PREEMPT without disturbing interrupt state or breaking PREEMPT_RT locking semantics.
--- net/core/drop_monitor.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
net/core/drop_monitor.c +3 -3
@@ -448,7 +448,7 @@ net_dm_hw_trap_summary_probe(void *ignore, const struct devlink *devlink,if (metadata->trap_type ==DEVLINK_TRAP_TYPE_CONTROL )return ; - hw_data =this_cpu_ptr (&dm_hw_cpu_data); + hw_data =raw_cpu_ptr (&dm_hw_cpu_data);raw_spin_lock_irqsave (&hw_data->lock, flags); hw_entries = hw_data->hw_entries;
@@ -516,7 +516,7 @@ static void net_dm_packet_trace_kfree_skb_hit(void *ignore,*/ nskb->tstamp = tstamp; - data =this_cpu_ptr (&dm_cpu_data); + data =raw_cpu_ptr (&dm_cpu_data);spin_lock_irqsave (&data->drop_queue.lock, flags);if (skb_queue_len (&data->drop_queue) < net_dm_queue_len)
@@ -983,7 +983,7 @@ net_dm_hw_trap_packet_probe(void *ignore, const struct devlink *devlink,NET_DM_SKB_CB (nskb)->hw_metadata = n_hw_metadata; nskb->tstamp = tstamp; - hw_data =this_cpu_ptr (&dm_hw_cpu_data); + hw_data =raw_cpu_ptr (&dm_hw_cpu_data);spin_lock_irqsave (&hw_data->drop_queue.lock, flags);if (skb_queue_len (&hw_data->drop_queue) < net_dm_queue_len)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 186/438 drop_monitor: fix out-of-bounds write in reset_per_cpu_data()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit 439f392084f8f7f59ab9d47a9579185accefe1d8 ]
In reset_per_cpu_data(), al is computed as:
al = sizeof(struct net_dm_alert_msg);
al += dm_hit_limit * sizeof(struct net_dm_drop_point);
al += sizeof(struct nlattr);
skb = genlmsg_new(al, GFP_KERNEL);
...
nla = nla_reserve(skb, NLA_UNSPEC, sizeof(struct net_dm_alert_msg));
...
msg = nla_data(nla);
memset(msg, 0, al);Because al includes sizeof(struct nlattr) (the 4-byte attribute header), genlmsg_new() allocates al bytes of tailroom starting at nla. However, msg points to nla_data(nla), which is located sizeof(struct nlattr) bytes past nla. Calling memset(msg, 0, al) therefore writes al bytes starting from msg, exceeding the allocated buffer by sizeof(struct nlattr) (4 bytes) and corrupting skb_shared_info.
Fix this by letting al represent only the payload length, allocating the skb with genlmsg_new(nla_total_size(al), GFP_KERNEL), and zeroing al bytes from msg.
--- net/core/drop_monitor.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
net/core/drop_monitor.c +1 -2
@@ -141,9 +141,8 @@ static struct sk_buff *reset_per_cpu_data(struct per_cpu_dm_data *data) al =sizeof (struct net_dm_alert_msg ); al += dm_hit_limit *sizeof (struct net_dm_drop_point ); - al +=sizeof (struct nlattr ); - skb =genlmsg_new (al,GFP_KERNEL ); + skb =genlmsg_new (nla_total_size (al),GFP_KERNEL );if (!skb) goto err;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 187/438 net: stmmac: do not overwrite phc_index when no PTP clock is registered
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Lorenzo Bianconi <[email protected]>
[ Upstream commit f0ef4b1eaed000a304726a43091588e8426ba08a ]
stmmac_get_ts_info() reports phc_index as 0 when hardware timestamping is supported but no PTP clock has been registered yet (e.g. while the interface is down). Zero is a valid PHC index and would make userspace resolve the wrong clock; the absence of a clock should be reported as -1.
The ethtool core already initializes phc_index to -1 before invoking the get_ts_info callback (ethtool_init_tsinfo()), so just drop the erroneous assignment.
--- drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c | 2 -- 1 file changed, 2 deletions(-)
drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c +0 -2
@@ -1013,8 +1013,6 @@ static int stmmac_get_ts_info(struct net_device *dev,if (priv->ptp_clock) info->phc_index =ptp_clock_index (priv->ptp_clock); -else - info->phc_index =0 ; info->tx_types = (1 <<HWTSTAMP_TX_OFF ) | (1 <<HWTSTAMP_TX_ON );
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 188/438 net: bridge: vlan: fix bugs caused by switchdev deletion errors
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Nikolay Aleksandrov <[email protected]>
[ Upstream commit 2842ce397dd09882530b42f7fdb0c855767eb24e ]
Allowing switchdev to prevent vlan deletion and error out in __vlan_del could cause multiple different issues - inconsistent state, memory leaks when flushing, NULL pointer dereference on bridge error when flushing. It doesn't make sense to allow it to stop __vlan_del, so log the error and continue with software vlan deletion. This is also consistent with 8021q behaviour.
--- net/bridge/br_vlan.c | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-)
net/bridge/br_vlan.c +14 -17
@@ -387,12 +387,12 @@ static int __vlan_add(struct net_bridge_vlan *v, u16 flags, goto out; } -static int __vlan_del (struct net_bridge_vlan *v) +static void __vlan_del (struct net_bridge_vlan *v) {struct net_bridge_vlan *masterv = v;struct net_bridge_vlan_group *vg;struct net_bridge_port *p =NULL ; -int err =0 ; +int err;if (br_vlan_is_master (v)) { vg =br_vlan_group (v->br);
@@ -406,12 +406,16 @@ static int __vlan_del(struct net_bridge_vlan *v)if (p) { err =__vlan_vid_del (p->dev, p->br, v);if (err) - goto out; +br_warn (p->br, +"port %u(%s) failed to delete vlan %u from switchdev: %pe\n" , + (unsigned int )p->port_no, p->dev->name, + v->vid,ERR_PTR (err)); }else { err =br_switchdev_port_vlan_del (v->br->dev, v->vid);if (err && err != -EOPNOTSUPP ) - goto out; - err =0 ; +br_warn (v->br, +"failed to delete bridge vlan %u from switchdev: %pe\n" , + v->vid,ERR_PTR (err)); }if (br_vlan_should_use (v)) {
@@ -431,8 +435,6 @@ static int __vlan_del(struct net_bridge_vlan *v) }br_vlan_put_master (masterv); -out: -return err; }static void __vlan_group_free (struct net_bridge_vlan_group *vg)
@@ -449,7 +451,6 @@ static void __vlan_flush(const struct net_bridge *br, {struct net_bridge_vlan *vlan, *tmp;u16 v_start =0 , v_end =0 ; -int err;__vlan_delete_pvid (vg, vg->pvid);list_for_each_entry_safe (vlan, tmp, &vg->vlan_list, vlist) {
@@ -463,13 +464,7 @@ static void __vlan_flush(const struct net_bridge *br, } v_end = vlan->vid; - err =__vlan_del (vlan); -if (err) { -br_err (br, -"port %u(%s) failed to delete vlan %d: %pe\n" , - (unsigned int ) p->port_no, p->dev->name, - vlan->vid,ERR_PTR (err)); - } +__vlan_del (vlan); }/* notify about the last/whole vlan range */
@@ -837,8 +832,9 @@ int br_vlan_delete(struct net_bridge *br, u16 vid)br_fdb_delete_by_port (br,NULL , vid,0 );vlan_tunnel_info_del (vg, v); +__vlan_del (v); -return __vlan_del (v); +return 0 ; }void br_vlan_flush (struct net_bridge *br)
@@ -1368,8 +1364,9 @@ int nbp_vlan_delete(struct net_bridge_port *port, u16 vid)return -ENOENT ;br_fdb_find_delete_local (port->br, port, port->dev->dev_addr, vid);br_fdb_delete_by_port (port->br, port, vid,0 ); +__vlan_del (v); -return __vlan_del (v); +return 0 ; }void nbp_vlan_flush (struct net_bridge_port *port)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 189/438 netlink: do not free nlk->groups while lockless readers can use it
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit ceac0de741bfb47ca255eee075257b3bb31f0651 ]
netlink_realloc_groups() uses krealloc() under netlink_table_grab(). Whenever NLGRPSZ(groups) lands in a different kmalloc bucket, the old bitmap is freed immediately.
Two readers of nlk->groups / nlk->ngroups do not hold the netlink table lock:
1) sk_diag_dump_groups(). Hashed (bound) sockets are dumped from the rhashtable walk in __netlink_diag_dump(), which only holds RCU. Only the mc_list part of the dump takes nl_table_lock.
2) netlink_native_seq_show() (/proc/net/netlink), whose walk has been lockless since commit 21e4902aea80 ("netlink: Lockless lookup with RCU grace period in socket release").
Both can read a freed buffer, and sk_diag_dump_groups() can also read past the end of the old (smaller) buffer if it happens to load the old @groups pointer together with the new @ngroups value, copying the result into a NETLINK_DIAG_GROUPS attribute.
This is the same class of bug that commit f773608026ee ("netlink:
access nlk groups safely in netlink bind and getname") fixed for bind() and getname(); these two readers were missed. Simply grabbing the table lock in sk_diag_dump_groups() is not an option, because it is also called with nl_table_lock already held from the mc_list section of the dump.
Make the lockless readers safe instead:
- Allocate a new bitmap and free the old one after an RCU grace period, instead of relying on the implicit kfree() done by krealloc().
- Publish @groups before @ngroups, both with release semantics, and have the lockless readers load @ngroups first. A reader can then never pair the new (bigger) size with the old (smaller) buffer, and a reader picking up the new pointer while still seeing the old size is guaranteed to see the initialized bitmap.
netlink_realloc_groups() is called from process context (bind() and setsockopt()), so kfree_rcu_mightsleep() can be used, once the table has been released.
--- net/netlink/af_netlink.c | 42 ++++++++++++++++++++++++++++++++-------- net/netlink/diag.c | 20 +++++++++++++++---- 2 files changed, 50 insertions(+), 12 deletions(-)
net/netlink/af_netlink.c +34 -8
@@ -922,9 +922,9 @@ netlink_update_subscriptions(struct sock *sk, unsigned int subscriptions)static int netlink_realloc_groups (struct sock *sk) { +unsigned long *new_groups, *old_groups =NULL ;struct netlink_sock *nlk =nlk_sk (sk);unsigned int groups; -unsigned long *new_groups;int err =0 ;netlink_table_grab ();
@@ -938,18 +938,37 @@ static int netlink_realloc_groups(struct sock *sk)if (nlk->ngroups >= groups) goto out_unlock; - new_groups =krealloc (nlk->groups,NLGRPSZ (groups),GFP_ATOMIC ); -if (new_groups ==NULL ) { +/* Can not use krealloc(), because the old buffer might be freed +* immediately, while lockless readers (netlink diag dump and +* /proc/net/netlink) can still be looking at it. +*/ + new_groups =kzalloc (NLGRPSZ (groups),GFP_ATOMIC ); +if (!new_groups) { err = -ENOMEM ; goto out_unlock; } -memset ((char *)new_groups +NLGRPSZ (nlk->ngroups),0 , -NLGRPSZ (groups) -NLGRPSZ (nlk->ngroups)); + old_groups = nlk->groups; +if (old_groups) +memcpy (new_groups, old_groups,NLGRPSZ (nlk->ngroups)); + +/* Publish the new bitmap and its content: pairs with the address +* dependency in lockless readers, which can pick up the new pointer +* while still seeing the old (smaller) nlk->ngroups. +*/ +smp_store_release (&nlk->groups, new_groups); + +/* Then publish the new size: pairs with smp_load_acquire() from +* lockless readers, so that they can not read NLGRPSZ(new ngroups) +* bytes from the old buffer. +*/ +smp_store_release (&nlk->ngroups, groups); - nlk->groups = new_groups; - nlk->ngroups = groups; out_unlock:netlink_table_ungrab (); + +if (old_groups) +kfree_rcu_mightsleep (old_groups); +return err; }
@@ -2705,12 +2724,19 @@ static int netlink_native_seq_show(struct seq_file *seq, void *v) }else {struct sock *s = v;struct netlink_sock *nlk =nlk_sk (s); +const unsigned long *groups; + +/* Lockless read : netlink_realloc_groups() can change +* nlk->groups under us. The old buffer is freed after an +* RCU grace period, and this walk is RCU protected. +*/ + groups =READ_ONCE (nlk->groups); seq_printf(seq, "%pK %-3d %-10u %08x %-8d %-8d %-5d %-8d %-8u %-8llu\n", s, s->sk_protocol, nlk->portid, - nlk->groups ? (u32)nlk->groups[0] : 0, + groups ? (u32)groups[0] : 0, sk_rmem_alloc_get(s), sk_wmem_alloc_get(s), READ_ONCE(nlk->cb_running),
net/netlink/diag.c +16 -4
@@ -12,12 +12,24 @@static int sk_diag_dump_groups (struct sock *sk,struct sk_buff *nlskb) {struct netlink_sock *nlk =nlk_sk (sk); - -if (nlk->groups ==NULL ) +unsigned long *groups; +unsigned int ngroups; + +/* Hashed sockets are dumped from the rhashtable walk, which only +* holds rcu_read_lock(), while netlink_realloc_groups() can replace +* nlk->groups and nlk->ngroups at any time. +* +* Read nlk->ngroups first : this pairs with smp_store_release() +* from netlink_realloc_groups(), so that we can not use the new +* (bigger) size with the old (smaller) buffer. The old buffer is +* freed after an RCU grace period. +*/ + ngroups =smp_load_acquire (&nlk->ngroups); + groups =READ_ONCE (nlk->groups); +if (!groups)return 0 ; -return nla_put (nlskb,NETLINK_DIAG_GROUPS ,NLGRPSZ (nlk->ngroups), - nlk->groups); +return nla_put (nlskb,NETLINK_DIAG_GROUPS ,NLGRPSZ (ngroups), groups); }static int sk_diag_put_flags (struct sock *sk,struct sk_buff *skb)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 190/438 KVM: PPC: Book3S HV: fix use-after-free in kvmhv_emulate_tlbie_all_lpid()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Amit Machhiwal <[email protected]>
[ Upstream commit 51938dfa8a51a4f85328413fca9b6e21f9d2d088 ]
kvmhv_emulate_tlbie_all_lpid() iterates the nested-guest IDR and drops mmu_lock before calling kvmhv_emulate_tlbie_lpid(), but does not hold a reference on the kvm_nested_guest pointer obtained from the IDR. A concurrent vCPU issuing a single-LPID tlbie (is=2, ric=2) can race through kvmhv_flush_nested() -> kvmhv_remove_nested() -> idr_remove / --refcnt -> kvmhv_release_nested() -> kfree(gp) in that window, leaving the iterating vCPU with a dangling pointer. The subsequent mutex_lock(&gp->tlb_lock) and accesses to gp->shadow_pgtable, gp->shadow_lpid and gp->l1_host all touch freed memory. The free path is fully L1-controlled.
Fix this by incrementing gp->refcnt inside the loop before dropping mmu_lock, mirroring what kvmhv_get_nested() does, and releasing the reference with kvmhv_put_nested() after the per-guest work completes. This is the same get/put discipline already used at every other call site that drops mmu_lock while holding a nested-guest pointer.
--- arch/powerpc/kvm/book3s_hv_nested.c | 2 ++ 1 file changed, 2 insertions(+)
arch/powerpc/kvm/book3s_hv_nested.c +2 -0
@@ -1204,8 +1204,10 @@ static void kvmhv_emulate_tlbie_all_lpid(struct kvm_vcpu *vcpu, int ric)spin_lock (&kvm->mmu_lock);idr_for_each_entry (&kvm->arch.kvm_nested_guest_idr, gp, lpid) { + ++gp->refcnt;spin_unlock (&kvm->mmu_lock);kvmhv_emulate_tlbie_lpid (vcpu, gp, ric); +kvmhv_put_nested (gp);spin_lock (&kvm->mmu_lock); }spin_unlock (&kvm->mmu_lock);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 191/438 KVM: PPC: Book3S HV: fix secure device page leak on uv_page_in() failure
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Amit Machhiwal <[email protected]>
[ Upstream commit 0a416ee20bcccddf91ca5b63696a23b9d11d73aa ]
In kvmppc_svm_page_in(), if uv_page_in() fails after kvmppc_uvmem_get_page() has succeeded, the secure device page is never released. kvmppc_uvmem_get_page() sets a bit in kvmppc_uvmem_bitmap, allocates a kvmppc_uvmem_page_pvt struct, marks the GFN as KVMPPC_GFN_UVMEM_PFN, and calls zone_device_page_init() which sets refcount=1 and locks the page. The subsequent goto out_finalize skips the *mig.dst assignment, so migrate_vma_finalize() is a no-op for the page, and none of those resources are ever reclaimed.
Each occurrence permanently consumes one entry from the firmware-bounded secure memory pool (kvmppc_uvmem_bitmap), leaks pvt, and leaves the GFN marked as secure — making it unusable for the lifetime of the VM.
The twin __kvmppc_svm_page_out() already handles the analogous uv_page_out() failure correctly with unlock_page(dpage); __free_page(dpage). Apply the same pattern here: unlock_page() followed by put_page(), which chains through free_zone_device_folio() into kvmppc_uvmem_folio_free() to clear the bitmap bit, free pvt, and reset the GFN state.
Reachable whenever uv_page_in() returns an error (e.g. UV pool exhaustion) on any POWER9/10 + Ultravisor/PEF system.
--- arch/powerpc/kvm/book3s_hv_uvmem.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
arch/powerpc/kvm/book3s_hv_uvmem.c +4 -1
@@ -779,8 +779,11 @@ static int kvmppc_svm_page_in(struct vm_area_struct *vma,if (spage) { ret =uv_page_in (kvm->arch.lpid, pfn << page_shift, gpa,0 , page_shift); -if (ret) +if (ret) { +unlock_page (dpage); +put_page (dpage); goto out_finalize; + } } }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 192/438 powerpc/iommu: Fix the overflow validation in iommu_tce_check_ioba
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Shivaprasad G Bhat <[email protected]>
[ Upstream commit 0b271f7d7f5ed45bc498a03ce0aa9cfd8402fc71 ]
The commit b1af23d836f8 ("KVM: PPC: iommu: Unify TCE checking") unified IOBA parameter checking across KVM and VFIO into iommu_tce_check_ioba(). While doing so, the passed in argument npages is ignored and constant value '1' is used leaving out a possible overflow as the callers can legitimately be using npages > 1 for H_STUFF_TCE or H_PUT_TCE_INDIRECT cases.
Fix this by accounting for 'npages', checking for arithmetic overflow, and verifying that the entire requested range (ioba - offset + npages) does not exceed the table capacity 'size'.
--- arch/powerpc/kernel/iommu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
arch/powerpc/kernel/iommu.c +1 -1
@@ -1076,7 +1076,7 @@ int iommu_tce_check_ioba(unsigned long page_shift,if (ioba < offset)return -EINVAL ; -if ((ioba +1 ) > (offset + size)) +if ((ioba + npages < ioba) || (ioba - offset + npages > size))return -EINVAL ;return 0 ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 193/438 Revert "drm/i915/display: Clear SEL_FETCH_PLANE_CTL on plane disable"
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Nemesa Garg <[email protected]>
[ Upstream commit a26204be587c57bd5c54fa513be26c4fd7bf252d ]
This reverts commit 7f1172a2ac0d7e50850785e2e65789c8aac8411a.
This commit replaced the crtc_state->enable_psr2_sel_fetch guard in icl_plane_disable_sel_fetch_arm() and i9xx_cursor_disable_sel_fetch_arm() with HAS_PSR2_SEL_FETCH(). This is a display version check and says nothing about the pipe, so every plane and cursor disable on a display 12+ platform started writing SEL_FETCH_PLANE_CTL() / SEL_FETCH_CUR_CTL(), including on pipes that do not implement them. It shows up as an unclaimed register access on pipes driving HDMI where selective fetch was never enabled.
The stale selective fetch enable bit that commit addressed is handled in the next patch.
--- drivers/gpu/drm/i915/display/intel_cursor.c | 15 +++++---------- .../gpu/drm/i915/display/skl_universal_plane.c | 15 +++++---------- 2 files changed, 10 insertions(+), 20 deletions(-)
drivers/gpu/drm/i915/display/intel_cursor.c +5 -10
@@ -530,18 +530,13 @@ static int i9xx_check_cursor(struct intel_crtc_state *crtc_state, }static void i9xx_cursor_disable_sel_fetch_arm (struct intel_dsb *dsb, -struct intel_plane *plane) +struct intel_plane *plane, +const struct intel_crtc_state *crtc_state) {struct intel_display *display =to_intel_display (plane);enum pipe pipe = plane->pipe; -/* -* Clear this whenever the hardware has selective fetch, not just when -* the current state uses it. The cursor may have been enabled with -* selective fetch earlier and had its enable bit orphaned when the -* feature was switched off. -*/ -if (!HAS_PSR2_SEL_FETCH (display)) +if (!crtc_state->enable_psr2_sel_fetch)return ;intel_de_write_dsb (display, dsb,SEL_FETCH_CUR_CTL (pipe),0 );
@@ -591,7 +586,7 @@ static void i9xx_cursor_update_sel_fetch_arm(struct intel_dsb *dsb,if (crtc_state->enable_psr2_su_region_et)wa_16021440873 (dsb, plane, crtc_state, plane_state);else -i9xx_cursor_disable_sel_fetch_arm (dsb, plane); +i9xx_cursor_disable_sel_fetch_arm (dsb, plane, crtc_state); } }
@@ -700,7 +695,7 @@ static void i9xx_cursor_update_arm(struct intel_dsb *dsb,if (plane_state)i9xx_cursor_update_sel_fetch_arm (dsb, plane, crtc_state, plane_state);else -i9xx_cursor_disable_sel_fetch_arm (dsb, plane); +i9xx_cursor_disable_sel_fetch_arm (dsb, plane, crtc_state); if (plane->cursor.base != base || plane->cursor.size != fbc_ctl ||
drivers/gpu/drm/i915/display/skl_universal_plane.c +5 -10
@@ -879,18 +879,13 @@ skl_plane_disable_arm(struct intel_dsb *dsb, }static void icl_plane_disable_sel_fetch_arm (struct intel_dsb *dsb, -struct intel_plane *plane) +struct intel_plane *plane, +const struct intel_crtc_state *crtc_state) {struct intel_display *display =to_intel_display (plane);enum pipe pipe = plane->pipe; -/* -* Clear this whenever the hardware has selective fetch, not just when -* the current state uses it. The plane may have been enabled with -* selective fetch earlier and had its enable bit orphaned when the -* feature was switched off. -*/ -if (!HAS_PSR2_SEL_FETCH (display)) +if (!crtc_state->enable_psr2_sel_fetch)return ;intel_de_write_dsb (display, dsb,SEL_FETCH_PLANE_CTL (pipe, plane->id),0 );
@@ -926,7 +921,7 @@ icl_plane_disable_arm(struct intel_dsb *dsb,skl_write_plane_wm (dsb, plane, crtc_state); -icl_plane_disable_sel_fetch_arm (dsb, plane); +icl_plane_disable_sel_fetch_arm (dsb, plane, crtc_state);if (plane_has_normalizer (plane)) intel_de_write_dsb(display, dsb,
@@ -1646,7 +1641,7 @@ static void icl_plane_update_sel_fetch_arm(struct intel_dsb *dsb,intel_de_write_dsb (display, dsb,SEL_FETCH_PLANE_CTL (pipe, plane->id),SEL_FETCH_PLANE_CTL_ENABLE ); else -icl_plane_disable_sel_fetch_arm (dsb ,plane ); +icl_plane_disable_sel_fetch_arm (dsb ,plane ,crtc_state ); } static void
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 194/438 futex: Also allocate private hash on vfork()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Peter Zijlstra <[email protected]>
[ Upstream commit b61b6f95d6722ddbbbd09e689fa41b55fd36f9a5 ]
As Jann demonstrated, it is entirely feasible to access the mm through vfork(). Therefore we need to allocate a private hash on vfork() as well as any other CLONE_VM user.
Specifically, it must be avoided to have (private) futex waiters before allocating the private hash.
--- kernel/fork.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
kernel/fork.c +2 -2
@@ -1977,9 +1977,9 @@ static bool need_futex_hash_allocate_default(u64 clone_flags) {/* * Allocate a default futex hash for any sibling that will -* share the parent's mm, except vfork. +* share the parent's mm. */ -return (clone_flags & (CLONE_VM |CLONE_VFORK )) ==CLONE_VM ; +return clone_flags &CLONE_VM ; }/*
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 195/438 drm/xe/i2c: Disable IRQ on unbind
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Raag Jadav <[email protected]>
[ Upstream commit f0e9f963a3d209d7dc7ddd61116118ab5da2797d ]
Currently, struct xe_i2c is freed before SGUnit IRQ is disabled in unbind path, leaving a potential UAF in case I2C IRQ is hit during this small window. Explicitly disable I2C IRQ in xe_i2c_remove() and fix this.
--- drivers/gpu/drm/xe/xe_i2c.c | 3 +++ 1 file changed, 3 insertions(+)
drivers/gpu/drm/xe/xe_i2c.c +3 -0
@@ -277,8 +277,10 @@ void xe_i2c_pm_resume(struct xe_device *xe, bool d3cold)static void xe_i2c_remove (void *data) {struct xe_i2c *i2c = data; +struct xe_device *xe =tile_to_xe (i2c->mmio->tile);unsigned int i; +xe_i2c_irq_reset (xe);xe_amc_exit (i2c);for (i =0 ; i <XE_I2C_MAX_CLIENTS ; i++) {
@@ -288,6 +290,7 @@ static void xe_i2c_remove(void *data)bus_unregister_notifier (&i2c_bus_type, &i2c->bus_notifier);xe_i2c_unregister_adapter (i2c); + xe->i2c =NULL ; }/**
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 196/438 btrfs: handle lack of space when cleaning up verity items
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Daniel Linjama <[email protected]>
[ Upstream commit 76bf149cd0298544631e756670b89c399c7acbca ]
When enable_verity() hits the qgroup limit, rollback_verity() needs its own metadata reservation. When the qgroup limit or lack of space refuses the rollback, the whole filesystem is forced read-only even though the qgroup limit was for one subvolume only. Also orphan cleanup at the next mount fails the same way, so the leftover items are never removed: with -EDQUOT the subvolume stays unreachable, and with -ENOSPC on a full filesystem the next read-write mount fails.
Start transactions with btrfs_start_transaction_fallback_global_rsv() in btrfs_orphan_cleanup(), drop_verity_items() and rollback_verity(). Those calls only delete items and free the space in the end, so they may use the global reserve and skip the qgroup limit, which avoids -ENOSPC and -EDQUOT.
--- fs/btrfs/inode.c | 3 ++- fs/btrfs/verity.c | 18 ++++++++++++++++-- 2 files changed, 18 insertions(+), 3 deletions(-)
fs/btrfs/inode.c +2 -1
@@ -3876,7 +3876,8 @@ int btrfs_orphan_cleanup(struct btrfs_root *root)if (ret) goto out; } - trans =btrfs_start_transaction (root,1 ); +/* Only deletes the orphan. */ + trans =btrfs_start_transaction_fallback_global_rsv (root,1 );if (IS_ERR (trans)) { ret =PTR_ERR (trans); goto out;
fs/btrfs/verity.c +16 -2
@@ -93,6 +93,20 @@ static loff_t merkle_file_pos(const struct inode *inode)return rounded; } +/* +* Start a transaction for removing verity items or the verity orphan. +* +* Like unlink, this only deletes items and frees space in the end, so the +* reservation may come from the global reserve when the filesystem is full +* (-ENOSPC) and is not subject to the qgroup limit (-EDQUOT). Otherwise a +* failed enable could never be cleaned up in either situation. +*/ +static struct btrfs_trans_handle *start_verity_cleanup_trans (struct btrfs_root *root, +unsigned int num_items) +{ +return btrfs_start_transaction_fallback_global_rsv (root, num_items); +} +/* * Drop all the items for this inode with this key_type. *
@@ -120,7 +134,7 @@ static int drop_verity_items(struct btrfs_inode *inode, u8 key_type)while (1 ) {/* 1 for the item being dropped */ - trans =btrfs_start_transaction (root,1 ); + trans =start_verity_cleanup_trans (root,1 );if (IS_ERR (trans))return PTR_ERR (trans);
@@ -466,7 +480,7 @@ static int rollback_verity(struct btrfs_inode *inode)* 1 for updating the inode flag * 1 for deleting the orphan */ - trans =btrfs_start_transaction (root,2 ); + trans =start_verity_cleanup_trans (root,2 );if (IS_ERR (trans)) { ret =PTR_ERR (trans); trans =NULL ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 197/438 ASoC: ux500: Parenthesize MSP_{RX,TX}_CLKPOL_BIT() arguments
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
[ Upstream commit 11fc0048a6930f4fca44fe3bd16a0023e78846a2 ]
arm allmodconfig fails to build with gcc:
In file included from sound/soc/ux500/ux500_msp_i2s.c:20:
sound/soc/ux500/ux500_msp_i2s.h:151:38: error: suggest parentheses around arithmetic in operand of '^' [-Werror=parentheses] sound/soc/ux500/ux500_msp_i2s.c:204:21: note: in expansion of macro 'MSP_TX_CLKPOL_BIT'
cc1: all warnings being treated as errors
The macros never parenthesized their argument:
#define MSP_TX_CLKPOL_BIT(n) ((n & TCKPOL_MASK) << TCKPOL_SHIFT)
That went unnoticed while every caller passed a plain variable, but configure_protocol() now passes an XOR expression, which binds as "a ^ (b & MASK)" rather than "(a ^ b) & MASK", and gcc rightly complains.
No functional change: tx_clk_pol and rx_clk_pol only ever hold MSP_FALLING_EDGE (0) or MSP_RISING_EDGE (1), and bclk_inverted is a bool, so masking before or after the XOR gives the same 0/1 result. Parenthesize the argument anyway - it fixes the build and stops the macros from silently mis-evaluating a future composite argument.
--- sound/soc/ux500/ux500_msp_i2s.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
sound/soc/ux500/ux500_msp_i2s.h +2 -2
@@ -147,8 +147,8 @@ enum msp_direction {#define RCKPOL_MASK BIT(0)#define TCKPOL_MASK BIT(0)#define SPICKM_MASK (BIT(1) | BIT(0)) -#define MSP_RX_CLKPOL_BIT (n) ((n & RCKPOL_MASK) << RCKPOL_SHIFT) -#define MSP_TX_CLKPOL_BIT (n) ((n & TCKPOL_MASK) << TCKPOL_SHIFT) +#define MSP_RX_CLKPOL_BIT (n) (((n) & RCKPOL_MASK) << RCKPOL_SHIFT) +#define MSP_TX_CLKPOL_BIT (n) (((n) & TCKPOL_MASK) << TCKPOL_SHIFT)#define P1ELEN_SHIFT 0#define P1FLEN_SHIFT 3
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 198/438 ASoC: hdmi-codec: Report a change when the channel status moves
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: HyeongJun An <[email protected]>
[ Upstream commit c17ae8c26eac16ad244daef44044d714f68a2ddc ]
The put() callback of "IEC958 Playback Default" stores all 24 channel status bytes and then returns 0. The core notifies userspace only on a positive return, so a write that changes what the get() callback hands back is never announced, and a mixer holding the control open keeps showing the old value.
Compare the stored bytes and return 1 when they move, the way snd_hda_spdif_default_put() does.
The same shape is in img-spdif-out and uniperif_player.
No board with this codec was to hand. The change is a comparison of driver state with no hardware behaviour in it, and mixer-test counts the missing notification as event_missing.
--- sound/soc/codecs/hdmi-codec.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-)
sound/soc/codecs/hdmi-codec.c +5 -1
@@ -425,10 +425,14 @@ static int hdmi_codec_iec958_default_put(struct snd_kcontrol *kcontrol,struct snd_soc_component *component =snd_kcontrol_chip (kcontrol);struct hdmi_codec_priv *hcp =snd_soc_component_get_drvdata (component); +if (!memcmp (hcp->iec_status, ucontrol->value.iec958.status, +sizeof (hcp->iec_status))) +return 0 ; +memcpy (hcp->iec_status, ucontrol->value.iec958.status,sizeof (hcp->iec_status)); -return 0 ; +return 1 ; } static int hdmi_codec_iec958_mask_get(struct snd_kcontrol *kcontrol,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 199/438 ASoC: cs-amp-lib: Prevent NULL pointer if efi variable is zero length
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Richard Fitzgerald <[email protected]>
[ Upstream commit 3482062c786ce4233f8ed3224d824184f53ec154 ]
In cs_amp_alloc_get_efi_variable() the first call to cs_amp_get_efi_variable() might return EFI_SUCCESS if the variable exists with zero length. Trap this and return -ENOENT to prevent returning an unexpected NULL pointer.
The first cs_amp_get_efi_variable() call was assumed to return EFI_BUFFER_TOO_SMALL if the variable existed, but if instead it returned EFI_SUCCESS this would be converted to 0 by cs_amp_convert_efi_status() and then be returned as a NULL pointer.
--- sound/soc/codecs/cs-amp-lib.c | 2 ++ 1 file changed, 2 insertions(+)
sound/soc/codecs/cs-amp-lib.c +2 -0
@@ -314,6 +314,8 @@ static void *cs_amp_alloc_get_efi_variable(efi_char16_t *name,unsigned long size =0 ; status =cs_amp_get_efi_variable (name, guid,NULL , &size,NULL ); +if (status ==EFI_SUCCESS ) +return ERR_PTR (-ENOENT );if (status !=EFI_BUFFER_TOO_SMALL )return ERR_PTR (cs_amp_convert_efi_status (status));
Signature · 1 line
2.53.0
PATCH 200/438 ASoC: amd: acp: bounds-check SoundWire link ID in machine drivers
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Vijendar Mukunda <[email protected]>
[ Upstream commit 29218a4d11a31a8157389bc2b9e62dd768d7ea42 ]
Add a bounds check in create_sdw_dailink() to validate that the SoundWire link ID derived from link_mask does not exceed the maximum supported by the platform. If the link ID is out of range or link_mask is zero, log an error and return -EINVAL to prevent accessing invalid CPU pin ID tables.
Applied to both acp-sdw-sof-mach.c and acp-sdw-legacy-mach.c.
--- sound/soc/amd/acp/acp-sdw-legacy-mach.c | 10 ++++++++++ sound/soc/amd/acp/acp-sdw-sof-mach.c | 9 +++++++++ 2 files changed, 19 insertions(+)
sound/soc/amd/acp/acp-sdw-legacy-mach.c +10 -0
@@ -205,6 +205,16 @@ static int create_sdw_dailink(struct snd_soc_card *card,return -EINVAL ; } +if (!soc_end->link_mask) { +dev_err (dev,"invalid zero link_mask\n" ); +return -EINVAL ; + } +if ((ffs (soc_end->link_mask) -1 ) >= amd_ctx->max_sdw_links) { +dev_err (dev,"link_id %d exceeds max_sdw_links %d\n" , +ffs (soc_end->link_mask) -1 , amd_ctx->max_sdw_links); +return -EINVAL ; + } +switch (amd_ctx->acp_rev) {case ACP63_PCI_REV : ret = get_acp63_cpu_pin_id(ffs(soc_end->link_mask - 1),
sound/soc/amd/acp/acp-sdw-sof-mach.c +9 -0
@@ -121,6 +121,15 @@ static int create_sdw_dailink(struct snd_soc_card *card,return -EINVAL ; } +if (!sof_end->link_mask) { +dev_err (dev,"invalid zero link_mask\n" ); +return -EINVAL ; + } +if ((ffs (sof_end->link_mask) -1 ) >= amd_ctx->max_sdw_links) { +dev_err (dev,"link_id %d exceeds max_sdw_links %d\n" , +ffs (sof_end->link_mask) -1 , amd_ctx->max_sdw_links); +return -EINVAL ; + }switch (amd_ctx->acp_rev) {case ACP63_PCI_REV : ret = get_acp63_cpu_pin_id(ffs(sof_end->link_mask - 1),
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 201/438 ASoC: sdw_utils: tidyup .count_sidecar
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kuninori Morimoto <[email protected]>
[ Upstream commit c97f0bf5f705b16d150f2b0d5ce0ee24eee4f68a ]
count_sidecar() is not using *card. Tidyup it.
Current code makes old style / new style conversion difficult. To make future conversions easier to understand, this patch clean up the code a little. but no functional change.
Stable-dep-of: 0b7d55d3a912 ("ASoC: amd: acp: refactor codec config count in SOF SoundWire machine driver")
--- include/sound/soc_sdw_utils.h | 28 ++++++++++---------- sound/soc/sdw_utils/soc_sdw_bridge_cs35l56.c | 4 +-- sound/soc/sdw_utils/soc_sdw_utils.c | 2 +- 3 files changed, 16 insertions(+), 18 deletions(-)
include/sound/soc_sdw_utils.h +14 -14
@@ -44,6 +44,18 @@struct asoc_sdw_codec_info ; +struct asoc_sdw_mc_private { +struct snd_soc_card card; +struct snd_soc_jack sdw_headset; +struct device *headset_codec_dev;/* only one headset per card */ +struct device *amp_dev1, *amp_dev2; +bool append_dai_type; +bool ignore_internal_dmic; +void *private; +unsigned long mc_quirk; +int codec_info_list_count; +}; +struct asoc_sdw_dai_info {const bool direction[2 ];/* playback & capture support */ const char *codec_name;
@@ -88,25 +100,13 @@ struct asoc_sdw_codec_info {int (*codec_card_late_probe)(struct snd_soc_card *card); -int (*count_sidecar)(struct snd_soc_card *card, +int (*count_sidecar)(struct asoc_sdw_mc_private *ctx,int *num_dais,int *num_devs);int (*add_sidecar)(struct snd_soc_card *card,struct snd_soc_dai_link **dai_links,struct snd_soc_codec_conf **codec_conf); }; -struct asoc_sdw_mc_private { -struct snd_soc_card card; -struct snd_soc_jack sdw_headset; -struct device *headset_codec_dev;/* only one headset per card */ -struct device *amp_dev1, *amp_dev2; -bool append_dai_type; -bool ignore_internal_dmic; -void *private; -unsigned long mc_quirk; -int codec_info_list_count; -}; -struct asoc_sdw_endpoint {struct list_head list;
@@ -235,7 +235,7 @@ int asoc_sdw_es9356_amp_init(struct snd_soc_card *card,int asoc_sdw_es9356_exit (struct snd_soc_card *card,struct snd_soc_dai_link *dai_link);/* CS AMP support */ -int asoc_sdw_bridge_cs35l56_count_sidecar (struct snd_soc_card *card, +int asoc_sdw_bridge_cs35l56_count_sidecar (struct asoc_sdw_mc_private *ctx,int *num_dais,int *num_devs); int asoc_sdw_bridge_cs35l56_add_sidecar(struct snd_soc_card *card, struct snd_soc_dai_link **dai_links,
sound/soc/sdw_utils/soc_sdw_bridge_cs35l56.c +1 -3
@@ -99,11 +99,9 @@ static const struct snd_soc_dai_link bridge_dai_template = { SND_SOC_DAILINK_REG(asoc_sdw_bridge_dai), }; -int asoc_sdw_bridge_cs35l56_count_sidecar (struct snd_soc_card *card, +int asoc_sdw_bridge_cs35l56_count_sidecar (struct asoc_sdw_mc_private *ctx,int *num_dais,int *num_devs) { -struct asoc_sdw_mc_private *ctx =snd_soc_card_get_drvdata (card); -if (ctx->mc_quirk &SOC_SDW_SIDECAR_AMPS ) { (*num_dais)++; (*num_devs) +=ARRAY_SIZE (bridge_cs35l56_name_prefixes);
sound/soc/sdw_utils/soc_sdw_utils.c +1 -1
@@ -2045,7 +2045,7 @@ int asoc_sdw_parse_sdw_endpoints(struct snd_soc_card *card, ctx->ignore_internal_dmic |= codec_info->ignore_internal_dmic;if (codec_info->count_sidecar && codec_info->add_sidecar) { - ret = codec_info->count_sidecar (card, &num_dais, num_devs); + ret = codec_info->count_sidecar (ctx, &num_dais, num_devs);if (ret)return ret;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 202/438 ASoC: sdw_utils: tidyup asoc_sdw_parse_sdw_endpoints()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kuninori Morimoto <[email protected]>
[ Upstream commit a1332be2a07090cf422507ec812ce2b9ba0a558a ]
We can avoid to use *card. Tidyup it.
Current code makes old style / new style conversion difficult. To make future conversions easier to understand, this patch clean up the code a little. but no functional change.
Stable-dep-of: 0b7d55d3a912 ("ASoC: amd: acp: refactor codec config count in SOF SoundWire machine driver")
--- include/sound/soc_sdw_utils.h | 3 ++- sound/soc/amd/acp/acp-sdw-legacy-mach.c | 2 +- sound/soc/amd/acp/acp-sdw-sof-mach.c | 2 +- sound/soc/intel/boards/sof_sdw.c | 2 +- sound/soc/sdw_utils/soc_sdw_utils.c | 5 ++--- 5 files changed, 7 insertions(+), 7 deletions(-)
include/sound/soc_sdw_utils.h +2 -1
@@ -182,7 +182,8 @@ struct asoc_sdw_dailink *asoc_sdw_find_dailink(struct asoc_sdw_dailink *dailinks const struct snd_soc_acpi_endpoint *new);int asoc_sdw_get_dai_type (u32 type); -int asoc_sdw_parse_sdw_endpoints(struct snd_soc_card *card, +int asoc_sdw_parse_sdw_endpoints(struct device *dev, + struct asoc_sdw_mc_private *ctx, struct snd_soc_aux_dev *soc_aux, struct asoc_sdw_dailink *soc_dais, struct asoc_sdw_endpoint *soc_ends,
sound/soc/amd/acp/acp-sdw-legacy-mach.c +1 -1
@@ -442,7 +442,7 @@ static int soc_card_dai_links_create(struct snd_soc_card *card)if (!soc_aux)return -ENOMEM ; - ret =asoc_sdw_parse_sdw_endpoints (card, soc_aux, soc_dais, soc_ends, &num_confs); + ret =asoc_sdw_parse_sdw_endpoints (dev, ctx, soc_aux, soc_dais, soc_ends, &num_confs);if (ret <0 )return ret;
sound/soc/amd/acp/acp-sdw-sof-mach.c +1 -1
@@ -312,7 +312,7 @@ static int sof_card_dai_links_create(struct snd_soc_card *card)if (!sof_aux)return -ENOMEM ; - ret =asoc_sdw_parse_sdw_endpoints (card, sof_aux, sof_dais, sof_ends, &num_devs); + ret =asoc_sdw_parse_sdw_endpoints (dev, ctx, sof_aux, sof_dais, sof_ends, &num_devs);if (ret <0 )return ret;
sound/soc/intel/boards/sof_sdw.c +1 -1
@@ -1288,7 +1288,7 @@ static int sof_card_dai_links_create(struct snd_soc_card *card) goto err_dai; } - ret =asoc_sdw_parse_sdw_endpoints (card, sof_aux, sof_dais, sof_ends, &num_confs); + ret =asoc_sdw_parse_sdw_endpoints (dev, ctx, sof_aux, sof_dais, sof_ends, &num_confs);if (ret <0 ) goto err_end;
sound/soc/sdw_utils/soc_sdw_utils.c +2 -3
@@ -1976,14 +1976,13 @@ static int is_sdca_endpoint_present(struct device *dev,return ret; } -int asoc_sdw_parse_sdw_endpoints (struct snd_soc_card *card, +int asoc_sdw_parse_sdw_endpoints (struct device *dev, +struct asoc_sdw_mc_private *ctx,struct snd_soc_aux_dev *soc_aux,struct asoc_sdw_dailink *soc_dais,struct asoc_sdw_endpoint *soc_ends,int *num_devs) { -struct device *dev = card->dev; -struct asoc_sdw_mc_private *ctx =snd_soc_card_get_drvdata (card);struct snd_soc_acpi_mach *mach =dev_get_platdata (dev);struct snd_soc_acpi_mach_params *mach_params = &mach->mach_params;const struct snd_soc_acpi_link_adr *adr_link;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 203/438 ASoC: amd: acp: refactor codec config count in SOF SoundWire machine driver
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Vijendar Mukunda <[email protected]>
[ Upstream commit 0b7d55d3a91200f2b1ed710f525a944b0a7d6369 ]
num_devs was used both as the endpoint count and as the output for asoc_sdw_parse_sdw_endpoints(), which overwrites it with the codec configuration count. Introduce a separate num_confs variable to hold the codec conf count so the two values remain distinct across codec_conf allocation and card->num_configs assignment.
--- sound/soc/amd/acp/acp-sdw-sof-mach.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-)
sound/soc/amd/acp/acp-sdw-sof-mach.c +5 -3
@@ -286,6 +286,7 @@ static int sof_card_dai_links_create(struct snd_soc_card *card)int num_devs =0 ;int num_ends =0 ;int num_aux =0 ; +int num_confs;int num_links;int be_id =0 ;int ret;
@@ -296,6 +297,7 @@ static int sof_card_dai_links_create(struct snd_soc_card *card)return ret; } + num_confs = num_ends;/* One per DAI link, worst case is a DAI link for every endpoint */ struct asoc_sdw_dailink *sof_dais __free(kfree) =kzalloc_objs (*sof_dais, num_ends);
@@ -312,7 +314,7 @@ static int sof_card_dai_links_create(struct snd_soc_card *card)if (!sof_aux)return -ENOMEM ; - ret =asoc_sdw_parse_sdw_endpoints (dev, ctx, sof_aux, sof_dais, sof_ends, &num_devs); + ret =asoc_sdw_parse_sdw_endpoints (dev, ctx, sof_aux, sof_dais, sof_ends, &num_confs);if (ret <0 )return ret;
@@ -324,7 +326,7 @@ static int sof_card_dai_links_create(struct snd_soc_card *card)dev_dbg (dev,"sdw %d, dmic %d" , sdw_be_num, dmic_num); - codec_conf =devm_kcalloc (dev, num_devs,sizeof (*codec_conf),GFP_KERNEL ); + codec_conf =devm_kcalloc (dev, num_confs,sizeof (*codec_conf),GFP_KERNEL );if (!codec_conf)return -ENOMEM ;
@@ -335,7 +337,7 @@ static int sof_card_dai_links_create(struct snd_soc_card *card)return -ENOMEM ; card->codec_conf = codec_conf; - card->num_configs = num_devs; + card->num_configs = num_confs; card->dai_link = dai_links; card->num_links = num_links; card->aux_dev = sof_aux;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 204/438 ASoC: amd: acp: fix ffs() operator precedence for SoundWire link ID
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Vijendar Mukunda <[email protected]>
[ Upstream commit 27098aaf28b96ab4e6891709062c343566d4882b ]
ffs(link_mask - 1) computes ffs on (link_mask - 1) instead of subtracting 1 from the result of ffs(link_mask). For a typical power-of-2 link_mask this returns the wrong link ID, causing cpu_pin_id lookup to select the incorrect SoundWire manager.
Fix the operator precedence to ffs(link_mask) - 1 in both acp-sdw-sof-mach.c and acp-sdw-legacy-mach.c.
--- sound/soc/amd/acp/acp-sdw-legacy-mach.c | 4 ++-- sound/soc/amd/acp/acp-sdw-sof-mach.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-)
sound/soc/amd/acp/acp-sdw-legacy-mach.c +2 -2
@@ -217,7 +217,7 @@ static int create_sdw_dailink(struct snd_soc_card *card,switch (amd_ctx->acp_rev) {case ACP63_PCI_REV : - ret =get_acp63_cpu_pin_id (ffs (soc_end->link_mask -1 ), + ret =get_acp63_cpu_pin_id (ffs (soc_end->link_mask) -1 , *be_id, &cpu_pin_id, dev);if (ret)return ret;
@@ -225,7 +225,7 @@ static int create_sdw_dailink(struct snd_soc_card *card,case ACP70_PCI_REV :case ACP71_PCI_REV :case ACP72_PCI_REV : - ret =get_acp70_cpu_pin_id (ffs (soc_end->link_mask -1 ), + ret =get_acp70_cpu_pin_id (ffs (soc_end->link_mask) -1 , *be_id, &cpu_pin_id, dev);if (ret)return ret;
sound/soc/amd/acp/acp-sdw-sof-mach.c +2 -2
@@ -132,7 +132,7 @@ static int create_sdw_dailink(struct snd_soc_card *card, }switch (amd_ctx->acp_rev) {case ACP63_PCI_REV : - ret =get_acp63_cpu_pin_id (ffs (sof_end->link_mask -1 ), + ret =get_acp63_cpu_pin_id (ffs (sof_end->link_mask) -1 , *be_id, &cpu_pin_id, dev);if (ret)return ret;
@@ -140,7 +140,7 @@ static int create_sdw_dailink(struct snd_soc_card *card,case ACP70_PCI_REV :case ACP71_PCI_REV :case ACP72_PCI_REV : - ret =get_acp70_cpu_pin_id (ffs (sof_end->link_mask -1 ), + ret =get_acp70_cpu_pin_id (ffs (sof_end->link_mask) -1 , *be_id, &cpu_pin_id, dev);if (ret)return ret;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 205/438 net/sched: codel: bound the dropping loop per dequeue call
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jamal Hadi Salim <[email protected]>
[ Upstream commit 7f4a5ec6258fd7c92633ec4b0493fc51166d9398 ]
The CoDel control law schedules the next drop one interval/sqrt(count) after the previous drop, using the configured interval (codel_params.interval). For very small intervals the scheduled step rounds down to zero, so the dropping loop in codel_dequeue() never advances and drains the entire backlog under the qdisc lock in one call - an unprivileged user can trigger a soft lockup this way.
Fix in the shared codel code used by both codel and fq_codel:
1. Make the control-law step at least 1 tick so the dropping loop always moves forward.
2. Cap the dropping loop at CODEL_MAX_DROPS_PER_DEQUEUE (256) drops per codel_dequeue() call, resyncing drop_next to now when the cap is hit: the catch-up owed to the loop grows with the idle gap and the backlog, which no interval threshold can bound. This is a deliberate behaviour change after long idle gaps.
The cap applies to fq_codel (4b549a2ef4be) and the mac80211 TXQ path (fixed interval, cap only).
The target sojourn delay (codel_params.target) is not validated: it does not feed the control law, so a sub-tick value is aggressive rather than deadlock-prone.
Conditions to recreate the bug:
- tc qdisc add dev lo root handle 1: tbf rate 1kbit burst 2kb limit 1000000
- tc qdisc add dev lo parent 1:1 handle 10: codel interval 2us target 1ms noecn limit 1000000 (same for fq_codel)
- unpatched kernel: tc accepts it; a UDP flood under the 1kbit tbf
soft-lockups (watchdog: BUG: soft lockup) while one
codel_dequeue() call drops the backlog under the qdisc lock
- patched kernel: same setup, at most 256 drops per dequeue call,
no soft lockup
Testing: claim reproducer and interval 2us/3us variants run clean; tdc qdisc category passes (see the selftests patch).
--- include/net/codel.h | 5 +++++ include/net/codel_impl.h | 16 +++++++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-)
include/net/codel.h +5 -0
@@ -140,6 +140,11 @@ struct codel_vars {/* needed shift to get a Q0.32 number from rec_inv_sqrt */ #define REC_INV_SQRT_SHIFT (32 - REC_INV_SQRT_BITS) +/* Cap on drops per codel_dequeue() call: the loop's work depends on the +* idle gap and backlog, both outside our control; resync when exceeded. +*/ +#define CODEL_MAX_DROPS_PER_DEQUEUE 256 +/** * struct codel_stats - contains codel shared variables and stats * @maxpacket: largest packet we've seen so far
include/net/codel_impl.h +15 -1
@@ -93,12 +93,17 @@ static void codel_Newton_step(struct codel_vars *vars)* CoDel control_law is t + interval/sqrt(count) * We maintain in rec_inv_sqrt the reciprocal value of sqrt(count) to avoid * both sqrt() and divide operation. +* +* Clamp the increment to at least 1 tick: a very small interval (or a +* large count) can truncate it to zero, stalling the dropping loop. */ static codel_time_t codel_control_law (codel_time_t t,codel_time_t interval,u32 rec_inv_sqrt) { -return t +reciprocal_scale (interval, rec_inv_sqrt <<REC_INV_SQRT_SHIFT ); +return t +max_t (u32,1 , +reciprocal_scale (interval, + rec_inv_sqrt <<REC_INV_SQRT_SHIFT )); } static bool codel_should_drop(const struct sk_buff *skb,
@@ -154,6 +159,7 @@ static struct sk_buff *codel_dequeue(void *ctx, codel_skb_dequeue_t dequeue_func) {struct sk_buff *skb =dequeue_func (vars, ctx); +unsigned int drops =0 ;codel_time_t now;bool drop;
@@ -180,6 +186,14 @@ static struct sk_buff *codel_dequeue(void *ctx,*/ while (vars->dropping &&codel_time_after_eq (now, vars->drop_next)) { +if (++drops >CODEL_MAX_DROPS_PER_DEQUEUE ) { +/* fell far behind the schedule */ +WRITE_ONCE (vars->drop_next, +codel_control_law (now, + params->interval, + vars->rec_inv_sqrt)); +break ; + }/* dont care of possible wrap * since there is no more divide. */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 206/438 net: do not advance stack index from dev_fwd_path()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pablo Neira Ayuso <[email protected]>
[ Upstream commit 5deda60c56eeeab25beb10cf4d48e07587076b11 ]
Update stack index from dev_fill_forward_path() instead, once the forward path slot has been populated.
Stable-dep-of: 150dba2c69e9 ("net: remove WARN_ON_ONCE() from the dev_fill_forward_path() loop check")
--- net/core/dev.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
net/core/dev.c +4 -4
@@ -742,12 +742,10 @@ EXPORT_SYMBOL_GPL(dev_fill_metadata_dst);static struct net_device_path *dev_fwd_path (struct net_device_path_stack *stack) { -int k = stack->num_paths++; - -if (k >=NET_DEVICE_PATH_STACK_MAX ) +if (stack->num_paths +1 >NET_DEVICE_PATH_STACK_MAX )return NULL ; -return &stack->path[k]; +return &stack->path[stack->num_paths]; } int dev_fill_forward_path(const struct net_device *dev, const u8 *daddr,
@@ -773,6 +771,7 @@ int dev_fill_forward_path(const struct net_device *dev, const u8 *daddr,if (ret <0 )return -1 ; + stack->num_paths++;if (WARN_ON_ONCE (last_dev == ctx.dev))return -1 ; }
@@ -785,6 +784,7 @@ int dev_fill_forward_path(const struct net_device *dev, const u8 *daddr,return -1 ; path->type =DEV_PATH_ETHERNET ; path->dev = ctx.dev; + stack->num_paths++;return ret; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 207/438 net: pass dst via net_device_path in dev_fill_forward_path()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pablo Neira Ayuso <[email protected]>
[ Upstream commit 0ad8404e776698de4dac5cc0df3be68d344e741c ]
Add dst_entry to tunnel device path, this will allow us to remove a duplicated route lookup.
This is a preparation patch to retrieve the tunnel route directly from the .fill_forward_path. This new dst_entry in the tunnel will be used by a follow up patch.
Since dst_release() works fine on NULL interface, this is still noop until the flowtable starts using this.
Add a new dev_fill_forward_path_release() function to drop the refcount on the tunnel device route and use it in case of error out. Export it so to drop the refcount on the tunnel route at a later stage.
Adjust existing drivers that recycle dev_fill_forward_path() to call dev_fill_forward_path_release() for safety reasons.
Stable-dep-of: 150dba2c69e9 ("net: remove WARN_ON_ONCE() from the dev_fill_forward_path() loop check")
--- drivers/net/ethernet/airoha/airoha_ppe.c | 10 ++++-- .../net/ethernet/mediatek/mtk_ppe_offload.c | 10 ++++-- include/linux/netdevice.h | 2 ++ net/core/dev.c | 36 ++++++++++++++++--- 4 files changed, 47 insertions(+), 11 deletions(-)
drivers/net/ethernet/airoha/airoha_ppe.c +7 -3
@@ -293,14 +293,18 @@ static int airoha_ppe_get_wdma_info(struct net_device *dev, const u8 *addr,return err; path = &stack.path[stack.num_paths -1 ]; -if (path->type !=DEV_PATH_MTK_WDMA ) -return -EINVAL ; +if (path->type !=DEV_PATH_MTK_WDMA ) { + err = -EINVAL ; + goto err_out; + } info->idx = path->mtk_wdma.wdma_idx; info->bss = path->mtk_wdma.bss; info->wcid = path->mtk_wdma.wcid; +err_out: +dev_fill_forward_path_release (&stack); -return 0 ; +return err; }static int airoha_get_dsa_port (struct net_device **dev)
drivers/net/ethernet/mediatek/mtk_ppe_offload.c +7 -3
@@ -108,16 +108,20 @@ mtk_flow_get_wdma_info(struct net_device *dev, const u8 *addr, struct mtk_wdma_ireturn err; path = &stack.path[stack.num_paths -1 ]; -if (path->type !=DEV_PATH_MTK_WDMA ) -return -1 ; +if (path->type !=DEV_PATH_MTK_WDMA ) { + err = -EINVAL ; + goto err_out; + } info->wdma_idx = path->mtk_wdma.wdma_idx; info->queue = path->mtk_wdma.queue; info->bss = path->mtk_wdma.bss; info->wcid = path->mtk_wdma.wcid; info->amsdu = path->mtk_wdma.amsdu; +err_out: +dev_fill_forward_path_release (&stack); -return 0 ; +return err; }
include/linux/netdevice.h +2 -0
@@ -894,6 +894,7 @@ struct net_device_path {u8 h_dest[ETH_ALEN ]; } encap;struct { +struct dst_entry *dst;union {struct in_addr src_v4;struct in6_addr src_v6;
@@ -3424,6 +3425,7 @@ int dev_get_iflink(const struct net_device *dev);int dev_fill_metadata_dst (struct net_device *dev,struct sk_buff *skb);int dev_fill_forward_path (const struct net_device *dev,const u8 *daddr,struct net_device_path_stack *stack); +void dev_fill_forward_path_release (struct net_device_path_stack *stack);struct net_device *dev_get_by_name (struct net *net,const char *name);struct net_device *dev_get_by_name_rcu (struct net *net,const char *name);struct net_device *__dev_get_by_name (struct net *net,const char *name);
net/core/dev.c +31 -5
@@ -748,6 +748,27 @@ static struct net_device_path *dev_fwd_path(struct net_device_path_stack *stack)return &stack->path[stack->num_paths]; } +void dev_fill_forward_path_release (struct net_device_path_stack *stack) +{ +struct net_device_path *path; +int k; + +if (stack->num_paths ==0 ) +return ; + +for (k = stack->num_paths -1 ; k >=0 ; k--) { + path = &stack->path[k]; +switch (path->type) { +case DEV_PATH_TUN : +dst_release (path->tun.dst); +break ; +default : +break ; + } + } +} +EXPORT_SYMBOL_GPL (dev_fill_forward_path_release); +int dev_fill_forward_path (const struct net_device *dev,const u8 *daddr,struct net_device_path_stack *stack) {
@@ -764,16 +785,16 @@ int dev_fill_forward_path(const struct net_device *dev, const u8 *daddr, last_dev = ctx.dev; path =dev_fwd_path (stack);if (!path) -return -1 ; + goto err_out;memset (path,0 ,sizeof (struct net_device_path )); ret = ctx.dev->netdev_ops->ndo_fill_forward_path (&ctx, path);if (ret <0 ) -return -1 ; + goto err_out; stack->num_paths++;if (WARN_ON_ONCE (last_dev == ctx.dev)) -return -1 ; + goto err_out; }if (!ctx.dev)
@@ -781,12 +802,17 @@ int dev_fill_forward_path(const struct net_device *dev, const u8 *daddr, path =dev_fwd_path (stack);if (!path) -return -1 ; + goto err_out; + path->type =DEV_PATH_ETHERNET ; path->dev = ctx.dev; stack->num_paths++; -return ret; +return 0 ; +err_out: +dev_fill_forward_path_release (stack); + +return -1 ; }EXPORT_SYMBOL_GPL (dev_fill_forward_path);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 208/438 net: pass net_device_path_ctx to dev_fill_forward_path()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Lorenzo Bianconi <[email protected]>
[ Upstream commit 95133a416809c7e822da4023b7f4193ef2620796 ]
Refactor dev_fill_forward_path() to take a struct net_device_path_ctx pointer instead of a (dev, daddr) pair, so the caller can build and populate the context up front and keep it after the forward path walk.
This allows additional fields (e.g. vlan and ether_type) to be carried in the context and shared with ndo_fill_forward_path implementations, instead of being reconstructed on the stack inside the core helper.
Update the mtk_ppe_offload, airoha_ppe and nf_flow_table_path callers to allocate and fill the context before invoking dev_fill_forward_path(). The network topology resolution behaviour is unchanged.
This is a preliminary patch to enable HW flowtable offload for IPv4 over IPv6 tunnels.
Stable-dep-of: 150dba2c69e9 ("net: remove WARN_ON_ONCE() from the dev_fill_forward_path() loop check")
--- drivers/net/ethernet/airoha/airoha_ppe.c | 7 ++++++- .../net/ethernet/mediatek/mtk_ppe_offload.c | 7 ++++++- include/linux/netdevice.h | 2 +- net/core/dev.c | 18 +++++++----------- net/netfilter/nf_flow_table_path.c | 7 ++++++- 5 files changed, 26 insertions(+), 15 deletions(-)
drivers/net/ethernet/airoha/airoha_ppe.c +6 -1
@@ -280,14 +280,19 @@ static int airoha_ppe_get_wdma_info(struct net_device *dev, const u8 *addr, struct airoha_wdma_info *info) {struct net_device_path_stack stack; +struct net_device_path_ctx ctx = { + .dev = dev, + };struct net_device_path *path;int err;if (!dev)return -ENODEV ; +ether_addr_copy (ctx.daddr, addr); +rcu_read_lock (); - err =dev_fill_forward_path (dev, addr, &stack); + err =dev_fill_forward_path (&ctx, &stack);rcu_read_unlock ();if (err)return err;
drivers/net/ethernet/mediatek/mtk_ppe_offload.c +6 -1
@@ -92,6 +92,9 @@ static int mtk_flow_get_wdma_info(struct net_device *dev, const u8 *addr, struct mtk_wdma_info *info) { struct net_device_path_stack stack; +struct net_device_path_ctx ctx = { + .dev = dev, + };struct net_device_path *path;int err;
@@ -101,8 +104,10 @@ mtk_flow_get_wdma_info(struct net_device *dev, const u8 *addr, struct mtk_wdma_iif (!IS_ENABLED (CONFIG_NET_MEDIATEK_SOC_WED ))return -1 ; +ether_addr_copy (ctx.daddr, addr); +rcu_read_lock (); - err =dev_fill_forward_path (dev, addr, &stack); + err =dev_fill_forward_path (&ctx, &stack);rcu_read_unlock ();if (err)return err;
include/linux/netdevice.h +1 -1
@@ -3423,7 +3423,7 @@ void dev_remove_offload(struct packet_offload *po);int dev_get_iflink (const struct net_device *dev);int dev_fill_metadata_dst (struct net_device *dev,struct sk_buff *skb); -int dev_fill_forward_path (const struct net_device *dev,const u8 *daddr, +int dev_fill_forward_path (struct net_device_path_ctx *ctx,struct net_device_path_stack *stack);void dev_fill_forward_path_release (struct net_device_path_stack *stack);struct net_device *dev_get_by_name (struct net *net,const char *name);
net/core/dev.c +7 -11
@@ -769,35 +769,31 @@ void dev_fill_forward_path_release(struct net_device_path_stack *stack) }EXPORT_SYMBOL_GPL (dev_fill_forward_path_release); -int dev_fill_forward_path (const struct net_device *dev,const u8 *daddr, +int dev_fill_forward_path (struct net_device_path_ctx *ctx,struct net_device_path_stack *stack) {const struct net_device *last_dev; -struct net_device_path_ctx ctx = { - .dev = dev, - };struct net_device_path *path;int ret =0 ; -memcpy (ctx.daddr, daddr,sizeof (ctx.daddr)); stack->num_paths =0 ; -while (ctx.dev && ctx.dev->netdev_ops->ndo_fill_forward_path) { - last_dev = ctx.dev; +while (ctx->dev && ctx->dev->netdev_ops->ndo_fill_forward_path) { + last_dev = ctx->dev; path =dev_fwd_path (stack);if (!path) goto err_out;memset (path,0 ,sizeof (struct net_device_path )); - ret = ctx.dev->netdev_ops->ndo_fill_forward_path (&ctx, path); + ret = ctx->dev->netdev_ops->ndo_fill_forward_path (ctx, path);if (ret <0 ) goto err_out; stack->num_paths++; -if (WARN_ON_ONCE (last_dev == ctx.dev)) +if (WARN_ON_ONCE (last_dev == ctx->dev)) goto err_out; } -if (!ctx.dev) +if (!ctx->dev)return ret; path =dev_fwd_path (stack);
@@ -805,7 +801,7 @@ int dev_fill_forward_path(const struct net_device *dev, const u8 *daddr, goto err_out; path->type =DEV_PATH_ETHERNET ; - path->dev = ctx.dev; + path->dev = ctx->dev; stack->num_paths++;return 0 ;
net/netfilter/nf_flow_table_path.c +6 -1
@@ -50,6 +50,9 @@ static int nft_dev_fill_forward_path(const struct nf_flow_route *route, {const void *daddr = &ct->tuplehash[!dir].tuple.src.u3;struct net_device *dev = dst_cache->dev; +struct net_device_path_ctx ctx = { + .dev = dev, + };struct neighbour *n;u8 nud_state;
@@ -72,7 +75,9 @@ static int nft_dev_fill_forward_path(const struct nf_flow_route *route,return -1 ; out: -return dev_fill_forward_path (dev, ha, stack); +ether_addr_copy (ctx.daddr, ha); + +return dev_fill_forward_path (&ctx, stack); }struct nft_forward_info {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 209/438 net: remove WARN_ON_ONCE() from the dev_fill_forward_path() loop check
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Farhad Alemi <[email protected]>
[ Upstream commit 150dba2c69e93302af24a0c868eebe4871e2e107 ]
ipip_fill_forward_path() and ip6_tnl_fill_forward_path() look up the route to the tunnel's remote endpoint and set ctx->dev to its device, which is the tunnel itself when that route resolves back to the tunnel. dev_fill_forward_path() then makes no progress and trips WARN_ON_ONCE(last_dev == ctx->dev) as soon as a flowtable tries to offload a flow through the tunnel. That routing loop is a configuration any CAP_NET_ADMIN user can set up, and ip_tunnel_xmit() and ip6_tnl_xmit() already treat it as a tx error, so remove the warning and just fail the walk, as commit 008e7a7c293b ("net: remove WARN_ON_ONCE when accessing forward path array") did for the path stack overflow.
--- net/core/dev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/core/dev.c +1 -1
@@ -789,7 +789,7 @@ int dev_fill_forward_path(struct net_device_path_ctx *ctx, goto err_out; stack->num_paths++; -if (WARN_ON_ONCE (last_dev == ctx->dev)) +if (last_dev == ctx->dev) goto err_out; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 210/438 net: netsec: fix device_node reference leak on phy_np
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yige Jiang <[email protected]>
[ Upstream commit 5ae916fabca141b79b32e2e57f3c915c0f1e1b2e ]
netsec_of_probe() takes a reference on the PHY device_node with of_parse_phandle() and stores it in priv->phy_np, but the driver never drops it. One device_node reference is leaked per probe, on the success path as well as on every error path reached after netsec_of_probe().
Neither consumer takes ownership. of_mdio_parse_addr() is a static inline taking a const struct device_node * that only reads the "reg" property. of_phy_connect() borrows as well: of_phy_get_and_connect() in drivers/net/mdio/of_mdio.c brackets its own call with of_node_get() at :364 and of_node_put() at :373, which would be a double put if of_phy_connect() consumed the reference.
The node is still in use at netsec_netdev_open() time, where it is passed to of_phy_connect(), so it has device lifetime. Release it at the probe error label, which every failure path after the acquire funnels through, and in netsec_remove(). Both releases precede free_netdev(), since priv is netdev_priv(ndev). The ACPI probe path leaves priv->phy_np NULL and of_node_put(NULL) is a no-op.
There is no end-user visible symptom on currently supported platforms:
a device_node is only freed once OF_DYNAMIC is enabled and the node has been detached, so on a static device tree the imbalance is inert. It is observable as a refcount that grows across bind/unbind cycles, and would matter under device tree overlays.
Found by static analysis of reference acquire/release pairing rather than from a runtime report. No reproducer was produced and the change has not been runtime tested; it is compile-tested only (arm64, CONFIG_SNI_NETSEC=m via COMPILE_TEST).
--- drivers/net/ethernet/socionext/netsec.c | 2 ++ 1 file changed, 2 insertions(+)
drivers/net/ethernet/socionext/netsec.c +2 -0
@@ -2149,6 +2149,7 @@ static int netsec_probe(struct platform_device *pdev)pm_runtime_put_sync (&pdev->dev);pm_runtime_disable (&pdev->dev); free_ndev: +of_node_put (priv->phy_np);free_netdev (ndev);dev_err (&pdev->dev,"init failed\n" );
@@ -2166,6 +2167,7 @@ static void netsec_remove(struct platform_device *pdev)netif_napi_del (&priv->napi);pm_runtime_disable (&pdev->dev); +of_node_put (priv->phy_np);free_netdev (priv->ndev); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 211/438 eth: fbnic: ring the doorbell if a burst ends in a drop
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jakub Kicinski <[email protected]>
[ Upstream commit 490599ab23134962a6d18a024e84541d77bdb999 ]
fbnic_tx_map() skips the doorbell write, and the completion request, for every packet handed to it with xmit_more set, counting on the packet which ends the burst to publish them all. When that packet is dropped instead - skb_put_padto(), skb_cow_head() or a DMA mapping failure - nothing rings. The descriptors of the preceding packets stay invisible to the HW until the next transmit on that queue, which for a burst-then-idle workload may never come.
Remember the meta descriptor of the last packet left without a doorbell and flush it from the error paths. The completion request has to be set on that descriptor rather than simply writing the tail, otherwise the HW would transmit the packets but never report a head, and the ring would fill up and stall for good.
This is very similar to Joe's recent series of fixes for bnxt. Not seen in real life, reproduced under QEMU with failure injection.
--- drivers/net/ethernet/meta/fbnic/fbnic_txrx.c | 42 +++++++++++++++----- drivers/net/ethernet/meta/fbnic/fbnic_txrx.h | 9 ++++- 2 files changed, 40 insertions(+), 11 deletions(-)
drivers/net/ethernet/meta/fbnic/fbnic_txrx.c +33 -9
@@ -311,6 +311,29 @@ fbnic_rx_csum(u64 rcd, struct sk_buff *skb, struct fbnic_ring *rcq, } } +static void fbnic_tx_doorbell (struct fbnic_ring *ring,__le64 *meta) +{ + *meta |=cpu_to_le64 (FBNIC_TWD_FLAG_REQ_COMPLETION ); + ring->deferred_meta =-1 ; + +/* Force DMA writes to flush before writing to tail */ +dma_wmb (); + +writel (ring->tail, ring->doorbell); +} + +/* Packets handed to us with xmit_more set are left in the ring without a +* doorbell, and without a completion request, in the expectation that the +* packet ending the burst will ring for all of them. If that packet gets +* dropped instead we have to ring here, otherwise the descriptors sit in +* the ring until the next transmit, which may never come. +*/ +static void fbnic_tx_flush_doorbell (struct fbnic_ring *ring) +{ +if (ring->deferred_meta >=0 ) +fbnic_tx_doorbell (ring, &ring->desc[ring->deferred_meta]); +} +static bool fbnic_tx_map (struct fbnic_ring *ring,struct sk_buff *skb,__le64 *meta) {
@@ -378,14 +401,10 @@ fbnic_tx_map(struct fbnic_ring *ring, struct sk_buff *skb, __le64 *meta)/* Verify there is room for another packet */ fbnic_maybe_stop_tx (skb->dev, ring,FBNIC_MAX_SKB_DESC ); -if (fbnic_tx_sent_queue (skb, ring)) { - *meta |=cpu_to_le64 (FBNIC_TWD_FLAG_REQ_COMPLETION ); - -/* Force DMA writes to flush before writing to tail */ -dma_wmb (); - -writel (tail, ring->doorbell); - } +if (fbnic_tx_sent_queue (skb, ring)) +fbnic_tx_doorbell (ring, meta); +else + ring->deferred_meta = meta - ring->desc;return false; dma_error:
@@ -425,8 +444,10 @@ fbnic_xmit_frame_ring(struct sk_buff *skb, struct fbnic_ring *ring)* otherwise try next time */ desc_needed =skb_shinfo (skb)->nr_frags +10 ; -if (fbnic_maybe_stop_tx (skb->dev, ring, desc_needed)) +if (fbnic_maybe_stop_tx (skb->dev, ring, desc_needed)) { +fbnic_tx_flush_doorbell (ring);return NETDEV_TX_BUSY ; + } *meta =cpu_to_le64 (FBNIC_TWD_FLAG_DEST_MAC );
@@ -447,6 +468,8 @@ fbnic_xmit_frame_ring(struct sk_buff *skb, struct fbnic_ring *ring) err_free:dev_kfree_skb_any (skb); err_count: +fbnic_tx_flush_doorbell (ring); +u64_stats_update_begin (&ring->stats.syncp); ring->stats.dropped++;u64_stats_update_end (&ring->stats.syncp);
@@ -2491,6 +2514,7 @@ static void fbnic_enable_twq0(struct fbnic_ring *twq)fbnic_ring_wr32 (twq,FBNIC_QUEUE_TWQ0_CTL ,FBNIC_QUEUE_TWQ_CTL_RESET ); twq->tail =0 ; twq->head =0 ; + twq->deferred_meta =-1 ;/* Store descriptor ring address and size */ fbnic_ring_wr32 (twq,FBNIC_QUEUE_TWQ0_BAL ,lower_32_bits (twq->dma));
drivers/net/ethernet/meta/fbnic/fbnic_txrx.h +7 -2
@@ -128,9 +128,14 @@ struct fbnic_ring {/* Rx BDQs only */ struct page_pool *page_pool; -/* Deferred_head is used to cache the head for TWQ1 if +/* TWQ0 only, index of the meta descriptor of the last packet +* placed in the ring without ringing the doorbell, -1 if the +* doorbell is in sync with the tail. +*/ +s32 deferred_meta; + +/* TCQ only, used to cache the head for TWQ1 if * an attempt is made to clean TWQ1 with zero napi_budget. -* We do not use it for any other ring. */ s32 deferred_head; };
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 212/438 net: lock the socket in sock_gettstamp()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit 9ed55f3dbef4f4adfe65eb03b0c35c53229a8490 ]
sk->sk_flags must only be changed while holding the socket lock, because sock_set_flag() and sock_reset_flag() use non atomic operations (__set_bit() and __clear_bit()).
sock_gettstamp() is one of the last places where a bit of sk->sk_flags is changed from a syscall without owning the socket lock, through sock_enable_timestamp(sk, SOCK_TIMESTAMP).
sk_set_memalloc() and sk_clear_memalloc() also change sk->sk_flags without the socket lock, but their callers (nbd, iscsi_tcp, nvme-tcp, sunrpc, wireguard) need a careful audit, this will be addressed in a separate patch.
Jungwoo Lee and Wongi Lee reported an UDP socket use-after-free caused by this bug: a SIOCGSTAMPNS_NEW ioctl racing with bind() can cancel the SOCK_RCU_FREE bit that udp_lib_get_port() just set, because both threads perform a read-modify-write on the same word.
CPU 0 (bind) CPU 1 (SIOCGSTAMPNS_NEW) -------------------------------- ---------------------------- read sk_flags = F read sk_flags = F compute F | BIT(SOCK_RCU_FREE) compute F | BIT(SOCK_TIMESTAMP)
store F | BIT(SOCK_RCU_FREE)
sk_add_node_rcu(sk, ...)
store F | BIT(SOCK_TIMESTAMP)
After the lost update, SOCK_RCU_FREE is clear while the socket is visible to lockless UDP receive lookups. sk_destruct() then frees the socket immediately instead of waiting for a RCU grace period, while the receive path still holds a reference-less pointer to it:
BUG: KASAN: slab-use-after-free in ipv4_pktinfo_prepare+0x30/0x410 Read of size 8 at addr ffff888008806610 by task exploit/207 CPU: 0 UID: 1000 PID: 207 Comm: exploit Not tainted 6.12.95+ #1 ipv4_pktinfo_prepare+0x30/0x410 udp_queue_rcv_one_skb+0x51c/0x1180 udp_unicast_rcv_skb+0x109/0x350 ip_protocol_deliver_rcu+0x14b/0x310 ip_local_deliver_finish+0x29d/0x390 ip_local_deliver+0x24d/0x2a0
Only grab the socket lock when SOCK_TIMESTAMP has to be set, to keep the common case lockless.
--- net/core/sock.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-)
net/core/sock.c +8 -1
@@ -3908,7 +3908,14 @@ int sock_gettstamp(struct socket *sock, void __user *userstamp,struct sock *sk = sock->sk;struct timespec64 ts; -sock_enable_timestamp (sk,SOCK_TIMESTAMP ); +/* sk->sk_flags must only be changed under the socket lock, +* because sock_set_flag() uses non atomic operations. +*/ +if (!sock_flag (sk,SOCK_TIMESTAMP )) { +lock_sock (sk); +sock_enable_timestamp (sk,SOCK_TIMESTAMP ); +release_sock (sk); + } ts =ktime_to_timespec64 (sock_read_timestamp (sk));if (ts.tv_sec ==-1 )return -ENOENT ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 213/438 net: ethernet: cortina: Ack RX overrun interrupt correctly
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Linus Walleij <[email protected]>
[ Upstream commit 1dd85662fee6e2ac580b1c4f9a0c0a7ae6e31f0e ]
The RX overrun interrupt is reported in interrupt status register 4, but gmac_irq() acknowledges it using the RX descriptor error bit from status register 0. For GMAC0 this writes the GMAC1 overrun bit, while for GMAC1 the shift leaves no bit in the 32-bit register.
Acknowledge the same per-port RX overrun bit that was detected.
--- drivers/net/ethernet/cortina/gemini.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/net/ethernet/cortina/gemini.c +1 -1
@@ -1798,7 +1798,7 @@ static irqreturn_t gmac_irq(int irq, void *data)if (val & (GMAC0_RX_OVERRUN_INT_BIT << (netdev->dev_id *8 ))) {spin_lock (&geth->irq_lock); -writel (GMAC0_RXDERR_INT_BIT << (netdev->dev_id *8 ), +writel (GMAC0_RX_OVERRUN_INT_BIT << (netdev->dev_id *8 ), geth->base +GLOBAL_INTERRUPT_STATUS_4_REG );u64_stats_update_begin (&port->ir_stats_syncp); ++port->stats.rx_fifo_errors;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 214/438 net: stmmac: propagate FPE preemption-class mapping errors
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Lorenzo Bianconi <[email protected]>
[ Upstream commit 90e4b849dfa6fc8e6c050bcfe1b331b69c015d28 ]
stmmac_fpe_map_preemption_class() dispatches through the stmmac_do_void_callback() helper, which forces the callback's return value to 0 whenever the op pointer is populated. As a result the -EINVAL returned by dwmac5_fpe_map_preemption_class() (e.g. when a preemptible TC owns more than one TXQ under SP scheduling) is silently swallowed by every caller.
Switch the dispatch macro to stmmac_do_callback() so the callback's real result is propagated, and honour it in the taprio and mqprio qdisc offload.
Note that the taprio "if (ret)" check in tc_taprio_configure() used to be dead code and now becomes live: a preemptible TC spanning more than one TXQ under SP scheduling cannot be programmed in hardware, so a taprio or mqprio configuration that previously returned success while leaving the preemption-class register unprogrammed now fails with -EINVAL. For taprio, the failure also runs the disable path, tearing down the schedule that was just installed; this is the intended behaviour.
--- drivers/net/ethernet/stmicro/stmmac/hwif.h | 2 +- .../net/ethernet/stmicro/stmmac/stmmac_tc.c | 19 +++++++++---------- 2 files changed, 10 insertions(+), 11 deletions(-)
drivers/net/ethernet/stmicro/stmmac/hwif.h +1 -1
@@ -494,7 +494,7 @@ struct stmmac_ops { #define stmmac_set_arp_offload(__priv, __args...) \ stmmac_do_void_callback(__priv, mac, set_arp_offload, __args) #define stmmac_fpe_map_preemption_class(__priv, __args...) \ - stmmac_do_void_callback(__priv, mac, fpe_map_preemption_class, __args) + stmmac_do_callback(__priv, mac, fpe_map_preemption_class, __args)/* PTP and HW Timer helpers */ struct stmmac_hwtimestamp {
drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c +9 -10
@@ -970,7 +970,7 @@ static int tc_taprio_configure(struct stmmac_priv *priv,struct netlink_ext_ack *extack = qopt->mqprio.extack;struct timespec64 time, current_time, qopt_time;ktime_t current_time_ns; -int i, ret =0 ; +int err, i, ret =0 ;u64 ctr;if (qopt->base_time <0 )
@@ -1120,9 +1120,9 @@ static int tc_taprio_configure(struct stmmac_priv *priv,mutex_unlock (&priv->est_lock); } -stmmac_fpe_map_preemption_class (priv, priv->dev, extack,0 ); + err =stmmac_fpe_map_preemption_class (priv, priv->dev, extack,0 ); -return ret; +return qopt->cmd ==TAPRIO_CMD_DESTROY ? err : ret; } static void tc_taprio_stats(struct stmmac_priv *priv,
@@ -1237,14 +1237,15 @@ static int tc_query_caps(struct stmmac_priv *priv, } } -static void stmmac_reset_tc_mqprio (struct net_device *ndev, -struct netlink_ext_ack *extack) +static int stmmac_reset_tc_mqprio (struct net_device *ndev, +struct netlink_ext_ack *extack) {struct stmmac_priv *priv =netdev_priv (ndev);netdev_reset_tc (ndev);netif_set_real_num_tx_queues (ndev, priv->plat->tx_queues_to_use); -stmmac_fpe_map_preemption_class (priv, ndev, extack,0 ); + +return stmmac_fpe_map_preemption_class (priv, ndev, extack,0 ); } static int tc_setup_dwmac510_mqprio(struct stmmac_priv *priv,
@@ -1257,10 +1258,8 @@ static int tc_setup_dwmac510_mqprio(struct stmmac_priv *priv,u32 num_tc = qopt->num_tc;int err; -if (!num_tc) { -stmmac_reset_tc_mqprio (ndev, extack); -return 0 ; - } +if (!num_tc) +return stmmac_reset_tc_mqprio (ndev, extack); err =netdev_set_num_tc (ndev, num_tc);if (err)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 215/438 net: prevent torn reads in netdev_tc_txq
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit 21ef2d065ad3f0cfbf2ae51260bf962a9fa2c643 ]
netdev_set_tc_queue() (and related helpers/drivers such as netdev_bind_sb_channel_queue(), netdev_reset_tc(), and netdev_unbind_sb_channel()) perform separate 16-bit writes to dev->tc_to_txq[tc].count and dev->tc_to_txq[tc].offset.
Furthermore, memset() in netdev_reset_tc() and netdev_unbind_sb_channel() provides no guarantee of performing full 32-bit word stores.
Concurrent lockless readers (e.g. skb_tx_hash(), netdev_txq_to_tc(), ixgbe_select_queue(), taprio, mqprio, FPE drivers) can observe torn values where offset and count belong to inconsistent configurations.
Redefine struct netdev_tc_txq to embed count and offset inside a union with a u32 combined field, allowing atomic manipulation via READ_ONCE() and WRITE_ONCE().
Update all lockless readers and writers across the kernel to use READ_ONCE() and WRITE_ONCE() on the combined field.
Stable-dep-of: 02fffd1939f6 ("net: stmmac: preserve real_num_tx_queues on mqprio setup failure")
--- drivers/net/ethernet/intel/igc/igc_tsn.c | 6 ++- drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 7 +-- .../net/ethernet/mellanox/mlx5/core/en_main.c | 2 +- drivers/net/ethernet/sfc/falcon/tx.c | 8 +++- drivers/net/ethernet/sfc/siena/tx.c | 8 +++- .../net/ethernet/stmicro/stmmac/stmmac_fpe.c | 14 ++++-- include/linux/netdevice.h | 9 +++- net/core/dev.c | 46 +++++++++++++------ net/sched/sch_mqprio.c | 4 +- net/sched/sch_mqprio_lib.c | 7 ++- net/sched/sch_taprio.c | 26 ++++++----- 11 files changed, 94 insertions(+), 43 deletions(-)
drivers/net/ethernet/intel/igc/igc_tsn.c +4 -2
@@ -183,13 +183,15 @@ static u32 igc_fpe_map_preempt_tc_to_queue(const struct igc_adapter *adapter,u32 i, queue =0 ;for (i =0 ; i < dev->num_tc; i++) { +struct netdev_tc_txq res;u32 offset, count;if (!(preemptible_tcs &BIT (i)))continue ; - offset = dev->tc_to_txq[i].offset; - count = dev->tc_to_txq[i].count; + res.combined =READ_ONCE (dev->tc_to_txq[i].combined); + offset = res.offset; + count = res.count; queue |=GENMASK (offset + count -1 , offset); }
drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +4 -3
@@ -9273,10 +9273,11 @@ static u16 ixgbe_select_queue(struct net_device *dev, struct sk_buff *skb,if (sb_dev) {u8 tc =netdev_get_prio_tc_map (dev, skb->priority);struct net_device *vdev = sb_dev; +struct netdev_tc_txq res; - txq = vdev->tc_to_txq[tc].offset; - txq +=reciprocal_scale (skb_get_hash (skb), - vdev->tc_to_txq[tc].count); + res.combined =READ_ONCE (vdev->tc_to_txq[tc].combined); + txq = res.offset; + txq +=reciprocal_scale (skb_get_hash (skb), res.count);return txq; }
drivers/net/ethernet/mellanox/mlx5/core/en_main.c +1 -1
@@ -3247,7 +3247,7 @@ static int mlx5e_update_tc_and_tx_queues(struct mlx5e_priv *priv) old_num_txqs = netdev->real_num_tx_queues; old_ntc = netdev->num_tc ? :1 ;for (i =0 ; i <ARRAY_SIZE (old_tc_to_txq); i++) - old_tc_to_txq[i] = netdev->tc_to_txq[i]; + old_tc_to_txq[i].combined =READ_ONCE (netdev->tc_to_txq[i].combined); nch = priv->channels.params.num_channels; ntc = priv->channels.params.mqprio.num_tc;
drivers/net/ethernet/sfc/falcon/tx.c +6 -2
@@ -439,8 +439,12 @@ int ef4_setup_tc(struct net_device *net_dev, enum tc_setup_type type,return 0 ;for (tc =0 ; tc < num_tc; tc++) { - net_dev->tc_to_txq[tc].offset = tc * efx->n_tx_channels; - net_dev->tc_to_txq[tc].count = efx->n_tx_channels; +struct netdev_tc_txq res = { + .offset = tc * efx->n_tx_channels, + .count = efx->n_tx_channels, + }; + +WRITE_ONCE (net_dev->tc_to_txq[tc].combined, res.combined); }if (num_tc > net_dev->num_tc) {
drivers/net/ethernet/sfc/siena/tx.c +6 -2
@@ -380,8 +380,12 @@ int efx_siena_setup_tc(struct net_device *net_dev, enum tc_setup_type type,return 0 ;for (tc =0 ; tc < num_tc; tc++) { - net_dev->tc_to_txq[tc].offset = tc * efx->n_tx_channels; - net_dev->tc_to_txq[tc].count = efx->n_tx_channels; +struct netdev_tc_txq res = { + .offset = tc * efx->n_tx_channels, + .count = efx->n_tx_channels, + }; + +WRITE_ONCE (net_dev->tc_to_txq[tc].combined, res.combined); } net_dev->num_tc = num_tc;
drivers/net/ethernet/stmicro/stmmac/stmmac_fpe.c +10 -4
@@ -217,8 +217,11 @@ int dwmac5_fpe_map_preemption_class(struct net_device *ndev,* and is direct one-to-one mapping." */ for (u32 tc =0 ; tc < num_tc; tc++) { - count = ndev->tc_to_txq[tc].count; - offset = ndev->tc_to_txq[tc].offset; +struct netdev_tc_txq res; + + res.combined =READ_ONCE (ndev->tc_to_txq[tc].combined); + count = res.count; + offset = res.offset;if (pclass &BIT (tc)) preemptible_txqs |=GENMASK (offset + count -1 , offset);
@@ -275,8 +278,11 @@ int dwxgmac3_fpe_map_preemption_class(struct net_device *ndev,* any of the scheduling algorithms." */ for (u32 tc =0 ; tc < num_tc; tc++) { - count = ndev->tc_to_txq[tc].count; - offset = ndev->tc_to_txq[tc].offset; +struct netdev_tc_txq res; + + res.combined =READ_ONCE (ndev->tc_to_txq[tc].combined); + count = res.count; + offset = res.offset;if (pclass &BIT (tc)) preemptible_txqs |=GENMASK (offset + count -1 , offset);
include/linux/netdevice.h +7 -2
@@ -832,8 +832,13 @@ struct xps_dev_maps {#define TC_BITMASK 15/* HW offloaded queuing disciplines txq count and offset maps */ struct netdev_tc_txq { -u16 count; -u16 offset; +union { +struct { +u16 count; +u16 offset; + }; +u32 combined; + }; };#if defined(CONFIG_FCOE ) || defined(CONFIG_FCOE_MODULE )
net/core/dev.c +33 -13
114 lines not on this page · raw message
net/sched/sch_mqprio.c +3 -1
@@ -679,12 +679,14 @@ static int mqprio_dump_class_stats(struct Qdisc *sch, unsigned long cl,rcu_read_lock ();if (cl >=TC_H_MIN_PRIORITY ) {struct net_device *dev =qdisc_dev (sch); -struct netdev_tc_txq tc = dev->tc_to_txq[cl &TC_BITMASK ]; +struct netdev_tc_txq tc;struct gnet_stats_queue qstats = {0 };struct gnet_stats_basic_sync bstats;u32 qlen =0 ;int i; + tc.combined =READ_ONCE (dev->tc_to_txq[cl &TC_BITMASK ].combined); +gnet_stats_basic_sync_init (&bstats);for (i = tc.offset; i < tc.offset + tc.count; i++) {
net/sched/sch_mqprio_lib.c +5 -2
@@ -108,8 +108,11 @@ void mqprio_qopt_reconstruct(struct net_device *dev, struct tc_mqprio_qopt *qoptmemcpy (qopt->prio_tc_map, dev->prio_tc_map,sizeof (qopt->prio_tc_map));for (tc =0 ; tc < num_tc; tc++) { - qopt->count[tc] = dev->tc_to_txq[tc].count; - qopt->offset[tc] = dev->tc_to_txq[tc].offset; +struct netdev_tc_txq res; + + res.combined =READ_ONCE (dev->tc_to_txq[tc].combined); + qopt->count[tc] = res.count; + qopt->offset[tc] = res.offset; } }EXPORT_SYMBOL_GPL (mqprio_qopt_reconstruct);
net/sched/sch_taprio.c +15 -11
@@ -762,12 +762,13 @@ static struct sk_buff *taprio_dequeue_from_txq(struct Qdisc *sch, int txq,static void taprio_next_tc_txq (struct net_device *dev,int tc,int *txq) { -int offset = dev->tc_to_txq[tc].offset; -int count = dev->tc_to_txq[tc].count; +struct netdev_tc_txq res; + + res.combined =READ_ONCE (dev->tc_to_txq[tc].combined); (*txq)++; -if (*txq == offset + count) - *txq = offset; +if (*txq == res.offset + res.count) + *txq = res.offset; }/* Prioritize higher traffic classes, and select among TXQs belonging to the
@@ -1441,15 +1442,14 @@ static u32 tc_map_to_queue_mask(struct net_device *dev, u32 tc_mask)u32 i, queue_mask =0 ;for (i =0 ; i < dev->num_tc; i++) { -u32 offset, count; +struct netdev_tc_txq res;if (!(tc_mask &BIT (i)))continue ; - offset = dev->tc_to_txq[i].offset; - count = dev->tc_to_txq[i].count; + res.combined =READ_ONCE (dev->tc_to_txq[i].combined); - queue_mask |=GENMASK (offset + count -1 , offset); + queue_mask |=GENMASK (res.offset + res.count -1 , res.offset); }return queue_mask;
@@ -1802,10 +1802,14 @@ static int taprio_mqprio_cmp(const struct net_device *dev,if (!mqprio || mqprio->num_tc != dev->num_tc)return -1 ; -for (i =0 ; i < mqprio->num_tc; i++) -if (dev->tc_to_txq[i].count != mqprio->count[i] || - dev->tc_to_txq[i].offset != mqprio->offset[i]) +for (i =0 ; i < mqprio->num_tc; i++) { +struct netdev_tc_txq res; + + res.combined =READ_ONCE (dev->tc_to_txq[i].combined); +if (res.count != mqprio->count[i] || + res.offset != mqprio->offset[i])return -1 ; + }for (i =0 ; i <=TC_BITMASK ; i++)if (dev->prio_tc_map[i] != mqprio->prio_tc_map[i])
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 216/438 net: stmmac: preserve real_num_tx_queues on mqprio setup failure
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Lorenzo Bianconi <[email protected]>
[ Upstream commit 02fffd1939f6b45892f61822459953ce95e42948 ]
With the FPE preemption-class mapping error now propagated from stmmac_fpe_map_preemption_class(), tc_setup_dwmac510_mqprio() can fail on the mapping step. The error path used to call stmmac_reset_tc_mqprio(), which resets the number of real TX queues to priv->plat->tx_queues_to_use (the platform maximum), overwriting the value that was active before the offload was attempted (for example a lower count left over from a previous mqprio configuration).
The issue can be triggered using the following configuration:
# First mqprio config lowers the hw queue count below the platform
# default (e.g. 8 TX queues). $tc qdisc add dev eth0 root handle 1: mqprio queues 2@0 2@2
# Replace mqprio configuration with a second one that fails FPE
# preemption-class mapping. stmmac driver resets the real_num_tx_queues # to the platform maximum, losing the previous configuration. $tc qdisc replace dev eth0 root handle 2: mqprio queues 2@0 2@2 fp E P
Save ndev->real_num_tx_queues before lowering it and restore it, together with the TC-to-queue and priority-to-TC mappings, when the FPE preemption-class mapping fails, instead of resetting the queue count to the platform maximum.
Note that a failed setup makes the qdisc layer run mqprio_destroy() on the new qdisc. Because priv->hw_offload is only assigned after ndo_setup_tc() succeeds, mqprio_destroy() calls netdev_set_num_tc(dev, 0), so dev->num_tc ends up 0 regardless of the driver-side restore and the previous qdisc is not reactivated. The restore is still needed to keep real_num_tx_queues and to avoid leaving the failed configuration's TC-to-queue and priority-to-TC mappings in place.
--- .../net/ethernet/stmicro/stmmac/stmmac_tc.c | 82 ++++++++++++++----- 1 file changed, 62 insertions(+), 20 deletions(-)
drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c +62 -20
@@ -1237,6 +1237,30 @@ static int tc_query_caps(struct stmmac_priv *priv, } } +static int stmmac_set_ndev_tcs (struct net_device *ndev,u8 ntc, +struct netdev_tc_txq *tc_to_txq) +{ +int i, err; + +netdev_reset_tc (ndev); +if (!ntc) +return 0 ; + + err =netdev_set_num_tc (ndev, ntc); +if (err) +return err; + +for (i =0 ; i < ntc; i++) { +u16 count, offset; + + count = tc_to_txq[i].count; + offset = tc_to_txq[i].offset; +netdev_set_tc_queue (ndev, i, count, offset); + } + +return 0 ; +} +static int stmmac_reset_tc_mqprio (struct net_device *ndev,struct netlink_ext_ack *extack) {
@@ -1251,43 +1275,61 @@ static int stmmac_reset_tc_mqprio(struct net_device *ndev,static int tc_setup_dwmac510_mqprio (struct stmmac_priv *priv,struct tc_mqprio_qopt_offload *mqprio) { +unsigned int ndev_num_tx_queues, num_tx_queues =0 ; +struct netdev_tc_txq ndev_tc_to_txq[TC_MAX_QUEUE ]; +struct netdev_tc_txq tc_to_txq[TC_MAX_QUEUE ] = {};struct netlink_ext_ack *extack = mqprio->extack;struct tc_mqprio_qopt *qopt = &mqprio->qopt; -u32 offset, count, num_stack_tx_queues =0 ;struct net_device *ndev = priv->dev; -u32 num_tc = qopt->num_tc; -int err; +u8 ndev_prio_tc_map[TC_BITMASK +1 ]; +int i, err, ndev_ntc; -if (!num_tc) +if (!qopt->num_tc)return stmmac_reset_tc_mqprio (ndev, extack); - err =netdev_set_num_tc (ndev, num_tc); -if (err) -return err; - -for (u32 tc =0 ; tc < num_tc; tc++) { - offset = qopt->offset[tc]; - count = qopt->count[tc]; - num_stack_tx_queues += count; +if (qopt->num_tc >ARRAY_SIZE (tc_to_txq)) +return -EINVAL ; - err =netdev_set_tc_queue (ndev, tc, count, offset); -if (err) - goto err_reset_tc; +/* save current tc values for reset */ + ndev_ntc =netdev_get_num_tc (ndev); +for (i =0 ; i <ARRAY_SIZE (ndev->tc_to_txq); i++) + ndev_tc_to_txq[i].combined = +READ_ONCE (ndev->tc_to_txq[i].combined); +for (i =0 ; i <ARRAY_SIZE (ndev_prio_tc_map); i++) + ndev_prio_tc_map[i] =READ_ONCE (ndev->prio_tc_map[i]); + +for (i =0 ; i < qopt->num_tc; i++) { + tc_to_txq[i] = (struct netdev_tc_txq ) { + .count = qopt->count[i], + .offset = qopt->offset[i], + }; + num_tx_queues += qopt->count[i]; } - err =netif_set_real_num_tx_queues (ndev, num_stack_tx_queues); + err =stmmac_set_ndev_tcs (ndev, qopt->num_tc, tc_to_txq); +if (err) + goto error_reset_tc; + + ndev_num_tx_queues = ndev->real_num_tx_queues; + err =netif_set_real_num_tx_queues (ndev, num_tx_queues);if (err) - goto err_reset_tc; + goto error_reset_tc; err =stmmac_fpe_map_preemption_class (priv, ndev, extack, mqprio->preemptible_tcs);if (err) - goto err_reset_tc; + goto error_reset_num_tx_queues;return 0 ; -err_reset_tc: -stmmac_reset_tc_mqprio (ndev, extack); +error_reset_num_tx_queues: +if (netif_set_real_num_tx_queues (ndev, ndev_num_tx_queues)) +netdev_warn (ndev,"Failed to restore %u TX queues\n" , + ndev_num_tx_queues); +error_reset_tc: +stmmac_set_ndev_tcs (ndev, ndev_ntc, ndev_tc_to_txq); +for (i =0 ; i <ARRAY_SIZE (ndev_prio_tc_map); i++) +netdev_set_prio_tc_map (ndev, i, ndev_prio_tc_map[i]);return err; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 217/438 net: psp: avoid conflicts with skb->decrypted and sk_validate_xmit_skb()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Daniel Zahka <[email protected]>
[ Upstream commit a41f24c612c3f5139a3143307eb85bbcf1bd4d07 ]
PSP conflicts with TLS ULP in its usage of both skb->decrypted and sk->sk_validate_xmit_skb().
Make PSP mutually exclusive with TLS ULP, the only other user of either of these. As other users of skb->decrypted come along, they can be added to sk_has_decrypt_user(). It would make sense to also assert that sk->sk_validate_xmit_skb() is also NULL in both of these setup paths for similar future proofing, but the PSP listener/sk_clone() path is still broken and it could be seen as a regression to not allow rx assoc to run on a child of a listener socket with PSP tx assoc state.
Include all TCP ULPs in the sk_has_decrypt_user() check, even though TLS is the only one that conflicts with PSP via the decrypted bit. This is intentional because PSP was not designed to be used with ULPs. It is best to close off surface area that may make bugs reachable, until someone wishes to design and test an actual user of PSP with ULPs.
--- include/net/sock.h | 2 ++ net/core/sock.c | 7 +++++++ net/ipv4/tcp_ulp.c | 4 ++++ net/psp/psp_sock.c | 4 ++++ 4 files changed, 17 insertions(+)
include/net/sock.h +2 -0
@@ -2312,6 +2312,8 @@ static inline void sk_gso_disable(struct sock *sk) sk->sk_route_caps &= ~NETIF_F_GSO_MASK ; } +bool sk_has_decrypt_user (const struct sock *sk); +static inline int skb_do_copy_data_nocache (struct sock *sk,struct sk_buff *skb,struct iov_iter *from,char *to,int copy,int offset)
net/core/sock.c +7 -0
@@ -142,6 +142,7 @@#include <trace/events/sock.h> +#include <net/psp.h> #include <net/tcp.h> #include <net/busy_poll.h> #include <net/phonet/phonet.h>
@@ -2667,6 +2668,12 @@ void sk_setup_caps(struct sock *sk, struct dst_entry *dst) }EXPORT_SYMBOL_GPL (sk_setup_caps); +bool sk_has_decrypt_user (const struct sock *sk) +{ +return psp_sk_assoc (sk) || + (sk_is_inet (sk) &&inet_csk_has_ulp (sk));/* for tls */ +} +/* * Simple resource managers for sockets. */
net/ipv4/tcp_ulp.c +4 -0
@@ -136,6 +136,10 @@ static int __tcp_set_ulp(struct sock *sk, const struct tcp_ulp_ops *ulp_ops)if (icsk->icsk_ulp_ops) goto out_err; + err = -EINVAL ; +if (sk_has_decrypt_user (sk)) + goto out_err; +if (sk->sk_socket)clear_bit (SOCK_SUPPORT_ZC , &sk->sk_socket->flags);
net/psp/psp_sock.c +4 -0
@@ -143,6 +143,10 @@ int psp_sock_assoc_set_rx(struct sock *sk, struct psp_assoc *pas,NL_SET_ERR_MSG (extack,"Socket already has PSP state" ); err = -EBUSY ; goto exit_unlock; + }else if (sk_has_decrypt_user (sk)) { +NL_SET_ERR_MSG (extack,"Socket has incompatible state" ); + err = -EINVAL ; + goto exit_unlock; }refcount_inc (&pas->refcnt);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 218/438 x86/kprobes: Fix crash when probing CS CALL instructions
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jinke Han <[email protected]>
[ Upstream commit a5f7a5bb3b7f28ba7e4fa246775b29a0e5537255 ]
When using eBPF to probe CS CALL instructions within a function, a crash can be triggered.
The eBPF tool probes offset 257 of the __hrtimer_run_queues() function:
<__hrtimer_run_queues+249>: nopl 0x0(%rax,%rax,1) <__hrtimer_run_queues+254>: mov %r14,%rdi <__hrtimer_run_queues+257>: cs call <__x86_indirect_thunk_r12> <__hrtimer_run_queues+263>: mov %eax,%r12d <__hrtimer_run_queues+266>: xchg %ax,%ax <__hrtimer_run_queues+268>: mov %r13,%rdi
Which triggers this crash:
BUG: unable to handle page fault for address: 00000000000f41c9 #PF: supervisor write access in kernel mode #PF: error_code(0x0002) - not-present page PGD 0 P4D 0 Oops: 0002 [#1] SMP NOPTI CPU: 1 PID: 0 Comm: swapper/1 Kdump: loaded Tainted: P RIP: 0010:__hrtimer_run_queues+0x106/0x230
Note that __hrtimer_run_queues+0x106 is __hrtimer_run_queues+262, which is at the 6th byte of the above CS CALL instruction. Since the CS CALL instruction occupies 6 bytes, the exception occurred in the middle of that call instruction.
The root cause is that when using eBPF tools to probe in the middle of a function, a kprobe with INT3 is used as the underlying implementation.
During single-step emulation of the original CALL instruction, int3_emulate_call() assumes that the probed CALL instruction is 5 bytes long. However, the actual CS-prefixed CALL instruction occupies 6 bytes, so it constructs an incorrect exception return address. When the CPU returns from the kprobe handler, the next instruction to be executed is at the address of the last byte of that CS CALL instruction. Coincidentally, starting from that address, the CPU fetches and decodes a completely different instruction, which ultimately triggers a kernel crash.
Fix the issue by using the actual instruction length obtained from the instruction decoder when constructing the exception return address, rather than relying on the hardcoded CALL_INSN_SIZE macro.
[ mingo: Refined the changelog ]
--- arch/x86/include/asm/text-patching.h | 4 ++-- arch/x86/kernel/alternative.c | 6 ++++-- arch/x86/kernel/kprobes/core.c | 5 ++--- 3 files changed, 8 insertions(+), 7 deletions(-)
arch/x86/include/asm/text-patching.h +2 -2
@@ -164,9 +164,9 @@ unsigned long int3_emulate_pop(struct pt_regs *regs) }static __always_inline -void int3_emulate_call(struct pt_regs *regs, unsigned long func) +void int3_emulate_call(struct pt_regs *regs, unsigned long ip, unsigned long func) { -int3_emulate_push (regs, regs->ip -INT3_INSN_SIZE +CALL_INSN_SIZE ); +int3_emulate_push (regs, ip);int3_emulate_jmp (regs, func); }
arch/x86/kernel/alternative.c +4 -2
@@ -2319,6 +2319,7 @@ int3_exception_notify(struct notifier_block *self, unsigned long val, void *dataunsigned long selftest = (unsigned long )&int3_selftest_asm;struct die_args *args = data;struct pt_regs *regs = args->regs; +unsigned long ip;OPTIMIZER_HIDE_VAR (selftest);
@@ -2331,7 +2332,8 @@ int3_exception_notify(struct notifier_block *self, unsigned long val, void *dataif (regs->ip -INT3_INSN_SIZE != selftest)return NOTIFY_DONE ; -int3_emulate_call (regs, (unsigned long )&int3_selftest_callee); + ip = regs->ip -INT3_INSN_SIZE +CALL_INSN_SIZE ; +int3_emulate_call (regs, ip, (unsigned long )&int3_selftest_callee);return NOTIFY_STOP ; }
@@ -2929,7 +2931,7 @@ noinstr int smp_text_poke_int3_handler(struct pt_regs *regs)break ;case CALL_INSN_OPCODE : -int3_emulate_call (regs, (long )ip + tpl->disp); +int3_emulate_call (regs, (long )ip, (long )ip + tpl->disp);break ;case JMP32_INSN_OPCODE :
arch/x86/kernel/kprobes/core.c +2 -3
@@ -510,10 +510,9 @@ NOKPROBE_SYMBOL(kprobe_emulate_ret);static void kprobe_emulate_call (struct kprobe *p,struct pt_regs *regs) { -unsigned long func = regs->ip -INT3_INSN_SIZE + p->ainsn.size; +unsigned long ip = regs->ip -INT3_INSN_SIZE + p->ainsn.size; - func += p->ainsn.rel32; -int3_emulate_call (regs, func); +int3_emulate_call (regs, ip, ip + p->ainsn.rel32); }NOKPROBE_SYMBOL (kprobe_emulate_call);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 219/438 net: macb: fix ordering around PTP timestamp read
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: James Clark <[email protected]>
[ Upstream commit 9ca4ba24259183ce15665be86b2956cd896c4687 ]
PTP_SYS_OFFSET_EXTENDED returns system timestamps that do not correctly bracket the PHC register read on MACB/GEM. On a Raspberry Pi 5, the returned interval can be as short as 37 ns, while an ordered register read takes approximately 1 us. This biases the midpoint used by phc2sys, causing CLOCK_REALTIME to run approximately 0.5 us ahead when synchronized to the PHC.
gem_tsu_get_time() reads the nanoseconds register using the driver's relaxed MMIO accessor. On weakly ordered systems, the subsequent system timestamp can be taken before the register read completes. The internal smp_rmb() in the pre-timestamp path also does not guarantee ordering against the subsequent MMIO read.
Add rmb() before and after the bracketed nanoseconds read in both the normal and seconds rollover paths so the system timestamps bracket the PHC read. Adding the post-read barrier increases the minimum interval on the same Raspberry Pi 5 to approximately 1 us.
--- drivers/net/ethernet/cadence/macb_ptp.c | 9 +++++++++ 1 file changed, 9 insertions(+)
drivers/net/ethernet/cadence/macb_ptp.c +9 -0
@@ -50,7 +50,12 @@ static int gem_tsu_get_time(struct ptp_clock_info *ptp, struct timespec64 *ts,spin_lock_irqsave (&bp->tsu_clk_lock, flags);ptp_read_system_prets (sts); +/* explicit barriers are needed because gem_readl() is relaxed */ +if (sts) +rmb (); first =gem_readl (bp,TN ); +if (sts) +rmb ();ptp_read_system_postts (sts); secl =gem_readl (bp,TSL ); sech =gem_readl (bp,TSH );
@@ -62,7 +67,11 @@ static int gem_tsu_get_time(struct ptp_clock_info *ptp, struct timespec64 *ts,* (assume all done within 1s) */ ptp_read_system_prets (sts); +if (sts) +rmb (); ts->tv_nsec =gem_readl (bp,TN ); +if (sts) +rmb ();ptp_read_system_postts (sts); secl =gem_readl (bp,TSL ); sech =gem_readl (bp,TSH );
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 220/438 net: mvpp2: prevent buffer overflow in page_pool allocation
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Dmitriy Okunev <[email protected]>
[ Upstream commit 14cb1e7702e5cb3c58888f6aed498381a73927d2 ]
The per‑processor buffering scheme is supported only if the number of pools (nrxqs * 2) does not exceed MVPP2_BM_MAX_POOLS (8). This is already checked in mvpp2_probe() during the initial activation of percpu_pools.
However, mvpp2_change_mtu() may later call mvpp2_bm_switch_buffers(priv, true) without this check, which can lead to an out-of-bounds access in the priv->page_pool array in mvpp2_bm_init(). The array is sized to hold MVPP2_PORT_MAX_RXQ entries, and mvpp2_get_nrxqs() may return exactly that value. The per-CPU scheme then doubles it to nrxqs * 2, exceeding the array bounds.
Check that the hardware version is MVPP22 or newer and that the number of pools (nrxqs * 2) does not exceed MVPP2_BM_MAX_POOLS before switching to per-CPU mode.
Found by Linux Verification Center (linuxtesting.org) with SVACE.
--- drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c +2 -1
@@ -5086,7 +5086,8 @@ static int mvpp2_change_mtu(struct net_device *dev, int mtu)netdev_warn (dev,"mtu %d too high, switching to shared buffers" , mtu);mvpp2_bm_switch_buffers (priv, false); } - }else { + }else if (priv->hw_version >=MVPP22 && +mvpp2_get_nrxqs (priv) *2 <=MVPP2_BM_MAX_POOLS ) {bool jumbo = false;int i;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 221/438 net: skbuff: do not leave stale header offsets after pskb_carve()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit a5117e1eccac6ee3bd4aed7cacf8ebcb6b3eb309 ]
pskb_carve_inside_header() and pskb_carve_inside_nonlinear() remove the first bytes of a packet and reallocate skb->head.
All the headers that were present before the operation are gone, but both functions call skb_headers_offset_update(skb, 0), which is a no-op : skb->mac_header, skb->network_header, skb->transport_header and skb->csum_start keep their old values and now describe bytes which are no longer there.
Both helpers size the new head from the old skb_end_offset(), so the stale offsets still land inside the new allocation. They point past skb_tail_pointer() though, to bytes that were never initialized.
pskb_carve_inside_nonlinear() is the worst case, because it leaves a zombie skb with an empty linear part (skb->data == skb_tail_pointer(skb), skb_headlen(skb) == 0), while skb_mac_header_was_set() is still true and skb->mac_header is way ahead of skb->data.
The only user of pskb_extract() is rds_tcp_data_recv(), and the carved skb is queued on tinc->ti_skb_list. When the RDS incoming message is released, rds_tcp_inc_free() calls skb_queue_purge(), which frees the skbs with SKB_DROP_REASON_QUEUE_PURGE. This is visible from drop_monitor, which then tries to pull back to the (bogus) mac header :
skbuff: __skb_pull(len=234)
skb len=6968 data_len=6968 headroom=0 headlen=0 tailroom=0 end-tail=384 mac=(234,14) mac_len=14 net=(248,40) trans=288 shinfo(txflags=0 nr_frags=1 gso(size=1428 type=16 segs=5)) csum(0x100120 start=288 offset=16 ip_summed=3 complete_sw=0 valid=1 level=0) hash(0x7b446c6c sw=0 l4=1) proto=0x86dd pkttype=0 iif=60
kernel BUG at ./include/linux/skbuff.h:2847!
Add skb_carve_reset_headers() to mark the mac and transport headers as not set, reset the network header, clear skb->mac_len, and drop a now meaningless CHECKSUM_PARTIAL (csum_start no longer describes anything).
Invalidate the inner offsets as well. Unlike mac_header and transport_header they have no "unset" sentinel, so a leftover non-zero value still looks like a real header. Zero skb->inner_mac_header, skb->inner_network_header, skb->inner_transport_header, skb->inner_protocol and skb->encapsulation, so that all the header state is invalidated in one place.
v2: fixed an inaccurate changelog. The stale offsets stay inside the
new skb->head, which is never smaller than the old one, they simply point past skb_tail_pointer() to bytes that are gone. Thanks to Xuanqiang Luo for insisting on this.
Also invalidate the inner header state, as suggested by the netdev AI review :
https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260911114922.621937-1-edumazet%40google.com
--- net/core/skbuff.c | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-)
net/core/skbuff.c +30 -2
@@ -6820,6 +6820,34 @@ struct sk_buff *alloc_skb_with_frags(unsigned long header_len, }EXPORT_SYMBOL (alloc_skb_with_frags); +/* pskb_carve_inside_header() and pskb_carve_inside_nonlinear() +* remove the first bytes of a packet and reallocate skb->head. +* +* Whatever headers were present before the operation are gone, +* we must not leave stale offsets, otherwise users of this skb +* (skb_dump(), drop_monitor, taps, ...) would read or pull garbage. +*/ +static void skb_carve_reset_headers (struct sk_buff *skb) +{ +skb_unset_mac_header (skb); +skb_unset_transport_header (skb); +skb_reset_network_header (skb); + skb->mac_len =0 ; + +/* Inner offsets have no "unset" marker, zero them so that +* skb_inner_network_header_was_set() becomes false and no +* consumer mistakes them for a real (and long gone) header. +*/ + skb->inner_mac_header =0 ; + skb->inner_network_header =0 ; + skb->inner_transport_header =0 ; + skb->inner_protocol =0 ; + skb->encapsulation =0 ; + +if (skb->ip_summed ==CHECKSUM_PARTIAL ) + skb->ip_summed =CHECKSUM_NONE ; +} +/* carve out the first off bytes from skb when off < headlen */ static int pskb_carve_inside_header (struct sk_buff *skb,const u32 off,const int headlen,gfp_t gfp_mask)
@@ -6875,7 +6903,7 @@ static int pskb_carve_inside_header(struct sk_buff *skb, const u32 off, skb->head_frag =0 ;skb_set_end_offset (skb, size);skb_set_tail_pointer (skb,skb_headlen (skb)); -skb_headers_offset_update (skb,0 ); +skb_carve_reset_headers (skb); skb->cloned =0 ; skb->hdr_len =0 ; skb->nohdr =0 ;
@@ -7015,7 +7043,7 @@ static int pskb_carve_inside_nonlinear(struct sk_buff *skb, const u32 off, skb->data = data;skb_set_end_offset (skb, size);skb_reset_tail_pointer (skb); -skb_headers_offset_update (skb,0 ); +skb_carve_reset_headers (skb); skb->cloned =0 ; skb->hdr_len =0 ; skb->nohdr =0 ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 222/438 drm/amdgpu: check ras and obj before dereference
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Dmitriy Chumachenko <[email protected]>
[ Upstream commit 723d4dc628d764b19cf9efca14b82cca5ff020c9 ]
nbio_v7_9_handle_ras_controller_intr_no_bifring() dereferences ras and obj without checking either for NULL. Both amdgpu_ras_get_context() and amdgpu_ras_find_obj() can return NULL, e.g. during the window between adev->nbio.ras being set (early in amdgpu_ras_init(), by design, to enable the fatal-error interrupt as soon as possible) and the PCIE_BIF ras object actually being created in RAS late_init. Any interrupt in that window crashes in hard-IRQ context.
This is analogous to commit d190b459b2a4 ("drm/amdgpu: the warning dereferencing obj for nbio_v7_4"), which fixed the same issue in the nbio_v7_4 handler.
Found by Linux Verification Center (linuxtesting.org) with SVACE.
--- drivers/gpu/drm/amd/amdgpu/nbio_v7_9.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/gpu/drm/amd/amdgpu/nbio_v7_9.c +1 -1
@@ -535,7 +535,7 @@ static void nbio_v7_9_handle_ras_controller_intr_no_bifring(struct amdgpu_device RAS_CNTLR_INTERRUPT_CLEAR, 1);WREG32_SOC15 (NBIO ,0 , regBIF_BX0_BIF_DOORBELL_INT_CNTL, bif_doorbell_intr_cntl); -if (!ras->disable_ras_err_cnt_harvest) { +if (ras && !ras->disable_ras_err_cnt_harvest && obj) {/* * clear error status after ras_controller_intr * according to hw team and count ue number
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 223/438 drm/amd/display: fix MALL hysteresis timer underflow at high refresh rates
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Francis Marlou Pacaro <[email protected]>
[ Upstream commit 2ac2fe765ef475f409616ac0b57c4a3922749b0f ]
dcn30_apply_idle_power_optimizations() derives the MALL frame cache hysteresis timer with
tmr_delay = (uint32_t)(div_u64(..., denom) - 64LL);
div_u64() returns a u64, so when the quotient is smaller than 64 the subtraction wraps instead of going negative and tmr_delay ends up huge. The loop that follows tries to squeeze it into the 6 bit register field by doubling denom, but that only makes the quotient smaller, so tmr_delay can never converge. tmr_scale is bumped past 3 and the function gives up with
/* Delay exceeds range of hysteresis timer */ ASSERT(false);
even though the requested delay is too *short* to encode, not too long.
With mall_additional_timer_percent left at its default of 0, the quotient drops below 64 once the refresh rate used for the calculation goes above ~243 Hz. Every DCN 3.0 display above that loses MALL static screen entirely and splats a WARN once per boot. Reproduced on Navi 23 (RX 6600) driving 1920x1080, resetting /sys/kernel/debug/clear_warn_once between modes:
refresh MALL ASSERT 144 Hz enabled no 240 Hz enabled no 280 Hz skipped yes 360 Hz skipped yes
Commit 3bb68cec4db8 ("drm/amd/display: Add Overflow check to skip MALL") already covered the other end of the range, where a large stutter period makes the delay too long to encode. Cover the short end by clamping to 0, which selects the shortest hysteresis the register can express, 65.28us * 64 = ~4.18ms. That is marginally longer than what the formula asks for at these refresh rates, and erring long is the safe direction:
it only delays MALL entry, it can never enter early.
The numerator does not change between iterations, only denom does, so compute it once and keep both call sites inside 100 columns.
The genuinely out of range case at very low refresh rates still reaches the ASSERT, which is where it belongs.
--- .../gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-)
drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.c +7 -6
@@ -1058,10 +1058,12 @@ bool dcn30_apply_idle_power_optimizations(struct dc *dc, bool enable)*/ unsigned int denom = refresh_hz *6528 ;unsigned int stutter_period = dc->current_state->perf_params.stutter_period_us; +uint64_t num = (1000000LL +2 * stutter_period * refresh_hz) * + (100LL + dc->debug.mall_additional_timer_percent); +uint64_t tmr_ticks; - tmr_delay = (uint32_t )(div_u64 (((1000000LL +2 * stutter_period * refresh_hz) * - (100LL + dc->debug.mall_additional_timer_percent) + denom -1 ), - denom) -64LL ); + tmr_ticks =div_u64 (num + denom -1 , denom); + tmr_delay = tmr_ticks >64 ? (uint32_t )(tmr_ticks -64 ) :0 ;/* In some cases the stutter period is really big (tiny modes) in these * cases MALL cant be enabled, So skip these cases to avoid a ASSERT()
@@ -1083,9 +1085,8 @@ bool dcn30_apply_idle_power_optimizations(struct dc *dc, bool enable) } denom *=2 ; - tmr_delay = (uint32_t )(div_u64 (((1000000LL +2 * stutter_period * refresh_hz) * - (100LL + dc->debug.mall_additional_timer_percent) + denom -1 ), - denom) -64LL ); + tmr_ticks =div_u64 (num + denom -1 , denom); + tmr_delay = tmr_ticks >64 ? (uint32_t )(tmr_ticks -64 ) :0 ; }/* Copy HW cursor */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 224/438 btrfs: abort transaction on failure to update inode for hole punching and reflinking
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Filipe Manana <[email protected]>
[ Upstream commit 97fcd34aa9fd73cefe3120ac9a82ca9d7763922f ]
If we fail to update the inode we error out without aborting the transaction, which can result in a persistent inconsistency if after the failure the transaction is committed, as we have dropped file extent items from a range and either punched a hole or insert a new file extent item for that range (for reflinks).
So add the missing transaction abort.
--- fs/btrfs/file.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
fs/btrfs/file.c +3 -1
@@ -2513,8 +2513,10 @@ int btrfs_replace_file_extents(struct btrfs_inode *inode, inode_set_ctime_current(&inode->vfs_inode)); ret =btrfs_update_inode (trans, inode); -if (ret) +if (unlikely (ret)) { +btrfs_abort_transaction (trans, ret);break ; + }btrfs_end_transaction (trans);btrfs_btree_balance_dirty (fs_info);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 225/438 btrfs: check if there is space for chunk item when validating sys chunk array
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Filipe Manana <[email protected]>
[ Upstream commit aeab4c62875748ecfd390a47ac1d91ea7c9a6abb ]
We checked if have enough remaining space for a key before dereferencing a key, but we then dereference a chunk item, to get the number of stripes, without checking if there is space for the item. So add a check to see if there is enough space for a chunk item before dereferencing the item to extract the stripe count.
--- fs/btrfs/disk-io.c | 4 ++++ 1 file changed, 4 insertions(+)
fs/btrfs/disk-io.c +4 -0
@@ -2358,6 +2358,10 @@ static int validate_sys_chunk_array(const struct btrfs_fs_info *fs_info, key.type, cur);return -EUCLEAN ; } + +if (unlikely (cur +sizeof (*chunk) > sys_array_size)) + goto short_read; + chunk = (struct btrfs_chunk *)(sb->sys_chunk_array + cur); num_stripes =btrfs_stack_chunk_num_stripes (chunk);if (unlikely (cur +btrfs_chunk_item_size (num_stripes) > sys_array_size))
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 226/438 perf: Fix null pointer access in is_include_guest_event()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Vinay Belgaumkar <[email protected]>
[ Upstream commit 88aed0422f39b22406f35f1e758cea25e7bbcfb5 ]
A typical module unload occurring event when there is an active perf connection leads to freeing of the pmu pointer. The call log is something like:
..
__pmu_detach_event
pmu_detach_event
pmu_detach_events
perf_pmu_unregister
..
__pmu_detach_event() sets event->pmu to null. When the perf connection finally is closed, the following stack trace is observed:
Oops: general protection fault, kernel NULL pointer dereference ... RIP: 0010:_free_event+0x3e/0x370 ... Call Trace: ... perf_event_release_kernel+0x260/0x2d0 perf_release+0x12/0x20
A call to mediated_pmu_unaccount_event() inside _free_event() is the root cause of this crash. Adding a check inside is_include_guest_event() ensures we don't accidentally access a null pmu ptr. In addition to this, we will now call mediated_pmu_unaccount_event() before clearing the pmu ptr so that nr_include_guest_events counts are maintained correctly.
--- kernel/events/core.c | 4 ++++ 1 file changed, 4 insertions(+)
kernel/events/core.c +4 -0
@@ -6350,6 +6350,9 @@ static DEFINE_MUTEX(perf_mediated_pmu_mutex);/* !exclude_guest event of PMU with PERF_PMU_CAP_MEDIATED_VPMU */ static inline bool is_include_guest_event (struct perf_event *event) { +if (!event->pmu) +return false; +if ((event->pmu->capabilities &PERF_PMU_CAP_MEDIATED_VPMU ) && !event->attr.exclude_guest)return true;
@@ -12977,6 +12980,7 @@ static void __pmu_detach_event(struct pmu *pmu, struct perf_event *event,exclusive_event_destroy (event);module_put (pmu->module); +mediated_pmu_unaccount_event (event); event->pmu =NULL ;/* force fault instead of UAF */ }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 227/438 sched/core: Avoid false migration warning for proxy donors
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Andrea Righi <[email protected]>
[ Upstream commit fe3c73d7bc769e7afc252f867a3421fe168b898d ]
Proxy execution can move a blocked donor's scheduling context to the lock owner's CPU even when the donor is migration-disabled. The donor does not execute there, and its original execution CPU remains recorded in wake_cpu.
set_task_cpu() warns unconditionally for migration-disabled tasks, so a subsequent proxy migration or the wakeup path returning the donor home triggers a false positive: moving a blocked scheduling context does not violate the migration-disabled execution context.
For example, creating a mutex owner on CPU1 and a migration-disabled waiter on CPU0 can trigger the following warning:
proxy_migrate_repro: donor blocking on CPU0 with migration disabled
proxy_migrate_repro: donor moved from CPU0 to CPU1
WARNING: kernel/sched/core.c:3389 at set_task_cpu+0x1d3/0x280 ... Call Trace: try_to_wake_up+0x43f/0x780 __mutex_unlock_slowpath+0x330/0x540 owner_fn+0x9f/0xc0 [proxy_migrate_repro] ... proxy_migrate_repro: donor woke on CPU0, task_cpu=0 proxy_migrate_repro: completed
Exclude blocked proxy donors from the warning. The proxy wakeup path restores an executable placement before clearing the blocked state.
--- kernel/sched/core.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-)
kernel/sched/core.c +8 -1
@@ -3352,6 +3352,8 @@ void relax_compatible_cpus_allowed_ptr(struct task_struct *p)void set_task_cpu (struct task_struct *p,unsigned int new_cpu) {unsigned int state =READ_ONCE (p->__state); +bool proxy_migrated =sched_proxy_exec () && p->is_blocked && +task_cpu (p) != p->wake_cpu;/* * We should never call set_task_cpu() on a blocked task,
@@ -3387,7 +3389,12 @@ void set_task_cpu(struct task_struct *p, unsigned int new_cpu)*/ WARN_ON_ONCE (!cpu_online (new_cpu)); -WARN_ON_ONCE (is_migration_disabled (p)); +/* +* Proxy execution can move a blocked task's scheduling context to any +* CPU without moving its migration-disabled execution context. The +* wakeup path will return the task to a CPU where it can execute. +*/ +WARN_ON_ONCE (is_migration_disabled (p) && !proxy_migrated);trace_sched_migrate_task (p, new_cpu);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 228/438 x86/fred: Reconstruct the #GP context for rejected INT instructions
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Matthew Schwartz <[email protected]>
[ Upstream commit 93f53499d0b945e8ae447f497faf743d60069f61 ]
FRED event delivery does not use the IDT, so the gate DPL check that rejects a user INT n falls to software (Intel FRED specification [1], section 8.3). fred_intx() rejects the same vectors as IDT delivery, but reports a zero error code and the IP after the INT. This breaks the signal ABI. Wine uses the error code to recognize INT 0x2d, so the changed context turns a handled breakpoint into an access violation in Elden Ring.
Rewind IP using the instruction length in the augmented SS and synthesize the IDT selector error code, (vector << 3) | 2. Set RF in the saved flags, as the CPU does for a #GP fault. Section 5.2.1 defines the saved vector, instruction length and RF state. The supplied length handles prefixes without reading user memory. Limit the changes to already-rejected software interrupts, preserving the accepted INT3, INT4 and enabled INT80 paths and hardware exceptions. With IA32 emulation disabled, INT 0x80 now reports the same #GP as the DPL 0 gate IDT installs there. The rewound IP also stops fixup_iopl_exception() from inspecting the byte after the INT.
Also clear the software event flag. Section 6.2.3 specifies that ERETU with this flag and TF set traps before executing any user instruction. A tracer that suppresses SIGSEGV and resumes with TF set expects the next instruction to run first, as after IRET. The sigreturn path clears the same flag for this reason in prevent_single_step_upon_eretu().
[1] Intel Flexible Return and Event Delivery (FRED) Specification, revision 9.0 (346446-009US), sections 5.2.1, 6.2.3 and 8.3.
--- arch/x86/entry/entry_fred.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-)
arch/x86/entry/entry_fred.c +10 -1
@@ -10,6 +10,7 @@#include <asm/desc.h> #include <asm/fred.h> #include <asm/idtentry.h> +#include <asm/processor-flags.h> #include <asm/syscall.h> #include <asm/trapnr.h> #include <asm/traps.h>
@@ -71,7 +72,15 @@ static noinstr void fred_intx(struct pt_regs *regs)#endif default : -return exc_general_protection (regs,0 ); +/* +* Reconstruct the #GP fault state that IDT delivery would produce. +* Clear the software event flag so ERETU with TF set does not trap +* before the resumed instruction. See prevent_single_step_upon_eretu(). +*/ + regs->ip -= regs->fred_ss.insnlen; + regs->flags |=X86_EFLAGS_RF ; + regs->fred_ss.swevent =0 ; +return exc_general_protection (regs, (regs->fred_ss.vector <<3 ) |2 ); } }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 229/438 Input: eeti_ts - publish the OF module alias
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: hpp.iscas <[email protected]>
[ Upstream commit a52ae68a937efc353251aec27fc995ff66cbe1ca ]
The EETI driver matches eeti,exc3000-i2c Device Tree clients, but only publishes the legacy eeti_ts I2C ID. The I2C core emits an OF modalias for a Device Tree client.
Publish the existing OF match table within its CONFIG_OF guard.
--- drivers/input/touchscreen/eeti_ts.c | 1 + 1 file changed, 1 insertion(+)
drivers/input/touchscreen/eeti_ts.c +1 -0
@@ -276,6 +276,7 @@ static const struct of_device_id of_eeti_ts_match[] = { { .compatible = "eeti,exc3000-i2c", }, { } }; +MODULE_DEVICE_TABLE (of, of_eeti_ts_match);#endif static struct i2c_driver eeti_ts_driver = {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 230/438 hwmon: (gpio-fan) return IRQ_HANDLED from the shared alarm IRQ handler
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Cong Nguyen <[email protected]>
[ Upstream commit bdf5f731957de48acada392f28e82bc019713adb ]
fan_alarm_irq_handler() always schedules alarm_work but returns IRQ_NONE, so the kernel treats every alarm interrupt as unhandled. On a shared line that risks the whole line being disabled as spurious.
v1 just fixed that, but it was still IRQF_SHARED, and always returning IRQ_HANDLED there defeats spurious-interrupt detection for the line -- if the interrupt ever fires without a real event, nothing catches it, and a fault could spin the CPU in the handler.
Sashiko flagged this in v1, and Guenter confirmed: this interrupt must not be shared. So v2 drops IRQF_SHARED too.
--- drivers/hwmon/gpio-fan.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/hwmon/gpio-fan.c +2 -2
@@ -68,7 +68,7 @@ static irqreturn_t fan_alarm_irq_handler(int irq, void *dev_id)schedule_work (&fan_data->alarm_work); -return IRQ_NONE ; +return IRQ_HANDLED ; } static ssize_t fan1_alarm_show(struct device *dev,
@@ -103,7 +103,7 @@ static int fan_alarm_init(struct gpio_fan_data *fan_data)irq_set_irq_type (alarm_irq,IRQ_TYPE_EDGE_BOTH );return devm_request_irq (dev, alarm_irq, fan_alarm_irq_handler, -IRQF_SHARED ,"GPIO fan alarm" , fan_data); +0 ,"GPIO fan alarm" , fan_data); }/*
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 231/438 hwmon: (hp-wmi-sensors) Improve raw WMI string handling
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: James Seo <[email protected]>
[ Upstream commit 92b68492eae701e5b0e9d142ffe229921af7b1fa ]
Commit c9ba59258094 ("hwmon: (hp-wmi-sensors) Fix failure to load on EliteDesk 800 G6") left out some logic for recognizing raw WMI strings in check_numeric_sensor_wobj(). This issue was reported by a user along with an incomplete and unsuitable proposed solution [1].
Add the missing logic and properly remedy the issue. Also slightly refactor how raw WMI strings are recognized elsewhere to make the intent that they should be treated as regular ACPI strings clearer.
--- drivers/hwmon/hp-wmi-sensors.c | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-)
drivers/hwmon/hp-wmi-sensors.c +22 -8
@@ -526,14 +526,12 @@ static int check_wobj(const union acpi_object *wobj,for (prop =0 ; prop <= last_prop; prop++) { type = elements[prop].type; valid_type = property_map[prop]; -if (type != valid_type) { -if (type ==ACPI_TYPE_BUFFER && - valid_type ==ACPI_TYPE_STRING && -is_raw_wmi_string (elements[prop].buffer.pointer, - elements[prop].buffer.length)) -continue ; +if (type ==ACPI_TYPE_BUFFER && +is_raw_wmi_string (elements[prop].buffer.pointer, + elements[prop].buffer.length)) + type =ACPI_TYPE_STRING ; +if (type != valid_type)return -EINVAL ; - } }return 0 ;
@@ -579,6 +577,7 @@ static int check_numeric_sensor_wobj(const union acpi_object *wobj,int prop =HP_WMI_PROPERTY_NAME ;acpi_object_type valid_type;union acpi_object *elements; +union acpi_object *element;u32 elem_count;int last_prop;bool is_new;
@@ -602,13 +601,20 @@ static int check_numeric_sensor_wobj(const union acpi_object *wobj, elem_count > HP_WMI_MAX_PROPERTIES)return -EINVAL ; - type = elements[HP_WMI_PROPERTY_SIZE ].type; + element = &elements[HP_WMI_PROPERTY_SIZE ]; + type = element->type;switch (type) {case ACPI_TYPE_INTEGER : is_new = true; last_prop =HP_WMI_PROPERTY_RATE_UNITS ;break ; +case ACPI_TYPE_BUFFER : +if (!is_raw_wmi_string (element->buffer.pointer, + element->buffer.length)) +return -EINVAL ; + fallthrough; +case ACPI_TYPE_STRING : is_new = false; last_prop =HP_WMI_PROPERTY_CURRENT_READING ;
@@ -631,6 +637,10 @@ static int check_numeric_sensor_wobj(const union acpi_object *wobj,for (i =0 ; i < elem_count && prop <= last_prop; i++, prop++) { type = elements[i].type; valid_type = hp_wmi_property_map[prop]; +if (type ==ACPI_TYPE_BUFFER && +is_raw_wmi_string (elements[i].buffer.pointer, + elements[i].buffer.length)) + type =ACPI_TYPE_STRING ;if (type != valid_type)return -EINVAL ;
@@ -651,6 +661,10 @@ static int check_numeric_sensor_wobj(const union acpi_object *wobj,/* PossibleStates[0] has already been type-checked. */ for (j =0 ; i +1 < elem_count && j +1 < count; j++) { type = elements[++i].type; +if (type ==ACPI_TYPE_BUFFER && +is_raw_wmi_string (elements[i].buffer.pointer, + elements[i].buffer.length)) + type =ACPI_TYPE_STRING ;if (type != valid_type)return -EINVAL ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 232/438 watchdog: da9062: fix suspend/resume handling of HW_RUNNING watchdog
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Li Jun <[email protected]>
[ Upstream commit 5071122bf5a628494db16d98d253f622a5aab074 ]
da9062_wdt_suspend() and da9062_wdt_resume() only check watchdog_active(), when the watchdog is left running by the driver sets WDOG_HW_RUNNING in da9062_wdt_probe() but userspace never opens the device, so WDOG_ACTIVE remains cleared, the wdt_disable() will not be executed in da9062_wdt_suspend. In this case, the suspend callback is a no-op and the watchdog keeps counting during system suspend, leading to an unexpected system reset.
Check WDOG_HW_RUNNING and wdt->wdd,can fix this issue.
--- drivers/watchdog/da9062_wdt.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/watchdog/da9062_wdt.c +2 -2
@@ -256,7 +256,7 @@ static int __maybe_unused da9062_wdt_suspend(struct device *dev)if (!wdt->use_sw_pm)return 0 ; -if (watchdog_active (wdd)) +if (watchdog_active (wdd) ||watchdog_hw_running (wdd))return da9062_wdt_stop (wdd);return 0 ;
@@ -270,7 +270,7 @@ static int __maybe_unused da9062_wdt_resume(struct device *dev)if (!wdt->use_sw_pm)return 0 ; -if (watchdog_active (wdd)) +if (watchdog_active (wdd) ||watchdog_hw_running (wdd))return da9062_wdt_start (wdd);return 0 ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 233/438 Input: xpad - add support for Victrix Pro BFG Controller
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Erich Sartison <[email protected]>
commit 971fa7ea8621e123feb9c8d7dc61be1c656bd945 upstream.
The controller doesn't currently work via USB-cable.
--- drivers/input/joystick/xpad.c | 1 + 1 file changed, 1 insertion(+)
drivers/input/joystick/xpad.c +1 -0
@@ -227,6 +227,7 @@ static const struct xpad_device { { 0x0e6f, 0x0213, "Afterglow Gamepad for Xbox 360", 0, XTYPE_XBOX360 }, { 0x0e6f, 0x021f, "Rock Candy Gamepad for Xbox 360", 0, XTYPE_XBOX360 }, { 0x0e6f, 0x0246, "Rock Candy Gamepad for Xbox One 2015", 0, XTYPE_XBOXONE }, + { 0x0e6f, 0x024c, "PDP Victrix Pro BFG Wired Controller for Xbox", 0, XTYPE_XBOXONE }, { 0x0e6f, 0x02a0, "PDP Xbox One Controller", 0, XTYPE_XBOXONE }, { 0x0e6f, 0x02a1, "PDP Xbox One Controller", 0, XTYPE_XBOXONE }, { 0x0e6f, 0x02a2, "PDP Wired Controller for Xbox One - Crimson Red", 0, XTYPE_XBOXONE },
Greg Kroah-Hartman
maintainerPATCH 234/438 Input: xpad - add support for Azeron devices
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Roberts Kursitis <[email protected]>
commit cba76c0f47af1a389d718c5bb69e75cbd67bba98 upstream.
Azeron controllers (Cyro, Cyborg, Classic/Compact, Cyro Lefty, Cyborg II and Keyzen) present a standard Xbox 360 controller interface, so they work with the existing xpad driver once their USB IDs are added.
The 0x16d0 vendor ID is a shared block, but this is safe because xpad only binds interfaces that match the Xbox 360 signature.
Tested with an Azeron Keyzen.
--- drivers/input/joystick/xpad.c | 7 +++++++ 1 file changed, 7 insertions(+)
drivers/input/joystick/xpad.c +7 -0
@@ -293,6 +293,12 @@ static const struct xpad_device { { 0x1689, 0xfd00, "Razer Onza Tournament Edition", 0, XTYPE_XBOX360 }, { 0x1689, 0xfd01, "Razer Onza Classic Edition", 0, XTYPE_XBOX360 }, { 0x1689, 0xfe00, "Razer Sabertooth", 0, XTYPE_XBOX360 }, + { 0x16d0, 0x1103, "Azeron Cyro", 0, XTYPE_XBOX360 }, + { 0x16d0, 0x113c, "Azeron Cyborg", 0, XTYPE_XBOX360 }, + { 0x16d0, 0x1192, "Azeron Classic/Compact", 0, XTYPE_XBOX360 }, + { 0x16d0, 0x1212, "Azeron Cyro Lefty", 0, XTYPE_XBOX360 }, + { 0x16d0, 0x12f7, "Azeron Cyborg II", 0, XTYPE_XBOX360 }, + { 0x16d0, 0x13ea, "Azeron Keyzen", 0, XTYPE_XBOX360 }, { 0x17ef, 0x6182, "Lenovo Legion Controller for Windows", 0, XTYPE_XBOX360 }, { 0x1949, 0x041a, "Amazon Game Controller", 0, XTYPE_XBOX360 }, { 0x1a86, 0xe310, "Legion Go S", 0, XTYPE_XBOX360 },
@@ -535,6 +541,7 @@ static const struct usb_device_id xpad_tXPAD_XBOX360_VENDOR (0x15e4 ),/* Numark Xbox 360 controllers */ XPAD_XBOX360_VENDOR (0x162e ),/* Joytech Xbox 360 controllers */ XPAD_XBOX360_VENDOR (0x1689 ),/* Razer Onza */ +XPAD_XBOX360_VENDOR (0x16d0 ),/* Azeron controllers */ XPAD_XBOX360_VENDOR (0x17ef ),/* Lenovo */ XPAD_XBOX360_VENDOR (0x1949 ),/* Amazon controllers */ XPAD_XBOX360_VENDOR(0x1a86), /* Nanjing Qinheng Microelectronics (WCH) */
Greg Kroah-Hartman
maintainerPATCH 235/438 Input: xpad - fix PDP Marvel Xbox 360 controller
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jeremy Nyberg <[email protected]>
commit 7bc369cb3d3f3656eb77285628ee264264d28ad4 upstream.
The PDP Marvel Xbox 360 controller with USB ID 0e6f:0147 is incorrectly classified as an Xbox One controller.
With the current XTYPE_XBOXONE classification, the controller is detected but produces no input, while its four player LEDs continue blinking indefinitely.
Classify USB ID 0e6f:0147 as an Xbox 360 controller instead.
Tested on a PDP Marvel Xbox 360 controller with USB ID 0e6f:0147. All inputs register correctly and the player LED indicates the current player.
--- drivers/input/joystick/xpad.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/input/joystick/xpad.c +1 -1
@@ -215,7 +215,7 @@ static const struct xpad_device { { 0x0e6f, 0x0139, "Afterglow Prismatic Wired Controller", 0, XTYPE_XBOXONE }, { 0x0e6f, 0x013a, "PDP Xbox One Controller", 0, XTYPE_XBOXONE }, { 0x0e6f, 0x0146, "Rock Candy Wired Controller for Xbox One", 0, XTYPE_XBOXONE }, - { 0x0e6f, 0x0147, "PDP Marvel Xbox One Controller", 0, XTYPE_XBOXONE }, + { 0x0e6f, 0x0147, "PDP Marvel Xbox 360 Controller", 0, XTYPE_XBOX360 }, { 0x0e6f, 0x015c, "PDP Xbox One Arcade Stick", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOXONE }, { 0x0e6f, 0x015d, "PDP Mirror's Edge Official Wired Controller for Xbox One", 0, XTYPE_XBOXONE }, { 0x0e6f, 0x0161, "PDP Xbox One Controller", 0, XTYPE_XBOXONE },
Greg Kroah-Hartman
maintainerPATCH 236/438 9p: Fix v9fs_issue_write() to update i_size and remote_i_size
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Howells <[email protected]>
commit c60ae98c5aa64021751b38ab1313b19d620bf640 upstream.
Fix v9fs_issue_write() to update i_size and remote_i_size to the new size of the server file if we made it larger, using the start fpos and the count returned by p9_client_write() to calculate the new minimum file size.
This assumes that if the 9P server makes a short write (say it hits ENOSPC), a reduced count is returned.
Message-ID: <[email protected]>
--- fs/9p/vfs_addr.c | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-)
fs/9p/vfs_addr.c +27 -1
@@ -54,11 +54,37 @@ static void v9fs_begin_writeback(structstatic void v9fs_issue_write (struct netfs_io_subrequest *subreq) {struct p9_fid *fid = subreq->rreq->netfs_priv; +struct inode *inode = subreq->rreq->inode; +struct netfs_inode *ictx =netfs_inode (inode);int err, len; len =p9_client_write (fid, subreq->start, &subreq->io_iter, &err); -if (len >0 ) +if (len >0 ) { +uoff_t end = subreq->start + len, i_size, remote, zp; +bool set = false; + +spin_lock (&inode->i_lock); + +/* We can read the sizes directly as we hold i_lock. */ + i_size = inode->i_size; + remote = ictx->_remote_i_size; + zp = ictx->_zero_point; + +if (end > i_size) { + i_size = end; + set = true; + } +if (end > remote) { + remote = end; + set = true; + } + +if (set) +netfs_write_sizes (inode, i_size, remote, zp); +spin_unlock (&inode->i_lock); +__set_bit (NETFS_SREQ_MADE_PROGRESS , &subreq->flags); + }netfs_write_subrequest_terminated (subreq, len ?: err); }
Greg Kroah-Hartman
maintainerPATCH 237/438 ALSA: core: Fix potential UAF after asynchronous card release
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Takashi Iwai <[email protected]>
commit fd95e68df6fe66344161a1329cbe5e5805e7b704 upstream.
Usually a sound driver releases the resources assigned to the card via snd_card_free(), and it synchronizes with the whole release procedure. However, when the card is released asynchronously via snd_card_free_when_closed() like USB-audio driver, the situation is slightly different; although the snd_card_disconnect() call at the disconnection guarantees that any newer accesses will be gated, the in-flight tasks might be still accessing to the underlying card->dev device even after the disconnection, which would cause a use-after-free in the end, as reported by fuzzers.
For addressing the bug above, this patch takes the refcount of card->dev at initialization of the card object, and releases at its
destructor. This assures the availability of the card->dev in its
whole lifecycle.
--- sound/core/init.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
sound/core/init.c +2 -1
@@ -310,7 +310,7 @@ static int snd_card_init(struct snd_cardkfree (card);/* manually free here, as no destructor called */ return err; } - card->dev = parent; + card->dev =get_device (parent); card->number = idx;WARN_ON (IS_MODULE (CONFIG_SND ) && !module); card->module = module;
@@ -601,6 +601,7 @@ static int snd_card_do_free(struct snd_cdev_warn (card->dev,"unable to free card info\n" );/* Not fatal error */ } +put_device (card->dev);if (card->release_completion)complete (card->release_completion);if (!managed)
Greg Kroah-Hartman
maintainerPATCH 238/438 ALSA: usb-audio: Clamp implicit feedback packet count to URB capacity
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xiang Mei <[email protected]>
commit 76a986c980bb502c7688d605ac7a67fd257a9a1b upstream.
data_ep_set_params() allocates each data URB for exactly u->packets isochronous frames, so urb->iso_frame_desc[] has u->packets slots and ctx->packets is the driver's only record of that limit. For an implicit feedback sink, snd_usb_queue_pending_output_urbs() overwrites it with the sync source's packet count, which is calculated independently from the capture endpoint's parameters. When that count is larger, prepare_playback_urb() and prepare_silent_urb() can write iso_frame_desc[] past the allocation; their existing bounds limit payload bytes, not the descriptor index.
The reproducer uses a high-speed UAC2 device declaring bInterval 1 for implicit feedback capture (8 packets) and bInterval 4 for playback (1 packet). On the first capture completion after the stream starts, it accesses seven descriptors spanning 112 bytes beyond the one-packet URB:
BUG: KASAN: slab-out-of-bounds in prepare_playback_urb (sound/usb/pcm.c:1560) Write of size 4 at addr ffff88801e696ad0 by task vhci_rx/178 prepare_playback_urb (sound/usb/pcm.c:1560) prepare_outbound_urb (sound/usb/endpoint.c:340) snd_usb_queue_pending_output_urbs (sound/usb/endpoint.c:501) snd_complete_urb (sound/usb/endpoint.c:1834) __usb_hcd_giveback_urb (drivers/usb/core/hcd.c:1657) usb_hcd_giveback_urb (drivers/usb/core/hcd.c:1741) vhci_rx_loop (drivers/usb/usbip/vhci_rx.c:107) kthread (kernel/kthread.c:436) The buggy address belongs to the object at ffff88801e696a00 which belongs to the cache kmalloc-256 of size 256 The buggy address is located 0 bytes to the right of allocated 208-byte region [ffff88801e696a00, ffff88801e696ad0)
Record the allocated packet count per endpoint and clamp both the adopted count and the packet-size copy to it. Fold the Format Type II delimiter into urb_packs before the allocation loop so the recorded limit matches every URB.
--- sound/usb/card.h | 1 + sound/usb/endpoint.c | 12 +++++++----- 2 files changed, 8 insertions(+), 5 deletions(-)
sound/usb/card.h +1 -0
@@ -116,6 +116,7 @@ struct snd_usb_endpoint {unsigned int phase;/* phase accumulator */ unsigned int maxpacksize;/* max packet size in bytes */ unsigned int maxframesize;/* max packet size in frames */ +unsigned int max_urb_packs;/* packets allocated per data URB */ unsigned int max_urb_frames;/* max URB size in frames */ unsigned int curpacksize;/* current packet size in bytes (for capture) */ unsigned int curframesize;/* current packet size in frames (for capture) */
sound/usb/endpoint.c +7 -5
@@ -492,9 +492,10 @@ int snd_usb_queue_pending_output_urbs(st/* copy over the length information */ if (implicit_fb) { - ctx->packets = packet->packets; + ctx->packets =min_t (int, packet->packets, + ep->max_urb_packs);memcpy (ctx->packet_size, packet->packet_size, - packet->packets *sizeof (packet->packet_size[0 ])); + ctx->packets *sizeof (packet->packet_size[0 ])); }/* call the data handler to fill in playback data */
@@ -1239,15 +1240,16 @@ static int data_ep_set_params(struct snd ep->nurbs =min (max_urbs, urbs_per_period * ep->cur_buffer_periods); } +if (fmt->fmt_type ==UAC_FORMAT_TYPE_II ) + urb_packs++;/* for transfer delimiter */ + ep->max_urb_packs = urb_packs; +/* allocate and initialize data urbs */ for (i =0 ; i < ep->nurbs; i++) {struct snd_urb_ctx *u = &ep->urb[i]; u->index = i; u->ep = ep; u->packets = urb_packs; - -if (fmt->fmt_type ==UAC_FORMAT_TYPE_II ) - u->packets++;/* for transfer delimiter */ u->buffer_size = maxsize * u->packets; u->urb =usb_alloc_urb (u->packets,GFP_KERNEL );if (!u->urb)
Greg Kroah-Hartman
maintainerPATCH 239/438 ALSA: virtio: reset device before deleting virtqueues
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yuho Choi <[email protected]>
commit 6c05d00af307560e6a9f1631d6270d3df5aa2272 upstream.
virtsnd_remove() and virtsnd_freeze() delete the virtqueues before resetting the device. del_vqs() frees the vring backing, but does not provide a generic device quiesce operation. In particular, modern virtio-pci keeps enabled queues active until the device is reset.
Reset the device before deleting the virtqueues so it can no longer access the vring memory when that memory is released. This also covers probe failures after DRIVER_OK, which unwind through virtsnd_remove().
--- sound/virtio/virtio_card.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
sound/virtio/virtio_card.c +2 -2
@@ -354,8 +354,8 @@ static void virtsnd_remove(struct virtioif (snd->card)snd_card_free (snd->card); - vdev->config->del_vqs (vdev);virtio_reset_device (vdev); + vdev->config->del_vqs (vdev);for (i =0 ; snd->substreams && i < snd->nsubstreams; ++i) {struct virtio_pcm_substream *vss = &snd->substreams[i];
@@ -383,8 +383,8 @@ static int virtsnd_freeze(struct virtio_virtsnd_disable_event_vq (snd);virtsnd_ctl_msg_cancel_all (snd); - vdev->config->del_vqs (vdev);virtio_reset_device (vdev); + vdev->config->del_vqs (vdev);for (i =0 ; i < snd->nsubstreams; ++i)cancel_work_sync (&snd->substreams[i].elapsed_period);
Greg Kroah-Hartman
maintainerPATCH 240/438 ASoC: codecs: rt712-sdca-dmic: fix uninitialized stream_config->type
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jiangshan Yi <[email protected]>
commit 03a5699a0a04309c597683967aaaf25d1e555ea2 upstream.
stream_config is not initialized before being passed to sdw_stream_add_slave(). The type field may contain garbage and is later copied to stream->type by sdw_config_stream().
Zero-initialize stream_config so type defaults to SDW_STREAM_PCM.
While at it, use snd_sdw_params_to_config() helper instead of open-coding the same logic.
--- sound/soc/codecs/rt712-sdca-dmic.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-)
sound/soc/codecs/rt712-sdca-dmic.c +5 -9
@@ -13,6 +13,7 @@#include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> +#include <sound/sdw.h> #include <sound/tlv.h> #include "rt712-sdca.h" #include "rt712-sdca-dmic.h"
@@ -632,10 +633,10 @@ static int rt712_sdca_dmic_hw_params(str {struct snd_soc_component *component = dai->component;struct rt712_sdca_dmic_priv *rt712 =snd_soc_component_get_drvdata (component); -struct sdw_stream_config stream_config; +struct sdw_stream_config stream_config = {0 };struct sdw_port_config port_config;struct sdw_stream_runtime *sdw_stream; -int retval, num_channels; +int retval;unsigned int sampling_rate;dev_dbg (dai->dev,"%s %s" , __func__, dai->name);
@@ -647,13 +648,8 @@ static int rt712_sdca_dmic_hw_params(strif (!rt712->slave)return -EINVAL ; - stream_config.frame_rate =params_rate (params); - stream_config.ch_count =params_channels (params); - stream_config.bps =snd_pcm_format_width (params_format (params)); - stream_config.direction =SDW_DATA_DIR_TX ; - - num_channels =params_channels (params); - port_config.ch_mask =GENMASK (num_channels -1 ,0 ); +/* SoundWire specific configuration */ +snd_sdw_params_to_config (substream, params, &stream_config, &port_config); port_config.num =2 ; retval = sdw_stream_add_slave(rt712->slave, &stream_config,
Greg Kroah-Hartman
maintainerPATCH 241/438 cgroup: Avoid iteration of dying tasks with zero refcount
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Michal Koutný <[email protected]>
commit 057dac23d329d5c5ed62352f2659a39fd46c6d4a upstream.
The commit 260fbcb92bbea ("cgroup: Move dying_tasks cleanup from cgroup_task_release() to cgroup_task_free()") extended the lifetime of tasks on the dying_tasks list.
The iterators have provision to go through dying_tasks because of dying threadgroup leaders or explicit CSS_TASK_ITER_WITH_DEAD, however, it was expected that such tasks can obtain a new reference (that is possible before cgroup_task_release()/put_task_struct_rcu_user()). The tasks after cgroup_task_release() and before cgroup_task_free() are subject to race when they may or may not have ->usage count > 0.
The race window is between css_task_iter_next() invocations when css_set_lock is released and we may arrive at a new ->task_pos. The iterator should not attempt to resurrect tasks whose ->usage count dropped to zero. (When that happens, __put_task_struct_rcu_cb() is already imminent and the returned task_struct would could be used after free.)
As for the fix, we cannot simply check the signal->live count of a task on the dying list because that won't distinguish regular zombies waiting to be reaped from RCU remnant tasks that are going to be free'd. Therefore add an extra check to rule out ->usage==0 tasks from any iteration.
The repeat: loop in css_task_iter_advance() doesn't consider ->usage count, so add a new loop to css_task_iter_next() to skip de-used tasks on the dying_list.
Rough illustration of the possible race
R (reader of cgroup.procs) T (thread) L (group leader)
--------------------------------- -------------------------------- --------------------------------
L exits, signal->live > 0
cgroup_task_dead(L)
css_set_skip_task_iters() // skips only cset->tasks
list_add_tail(&L->cg_list, &cset->dying_tasks)
css_task_iter_next()
take css_set_lock
css_task_iter_advance()
leader && signal->live != 0
=> it->task_pos = &L->cg_list
release css_set_lock
T exits
--signal->live == 0
cgroup_task_dead(T) // css_set_lock
release_task(T)
cgroup_task_release(T)
release_task(L) // zap_leader
cgroup_task_release(L)
put_task_struct_rcu_user(L)
...RCU...
put_task_struct(L)
L->usage = 0
/* L still on dying_tasks */
...RCU...
__put_task_struct(L)
css_task_iter_next() // another iteration
take css_set_lock
it->task_pos = &L->cg_list
get_task_struct(L)
=> addition on 0
drop css_set_lock
cgroup_task_free(L)
css_set_skip_task_iters() // dying skip comes too late
free_task(L)
cgroup_procs_show()
task_pid_vnr(L)--- kernel/cgroup/cgroup.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-)
kernel/cgroup/cgroup.c +5 -2
@@ -5228,10 +5228,13 @@ struct task_struct *css_task_iter_next(sif (it->flags &CSS_TASK_ITER_SKIPPED )css_task_iter_advance (it); -if (it->task_pos) { +while (it->task_pos && !it->cur_task) { it->cur_task = list_entry(it->task_pos, struct task_struct, cg_list); -get_task_struct (it->cur_task); +/* a task on dying_tasks with zero refcount is only valid for +* RCU readers, not even interesting for +* CSS_TASK_ITER_WITH_DEAD, find another one */ + it->cur_task =tryget_task_struct (it->cur_task);css_task_iter_advance (it); }
Greg Kroah-Hartman
maintainerPATCH 242/438 ata: libahci: clear PxCLBU and PxFBU for AHCI_HFLAG_32BIT_ONLY
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Niklas Cassel <[email protected]>
commit 82e47533221d4746947b74d2e79a478c36c6433a upstream.
A user reported that commit 105c42566a55 ("ata: ahci: force 32-bit DMA for JMicron JMB582/JMB585") made the JMicron JMB585 unusable on his board.
The failure is seen as soon as the ahci driver is probed, and booting with iommu=off does not solve the problem.
Looking at the AHCI specification, PxCLBU and PxFBU are both read only '0' for HBAs that do not support 64-bit addressing.
For HBAs that do support 64-bit addressing, the registers are read write, with a reset value that is Implementation Specific.
When using the AHCI_HFLAG_32BIT_ONLY flag, the HBA does support 64-bit addressing, and a 32-bit DMA mask is set by simply clearing HOST_CAP_64. Thus, in this case, we need to explicitly clear the registers to 0.
--- drivers/ata/libahci.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-)
drivers/ata/libahci.c +14 -1
@@ -744,15 +744,28 @@ void ahci_start_fis_rx(struct ata_port *struct ahci_port_priv *pp = ap->private_data;u32 tmp; -/* set FIS registers */ +/* +* On HBAs that only support 32-bit addressing PxCLBU is read only '0'. +* When applying the AHCI_HFLAG_32BIT_ONLY quirk, PxCLBU is RW, and the +* reset value is Implementation Specific, so we need to clear it to 0. +*/ if (hpriv->cap &HOST_CAP_64 )writel ((pp->cmd_slot_dma >>16 ) >>16 , port_mmio +PORT_LST_ADDR_HI ); +else if (hpriv->flags &AHCI_HFLAG_32BIT_ONLY ) +writel (0 , port_mmio +PORT_LST_ADDR_HI );writel (pp->cmd_slot_dma &0xffffffff , port_mmio +PORT_LST_ADDR ); +/* +* On HBAs that only support 32-bit addressing PxFBU is read only '0'. +* When applying the AHCI_HFLAG_32BIT_ONLY quirk, PxFBU is RW, and the +* reset value is Implementation Specific, so we need to clear it to 0. +*/ if (hpriv->cap &HOST_CAP_64 )writel ((pp->rx_fis_dma >>16 ) >>16 , port_mmio +PORT_FIS_ADDR_HI ); +else if (hpriv->flags &AHCI_HFLAG_32BIT_ONLY ) +writel (0 , port_mmio +PORT_FIS_ADDR_HI );writel (pp->rx_fis_dma &0xffffffff , port_mmio +PORT_FIS_ADDR );/* enable FIS reception */
Greg Kroah-Hartman
maintainerPATCH 243/438 ata: libahci_platform: Fix device reference leak in ahci_platform_get_resources()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Wentao Liang <[email protected]>
commit 0d1cb83337f13af082afb68b28d3fdfe29cde7fb upstream.
of_find_device_by_node() takes a reference on the port platform device, which is only used to look up its port regulator and is never released, neither on success nor on the error paths. Drop the reference with put_device() once the regulator has been obtained, which covers both the success and error paths.
--- drivers/ata/libahci_platform.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/ata/libahci_platform.c +1 -1
@@ -620,10 +620,10 @@ struct ahci_host_priv *ahci_platform_getof_platform_device_create (child,NULL ,NULL ); port_dev =of_find_device_by_node (child); -if (port_dev) { rc =ahci_platform_get_regulator (hpriv, port, &port_dev->dev); +put_device (&port_dev->dev);if (rc == -EPROBE_DEFER ) goto err_out; }
Greg Kroah-Hartman
maintainerPATCH 244/438 cifs: Fix server use-after-free in cifs_chan_skip_or_disable()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Wentao Liang <[email protected]>
commit 717e0a25036b6c92cecace30913b2d874a4c22b8 upstream.
When a secondary channel is no longer supported by the server, cifs_chan_skip_or_disable() drops the channel reference with cifs_put_tcp_session() and then continues to use the server pointer by calling cifs_signal_cifsd_for_reconnect() on it and reading its primary_server pointer. cifs_put_tcp_session() can drop the last reference of the channel and tear it down, so both the channel and the primary server (whose reference is also dropped by cifs_put_tcp_session()) can be freed before they are signaled for reconnect.
Signal the channel and the primary server and capture the primary server pointer before dropping the channel reference with cifs_put_tcp_session().
--- fs/smb/client/smb2pdu.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-)
fs/smb/client/smb2pdu.c +7 -6
@@ -189,18 +189,19 @@ cifs_chan_skip_or_disable(struct cifs_sespin_unlock (&ses->chan_lock);/* -* the above reference of server by channel -* needs to be dropped without holding chan_lock -* as cifs_put_tcp_session takes a higher lock -* i.e. cifs_tcp_ses_lock +* signal the channel and its primary server to +* reconnect before dropping the above reference of +* server by channel, which is done without holding +* chan_lock as cifs_put_tcp_session takes a higher +* lock i.e. cifs_tcp_ses_lock */ -cifs_put_tcp_session (server, from_reconnect); -cifs_signal_cifsd_for_reconnect (server, false);/* mark primary server as needing reconnect */ pserver = server->primary_server;cifs_signal_cifsd_for_reconnect (pserver, false); + +cifs_put_tcp_session (server, from_reconnect); skip_terminate:return -EHOSTDOWN ; }
Greg Kroah-Hartman
maintainerPATCH 245/438 exec: Cleanup POSIX timers right after de_thread()
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Hyunwoo Kim <[email protected]>
commit acb03d3881818581052924a9bbbe92b8741ed448 upstream.
A per-thread CPU timer holds a reference to the PID of the thread it is attached to and, while it is armed, its node is queued in that thread's posix_cputimers. The task is looked up by that PID.
When a non-leader thread exec()s, de_thread() changes which task owns that PID. pid_task(timer->it.cpu.pid, PIDTYPE_PID) then returns NULL, but the node is still queued on tsk, which is alive. timer_lock_sighand() takes a failed lookup to mean that the node is already dequeued, so it has nothing to undo.
begin_new_exec() calls posix_cpu_timers_exit(me) right after exec_task_namespaces() and that removes the leftover node, so the state normally stays invisible. But bprm->point_of_no_return is set before de_thread(), so if unshare_files(), set_mm_exe_file(), exec_mmap() or exec_task_namespaces() fails, the task dies before it gets there. exit_itimers() then frees the k_itimer while its node is still queued, and reaping tsk later erases that freed node from the rbtree.
In short:
the non-leader thread B the parent
timer_create(CLOCK_THREAD_CPUTIME_ID)
timer_settime()
arm_timer() // the node is queued on B
execve()
de_thread(B)
exchange_tids(B, leader) // B's PID now belongs to the leader release_task(leader)
__exit_signal(leader)
posix_cpu_timers_exit(leader) // cleans leader's queue, not B's __unhash_process(leader) // that PID has no task anymore
exec_mmap()
mmap_read_lock_killable(old_mm)
kill(B, SIGKILL)
// -EINTR
get_signal()
do_exit()
exit_itimers()
posix_timer_delete()
posix_cpu_timer_del()
posix_timer_unhash_and_free() // freed while still queued
wait4()
release_task(B)
posix_cpu_timers_exit(B)
cleanup_timerqueue()
timerqueue_del() // use-after-free
Move the POSIX timer cleanup right after de_thread() before any of the later failure conditions brings the task into do_exit().
[ tglx: Move the cleanup right after de_thread() ]
--- fs/exec.c | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-)
fs/exec.c +21 -8
@@ -1104,6 +1104,17 @@ void __set_task_comm(struct task_structperf_event_comm (tsk, exec); } +static void posixtimer_exec (struct task_struct *me) +{ +#ifdef CONFIG_POSIX_TIMERS +spin_lock_irq (&me->sighand->siglock); +posix_cpu_timers_exit (me); +spin_unlock_irq (&me->sighand->siglock); +exit_itimers (me); +flush_itimer_signals (); +#endif +} +/* * Calling this is the point of no return. None of the failures will be * seen by userspace since either the process is already taking a fatal
@@ -1137,6 +1148,16 @@ int begin_new_exec(struct linux_binprm * retval =de_thread (me);if (retval) goto out; + +/* +* This must be done here to ensure that POSIX CPU timers which were +* armed on the current task are dequeued from me::posix_cputimers. +* Otherwise in case of a TID switch the deletion of the related POSIX +* timer would not remove an enqueued timer because the TID lookup +* of the old TID fails. +*/ +posixtimer_exec (me); +/* see the comment in check_unsafe_exec() */ current->fs->in_exec =0 ;/*
@@ -1191,14 +1212,6 @@ int begin_new_exec(struct linux_binprm *if (retval) goto out_unlock; -#ifdef CONFIG_POSIX_TIMERS -spin_lock_irq (&me->sighand->siglock); -posix_cpu_timers_exit (me); -spin_unlock_irq (&me->sighand->siglock); -exit_itimers (me); -flush_itimer_signals (); -#endif -/* * Make the signal table private. */
Greg Kroah-Hartman
maintainerPATCH 246/438 fs/dax: check zero or empty entry before converting xarray entry
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Seunguk Shin <[email protected]>
commit 8e2b8614039853e68d5338e37821e8bcee9fc05f upstream.
Calling dax_to_folio() with empty entry causes kernel panic below when booting a VM with DAX enabled storage.
This patch checks empty entry before calling dax_to_folio() on dax_associate_entry(), dax_disassociate_entry(), and dax_busy_page().
Commit 98c183a4fccf ("fs/dax: don't disassociate zero page entries") added guards in the associate and disassociate paths, but the guards still come after dax_to_folio(), and dax_busy_page() still has the same problem.
[ 0.737679] EXT4-fs (pmem0p1): mounted filesystem 79676804-7c8b-491a-b2a6-9bae3c72af70 ro with ordered data mode. Quota mode: disabled. [ 0.737891] VFS: Mounted root (ext4 filesystem) readonly on device 259:1. [ 0.739119] devtmpfs: mounted [ 0.739476] Freeing unused kernel memory: 1920K [ 0.740156] Run /sbin/init as init process [ 0.740229] with arguments: [ 0.740286] /sbin/init [ 0.740321] with environment: [ 0.740369] HOME=/ [ 0.740400] TERM=linux [ 0.743162] Unable to handle kernel paging request at virtual address fffffdffbf000008 [ 0.743285] Mem abort info: [ 0.743316] ESR = 0x0000000096000006 [ 0.743371] EC = 0x25: DABT (current EL), IL = 32 bits [ 0.743444] SET = 0, FnV = 0 [ 0.743489] EA = 0, S1PTW = 0 [ 0.743545] FSC = 0x06: level 2 translation fault [ 0.743610] Data abort info: [ 0.743656] ISV = 0, ISS = 0x00000006, ISS2 = 0x00000000 [ 0.743720] CM = 0, WnR = 0, TnD = 0, TagAccess = 0 [ 0.743785] GCS = 0, Overlay = 0, DirtyBit = 0, Xs = 0 [ 0.743848] swapper pgtable: 4k pages, 48-bit VAs, pgdp=00000000b9d17000 [ 0.743931] [fffffdffbf000008] pgd=10000000bfa3d403, p4d=10000000bfa3d403, pud=1000000040bfe403, pmd=0000000000000000 [ 0.744070] Internal error: Oops: 0000000096000006 [#1] SMP [ 0.748888] CPU: 0 UID: 0 PID: 1 Comm: init Not tainted 6.18.4 #1 NONE [ 0.749421] pstate: 004000c5 (nzcv daIF +PAN -UAO -TCO -DIT -SSBS BTYPE=--) [ 0.749969] pc : dax_disassociate_entry.constprop.0+0x20/0x50 [ 0.750444] lr : dax_insert_entry+0xcc/0x408 [ 0.750802] sp : ffff80008000b9e0 [ 0.751083] x29: ffff80008000b9e0 x28: 0000000000000000 x27: 0000000000000000 [ 0.751682] x26: 0000000001963d01 x25: ffff0000004f7d90 x24: 0000000000000000 [ 0.752264] x23: 0000000000000000 x22: ffff80008000bcc8 x21: 0000000000000011 [ 0.752836] x20: ffff80008000ba90 x19: 0000000001963d01 x18: 0000000000000000 [ 0.753407] x17: 0000000000000000 x16: 0000000000000000 x15: 0000000000000000 [ 0.753970] x14: ffffbf3154b9ae70 x13: 0000000000000000 x12: ffffbf3154b9ae70 [ 0.754548] x11: ffffffffffffffff x10: 0000000000000000 x9 : 0000000000000000 [ 0.755122] x8 : 000000000000000d x7 : 000000000000001f x6 : 0000000000000000 [ 0.755707] x5 : 0000000000000000 x4 : 0000000000000000 x3 : fffffdffc0000000 [ 0.756287] x2 : 0000000000000008 x1 : 0000000040000000 x0 : fffffdffbf000000 [ 0.756871] Call trace: [ 0.757107] dax_disassociate_entry.constprop.0+0x20/0x50 (P) [ 0.757592] dax_iomap_pte_fault+0x4fc/0x808 [ 0.757951] dax_iomap_fault+0x28/0x30 [ 0.758258] ext4_dax_huge_fault+0x80/0x2dc [ 0.758594] ext4_dax_fault+0x10/0x3c [ 0.758892] __do_fault+0x38/0x12c [ 0.759175] __handle_mm_fault+0x530/0xcf0 [ 0.759518] handle_mm_fault+0xe4/0x230 [ 0.759833] do_page_fault+0x17c/0x4dc [ 0.760144] do_translation_fault+0x30/0x38 [ 0.760483] do_mem_abort+0x40/0x8c [ 0.760771] el0_ia+0x4c/0x170 [ 0.761032] el0t_64_sync_handler+0xd8/0xdc [ 0.761371] el0t_64_sync+0x168/0x16c [ 0.761677] Code: f9453021 f2dfbfe3 cb813080 8b001860 (f9400401) [ 0.762168] ---[ end trace 0000000000000000 ]--- [ 0.762550] note: init[1] exited with irqs disabled [ 0.762631] Kernel panic - not syncing: Attempted to kill init! exitcode=0x0000000b
--- fs/dax.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-)
fs/dax.c +6 -3
@@ -480,11 +480,12 @@ static void dax_associate_entry(void *en unsigned long address, bool shared) {unsigned long size =dax_entry_size (entry), index; -struct folio *folio =dax_to_folio (entry); +struct folio *folio;if (dax_is_zero_entry (entry) ||dax_is_empty_entry (entry))return ; + folio =dax_to_folio (entry); index =linear_page_index (vma, address & ~(size -1 ));if (shared && (folio->mapping ||dax_folio_is_shared (folio))) {if (folio->mapping)
@@ -505,21 +506,23 @@ static void dax_associate_entry(void *enstatic void dax_disassociate_entry (void *entry,struct address_space *mapping,bool trunc) { -struct folio *folio =dax_to_folio (entry); +struct folio *folio;if (dax_is_zero_entry (entry) ||dax_is_empty_entry (entry))return ; + folio =dax_to_folio (entry);dax_folio_put (folio); }static struct page *dax_busy_page (void *entry) { -struct folio *folio =dax_to_folio (entry); +struct folio *folio;if (dax_is_zero_entry (entry) ||dax_is_empty_entry (entry))return NULL ; + folio =dax_to_folio (entry);if (folio_ref_count (folio) -folio_mapcount (folio))return &folio->page;else
Greg Kroah-Hartman
maintainerPATCH 247/438 PCI: imx6: Move clock enable after core reset assertion
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Richard Zhu <[email protected]>
commit c9dc7d730319ad64b51570c5387f1fee7b07b510 upstream.
Commit 610fa91d9863 ("PCI: imx6: Assert PERST# before enabling regulators") inadvertently moved clock enablement before core reset assertion, breaking PCI device initialization on i.MX6Q Apalis platforms with ASM1061/ASM1062 SATA controllers connected:
imx6q-pcie 1ffc000.pcie: host bridge /soc/pcie@1ffc000 ranges:
imx6q-pcie 1ffc000.pcie: IO 0x0001f80000..0x0001f8ffff -> 0x0000000000
imx6q-pcie 1ffc000.pcie: MEM 0x0001000000..0x0001efffff -> 0x0001000000
imx6q-pcie 1ffc000.pcie: config reg[1] 0x01f00000 == cpu 0x01f00000 imx6q-pcie 1ffc000.pcie: iATU: unroll F, 4 ob, 4 ib, align 64K, limit 4G imx6q-pcie 1ffc000.pcie: Link: Only Gen1 is enabled
imx6q-pcie 1ffc000.pcie: Link failed to come up. LTSSM: POLL_CONFIG imx6q-pcie 1ffc000.pcie: probe with driver imx6q-pcie failed with error -110
NOTE: It is not 100% clear if the issue is specific to the ASM1061/ASM1062 device or on the specific power-up sequence (reset vs cold-power-on).
To fix this regression, restore the original sequence where clocks are enabled after asserting core reset and configuring the controller type.
--- drivers/pci/controller/dwc/pci-imx6.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-)
drivers/pci/controller/dwc/pci-imx6.c +6 -6
@@ -1388,12 +1388,6 @@ static int imx_pcie_host_init(struct dw_ goto err_pwrctrl_destroy; } - ret =imx_pcie_clk_enable (imx_pcie); -if (ret) { -dev_err (dev,"unable to enable pcie clocks: %d\n" , ret); - goto err_pwrctrl_power_off; - } -if (pp->bridge &&imx_check_flag (imx_pcie,IMX_PCIE_FLAG_HAS_LUT )) { pp->bridge->enable_device = imx_pcie_enable_device; pp->bridge->disable_device = imx_pcie_disable_device;
@@ -1409,6 +1403,12 @@ static int imx_pcie_host_init(struct dw_imx_pcie_configure_type (imx_pcie); + ret =imx_pcie_clk_enable (imx_pcie); +if (ret) { +dev_err (dev,"unable to enable pcie clocks: %d\n" , ret); + goto err_pwrctrl_power_off; + } +if (imx_pcie->phy) { ret =phy_init (imx_pcie->phy);if (ret) {
Greg Kroah-Hartman
maintainerPATCH 248/438 phy: renesas: rcar-gen3-usb2: Avoid long delay in atomic context
In reply to Greg Kroah-Hartman7.2-stable review patch. If anyone has any objections, please let me know.
------------------
From: Claudiu Beznea <[email protected]>
commit 48e97c59a49c9e90270f5e3d221db253b76de5da upstream.
The OTG PHY initialization sequence needs to wait for 20 ms at a specific step, as described in commit 72c0339c115b ("phy: renesas:
rcar-gen3-usb2: follow the hardware manual procedure").
Commit 55a387ebb921 ("phy: renesas: rcar-gen3-usb2: Lock around hardware registers and driver data") tried to address various problems in the rcar-gen3-usb2 driver and converted the mutex protecting HW register accesses to a spin lock, leaving, however, a long delay in the critical section protected by the spin lock. This may become a problem, especially on RT kernels.
To address this, release the spin lock before sleeping for 20 ms as required by the HW manual and reacquire it afterwards. To avoid other threads entering the critical section and configuring the HW while the software is waiting for the OTG initialization to complete, introduce the otg_initializing variable alongside the otg_init_done wait queue. Any other thread trying to configure the HW while the OTG PHY initialization is in progress waits for the wait queue instead of immediately returning errors to PHY users. The IRQs were also disabled while waiting for the OTG PHY initialization to complete, as the interrupt handler may also apply HW settings.
The OTG can only be initialized once. It is initialized by the first PHY that calls struct phy_ops::rcar_gen3_phy_usb2_init().
To avoid failures when multiple PHYs call struct phy_ops::rcar_gen3_phy_usb2_init() simultaneously, and the PHY responsible for initializing the OTG either fails or deinit quiqly and another PHY takes over the PHY init role), the code waiting for the channel->otg_init_done wait queue retries up to NUM_OF_PHYS times.
--- drivers/phy/renesas/phy-rcar-gen3-usb2.c | 310 ++++++++++++++++++++++++++----- 1 file changed, 265 insertions(+), 45 deletions(-)
drivers/phy/renesas/phy-rcar-gen3-usb2.c +265 -45
@@ -27,6 +27,7 @@#include <linux/reset.h> #include <linux/string.h> #include <linux/usb/of.h> +#include <linux/wait.h> #include <linux/workqueue.h> /******* USB2.0 Host registers (original offset is +0x200) *******/
@@ -106,6 +107,13 @@/* RZ/G2L specific */ #define USB2_LINECTRL1_USB2_IDMON BIT(0) +/* +* The OTG initialization is expected to finish in 20ms. Choose a large enough +* timeout to avoid waiters exit prematurely the waiting section under heavy +* CPU load. +*/ +#define USB2_OTG_INIT_TIMEOUT msecs_to_jiffies(120) +#define NUM_OF_PHYS 4enum rcar_gen3_phy_index {PHY_INDEX_BOTH_HC ,
@@ -138,12 +146,20 @@ struct rcar_gen3_chan {struct rcar_gen3_phy rphys[NUM_OF_PHYS ];struct regulator *vbus;struct work_struct work; +wait_queue_head_t otg_init_done;spinlock_t lock;/* protects access to hardware and driver data structure. */ enum usb_dr_mode dr_mode;bool extcon_host;bool is_otg_channel;bool uses_otg_pins;bool otg_internal_reg; +/* +* The OTG can be initialized only once and needs to release the spinlock +* and wait for 20 ms due to hardware constraints. If a thread executes +* PHY configuration code while the OTG PHY is waiting for the 20 ms, the +* thread will have to wait for the OTG PHY initialization to complete. +*/ +bool otg_initializing; };struct rcar_gen3_phy_drv_data {
@@ -392,26 +408,58 @@ static ssize_t role_store(struct devicestruct rcar_gen3_chan *ch =dev_get_drvdata (dev);bool is_b_device;enum phy_mode cur_mode, new_mode; +int retries =NUM_OF_PHYS ; +unsigned long flags; +int ret = -EIO ; -guard (spinlock_irqsave)(&ch->lock); +spin_lock_irqsave (&ch->lock, flags); -if (!ch->is_otg_channel || !rcar_gen3_is_any_otg_rphy_initialized (ch)) -return -EIO ; +if (!ch->is_otg_channel) + goto unlock; + +while (retries-- && ch->otg_initializing) { +spin_unlock_irqrestore (&ch->lock, flags); + + ret =wait_event_timeout (ch->otg_init_done, !ch->otg_initializing, +USB2_OTG_INIT_TIMEOUT ); + ret = ret ?0 : -ETIMEDOUT ; +if (ret && !retries) + goto exit; + +spin_lock_irqsave (&ch->lock, flags); + } + +/* If another thread started a new initialization just return -EBUSY. */ +if (ch->otg_initializing) { + ret = -EBUSY ; + goto unlock; + }else { + ret =0 ; + } + +if (!rcar_gen3_is_any_otg_rphy_initialized (ch)) { + ret = -EIO ; + goto unlock; + } -if (sysfs_streq (buf,"host" )) +if (sysfs_streq (buf,"host" )) { new_mode =PHY_MODE_USB_HOST ; -else if (sysfs_streq (buf,"peripheral" )) + }else if (sysfs_streq (buf,"peripheral" )) { new_mode =PHY_MODE_USB_DEVICE ; -else -return -EINVAL ; + }else { + ret = -EINVAL ; + goto unlock; + }/* is_b_device: true is B-Device. false is A-Device. */ is_b_device =rcar_gen3_check_id (ch); cur_mode =rcar_gen3_get_phy_mode (ch);/* If current and new mode is the same, this returns the error */ -if (cur_mode == new_mode) -return -EINVAL ; +if (cur_mode == new_mode) { + ret = -EINVAL ; + goto unlock; + }if (new_mode ==PHY_MODE_USB_HOST ) {/* And is_host must be false */ if (!is_b_device)/* A-Peripheral */
@@ -425,7 +473,10 @@ static ssize_t role_store(struct devicercar_gen3_init_for_peri (ch); } -return count; +unlock: +spin_unlock_irqrestore (&ch->lock, flags); +exit: +return ret ?: count; } static ssize_t role_show(struct device *dev, struct device_attribute *attr,
@@ -441,14 +492,11 @@ static ssize_t role_show(struct device * }static DEVICE_ATTR_RW (role); -static void rcar_gen3_init_otg (struct rcar_gen3_chan *ch) +static void rcar_gen3_init_otg_phase0 (struct rcar_gen3_chan *ch) {void __iomem *usb2_base = ch->base;u32 val; -if (!ch->is_otg_channel ||rcar_gen3_is_any_otg_rphy_initialized (ch)) -return ; -/* Should not use functions of read-modify-write a register */ val =readl (usb2_base +USB2_LINECTRL1 ); val = (val & ~USB2_LINECTRL1_DP_RPD) | USB2_LINECTRL1_DPRPD_EN |
@@ -471,7 +519,11 @@ static void rcar_gen3_init_otg(struct rcwritel (val |USB2_ADPCTRL_IDPULLUP , usb2_base +USB2_ADPCTRL ); } } -mdelay (20 ); +} + +static void rcar_gen3_init_otg_phase1 (struct rcar_gen3_chan *ch) +{ +void __iomem *usb2_base = ch->base;writel (0xffffffff , usb2_base +USB2_OBINTSTA );writel (ch->phy_data->obint_enable_bits, usb2_base +USB2_OBINTEN );
@@ -502,6 +554,7 @@ static irqreturn_t rcar_gen3_phy_usb2_irvoid __iomem *usb2_base = ch->base;struct device *dev = ch->dev;irqreturn_t ret =IRQ_NONE ; +unsigned long flags;u32 status;pm_runtime_get_noresume (dev);
@@ -509,33 +562,102 @@ static irqreturn_t rcar_gen3_phy_usb2_irif (pm_runtime_suspended (dev)) goto rpm_put; - scoped_guard(spinlock, &ch->lock) { - status =readl (usb2_base +USB2_OBINTSTA ); -if (status & ch->phy_data->obint_enable_bits) { -dev_vdbg (dev,"%s: %08x\n" , __func__, status); -if (ch->phy_data->vblvl_ctrl) -writel (USB2_OBINTSTA_CLEAR , usb2_base +USB2_OBINTSTA ); -else -writel (ch->phy_data->obint_enable_bits, usb2_base +USB2_OBINTSTA ); -rcar_gen3_device_recognition (ch); -rcar_gen3_configure_vblvl_ctrl (ch); - ret =IRQ_HANDLED ; - } +spin_lock_irqsave (&ch->lock, flags); + + status =readl (usb2_base +USB2_OBINTSTA ); +if (status & ch->phy_data->obint_enable_bits) { +dev_vdbg (dev,"%s: %08x\n" , __func__, status); +if (ch->phy_data->vblvl_ctrl) +writel (USB2_OBINTSTA_CLEAR , usb2_base +USB2_OBINTSTA ); +else +writel (ch->phy_data->obint_enable_bits, usb2_base +USB2_OBINTSTA ); + + ret =IRQ_HANDLED ; + +/* This should not happen! */ +if (ch->otg_initializing) + goto unlock; + +rcar_gen3_device_recognition (ch); +rcar_gen3_configure_vblvl_ctrl (ch); } +unlock: +spin_unlock_irqrestore (&ch->lock, flags); rpm_put:pm_runtime_put_noidle (dev);return ret; } +static void rcar_gen3_phy_usb2_irqs_mask_all (struct rcar_gen3_chan *channel, +u32 *masked_irqs_bits) +{ +u32 val, bitmask =USB2_INT_ENABLE_UCOM_INTEN ; +void __iomem *usb2_base = channel->base; + +for (unsigned int i =0 ; i <NUM_OF_PHYS ; i++) + bitmask |= channel->rphys[i].int_enable_bits; + + val =readl (usb2_base +USB2_INT_ENABLE ); + *masked_irqs_bits = val & bitmask; + val &= ~bitmask; +writel (val, usb2_base +USB2_INT_ENABLE ); + +/* +* Don't report channel->phy_data->obint_enable_bits IRQs. These are +* unmasked anyway in rcar_gen3_init_otg_phase1(). +*/ + val =readl (usb2_base +USB2_OBINTEN ); + val &= ~channel->phy_data->obint_enable_bits; +writel (val, usb2_base +USB2_OBINTEN ); +} + +static void rcar_gen3_phy_usb2_irqs_unmask (struct rcar_gen3_chan *channel, +u32 irqs_bits) +{ +u32 val, bitmask =USB2_INT_ENABLE_UCOM_INTEN ; +void __iomem *usb2_base = channel->base; + +for (unsigned int i =0 ; i <NUM_OF_PHYS ; i++) + bitmask |= channel->rphys[i].int_enable_bits; + + val =readl (usb2_base +USB2_INT_ENABLE ); + val &= ~bitmask; + val |= irqs_bits; +writel (val, usb2_base +USB2_INT_ENABLE ); +} +static int rcar_gen3_phy_usb2_init (struct phy *p) {struct rcar_gen3_phy *rphy =phy_get_drvdata (p);struct rcar_gen3_chan *channel = rphy->ch;void __iomem *usb2_base = channel->base; +int retries =NUM_OF_PHYS ; +unsigned long flags;u32 val; +int ret; + +spin_lock_irqsave (&channel->lock, flags); -guard (spinlock_irqsave)(&channel->lock); +while (retries-- && channel->otg_initializing) { +spin_unlock_irqrestore (&channel->lock, flags); + + ret =wait_event_timeout (channel->otg_init_done, !channel->otg_initializing, +USB2_OTG_INIT_TIMEOUT ); + ret = ret ?0 : -ETIMEDOUT ; +if (ret && !retries) +return ret; + +spin_lock_irqsave (&channel->lock, flags); + } + +/* If another thread started a new initialization just return -EBUSY. */ +if (channel->otg_initializing) { + ret = -EBUSY ; + goto unlock; + }else { + ret =0 ; + }/* Initialize USB2 part */ val =readl (usb2_base +USB2_INT_ENABLE );
@@ -548,8 +670,23 @@ static int rcar_gen3_phy_usb2_init(struc }/* Initialize otg part (only if we initialize a PHY with IRQs). */ -if (rphy->int_enable_bits) -rcar_gen3_init_otg (channel); +if (rphy->int_enable_bits && channel->is_otg_channel && + !rcar_gen3_is_any_otg_rphy_initialized (channel)) { +u32 masked_irq_bits =0 ; + +rcar_gen3_init_otg_phase0 (channel); +rcar_gen3_phy_usb2_irqs_mask_all (channel, &masked_irq_bits); + channel->otg_initializing = true; +spin_unlock_irqrestore (&channel->lock, flags); + +fsleep (20000 ); + +spin_lock_irqsave (&channel->lock, flags); +rcar_gen3_phy_usb2_irqs_unmask (channel, masked_irq_bits); +rcar_gen3_init_otg_phase1 (channel); + channel->otg_initializing = false; +wake_up_all (&channel->otg_init_done); + }if (channel->phy_data->vblvl_ctrl) {/* SIDDQ mode release */
@@ -568,7 +705,10 @@ static int rcar_gen3_phy_usb2_init(struc rphy->initialized = true; -return 0 ; +unlock: +spin_unlock_irqrestore (&channel->lock, flags); + +return ret; }static int rcar_gen3_phy_usb2_exit (struct phy *p)
@@ -576,9 +716,32 @@ static int rcar_gen3_phy_usb2_exit(strucstruct rcar_gen3_phy *rphy =phy_get_drvdata (p);struct rcar_gen3_chan *channel = rphy->ch;void __iomem *usb2_base = channel->base; +int retries =NUM_OF_PHYS ; +unsigned long flags;u32 val; +int ret; + +spin_lock_irqsave (&channel->lock, flags); + +while (retries-- && channel->otg_initializing) { +spin_unlock_irqrestore (&channel->lock, flags); + + ret =wait_event_timeout (channel->otg_init_done, !channel->otg_initializing, +USB2_OTG_INIT_TIMEOUT ); + ret = ret ?0 : -ETIMEDOUT ; +if (ret && !retries) +return ret; + +spin_lock_irqsave (&channel->lock, flags); + } -guard (spinlock_irqsave)(&channel->lock); +/* If another thread started a new initialization just return -EBUSY. */ +if (channel->otg_initializing) { + ret = -EBUSY ; + goto unlock; + }else { + ret =0 ; + } rphy->initialized = false;
@@ -588,7 +751,9 @@ static int rcar_gen3_phy_usb2_exit(struc val &= ~USB2_INT_ENABLE_UCOM_INTEN ;writel (val, usb2_base +USB2_INT_ENABLE ); -return 0 ; +unlock: +spin_unlock_irqrestore (&channel->lock, flags); +return ret; }static int rcar_gen3_phy_usb2_power_on (struct phy *p)
@@ -596,8 +761,10 @@ static int rcar_gen3_phy_usb2_power_on(sstruct rcar_gen3_phy *rphy =phy_get_drvdata (p);struct rcar_gen3_chan *channel = rphy->ch;void __iomem *usb2_base = channel->base; +int retries =NUM_OF_PHYS ; +unsigned long flags;u32 val; -int ret =0 ; +int ret;if (channel->vbus && !channel->otg_internal_reg) { ret =regulator_enable (channel->vbus);
@@ -605,7 +772,27 @@ static int rcar_gen3_phy_usb2_power_on(sreturn ret; } -guard (spinlock_irqsave)(&channel->lock); +spin_lock_irqsave (&channel->lock, flags); + +while (retries-- && channel->otg_initializing) { +spin_unlock_irqrestore (&channel->lock, flags); + + ret =wait_event_timeout (channel->otg_init_done, !channel->otg_initializing, +USB2_OTG_INIT_TIMEOUT ); + ret = ret ?0 : -ETIMEDOUT ; +if (ret && !retries) + goto disable_regulator; + +spin_lock_irqsave (&channel->lock, flags); + } + +/* If another thread started a new initialization just return -EBUSY. */ +if (channel->otg_initializing) { + ret = -EBUSY ; + goto unlock; + }else { + ret =0 ; + }if (!rcar_gen3_are_all_rphys_power_off (channel)) goto out;
@@ -620,27 +807,59 @@ out:/* The powered flag should be set for any other phys anyway */ rphy->powered = true; -return 0 ; +unlock: +spin_unlock_irqrestore (&channel->lock, flags); + +disable_regulator: +if (ret && channel->vbus && !channel->otg_internal_reg) +regulator_disable (channel->vbus); + +return ret; }static int rcar_gen3_phy_usb2_power_off (struct phy *p) {struct rcar_gen3_phy *rphy =phy_get_drvdata (p);struct rcar_gen3_chan *channel = rphy->ch; -int ret =0 ; +int retries =NUM_OF_PHYS ; +unsigned long flags; +int ret; - scoped_guard(spinlock_irqsave, &channel->lock) { - rphy->powered = false; +spin_lock_irqsave (&channel->lock, flags); -if (rcar_gen3_are_all_rphys_power_off (channel)) { -u32 val =readl (channel->base +USB2_USBCTR ); +while (retries-- && channel->otg_initializing) { +spin_unlock_irqrestore (&channel->lock, flags); - val |=USB2_USBCTR_PLL_RST ; -writel (val, channel->base +USB2_USBCTR ); - } + ret =wait_event_timeout (channel->otg_init_done, !channel->otg_initializing, +USB2_OTG_INIT_TIMEOUT ); + ret = ret ?0 : -ETIMEDOUT ; +if (ret && !retries) +return ret; + +spin_lock_irqsave (&channel->lock, flags); + } + +/* If another thread started a new initialization just return -EBUSY. */ +if (channel->otg_initializing) { + ret = -EBUSY ; + goto unlock; + }else { + ret =0 ; + } + + rphy->powered = false; + +if (rcar_gen3_are_all_rphys_power_off (channel)) { +u32 val =readl (channel->base +USB2_USBCTR ); + + val |=USB2_USBCTR_PLL_RST ; +writel (val, channel->base +USB2_USBCTR ); } -if (channel->vbus && !channel->otg_internal_reg) +unlock: +spin_unlock_irqrestore (&channel->lock, flags); + +if (!ret && channel->vbus && !channel->otg_internal_reg) ret =regulator_disable (channel->vbus);return ret;
@@ -1018,6 +1237,7 @@ static int rcar_gen3_phy_usb2_probe(strureturn ret;spin_lock_init (&channel->lock); +init_waitqueue_head (&channel->otg_init_done);for (i =0 ; i <NUM_OF_PHYS ; i++) { channel->rphys[i].phy =devm_phy_create (dev,NULL , channel->phy_data->phy_usb2_ops);