6.1.178-rc1 review
Subsystems (6)
Greg Kroah-Hartman
maintainerPATCH 944/1067 iio: make invensense timestamp module generic
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jean-Baptiste Maneyrol <[email protected]>
[ Upstream commit 0ecc363ccea71eda6a2cceade120489259bcdb33 ]
Rename common module to inv_sensors_timestamp, add configuration at init (chip internal clock, acceptable jitter, ...) and update inv_icm42600 driver integration.
Stable-dep-of: affe3f077d7a ("iio: imu: inv_icm42600: fix timestamping by limiting FIFO reading")
---
drivers/iio/common/inv_sensors/Makefile | 2
drivers/iio/common/inv_sensors/inv_icm42600_timestamp.c | 192 ----------------
drivers/iio/common/inv_sensors/inv_sensors_timestamp.c | 191 +++++++++++++++
drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c | 32 +-
drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c | 34 +-
drivers/iio/imu/inv_icm42600/inv_icm42600_core.c | 10
drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c | 32 +-
include/linux/iio/common/inv_icm42600_timestamp.h | 79 ------
include/linux/iio/common/inv_sensors_timestamp.h | 95 +++++++
9 files changed, 349 insertions(+), 318 deletions(-)
rename drivers/iio/common/inv_sensors/{inv_icm42600_timestamp.c => inv_sensors_timestamp.c} (61%)
delete mode 100644 include/linux/iio/common/inv_icm42600_timestamp.h
create mode 100644 include/linux/iio/common/inv_sensors_timestamp.hdrivers/iio/common/inv_sensors/Makefile +1 -1
@@ -3,4 +3,4 @@ # Makefile for TDK-InvenSense sensors module. # -obj-$(CONFIG_IIO_INV_SENSORS_TIMESTAMP) += inv_icm42600_timestamp.o +obj-$(CONFIG_IIO_INV_SENSORS_TIMESTAMP) += inv_sensors_timestamp.o
drivers/iio/common/inv_sensors/inv_icm42600_timestamp.c deleted +0 -192
192 lines not on this page · raw message
drivers/iio/common/inv_sensors/inv_sensors_timestamp.c new file +191 -0
191 lines not on this page · raw message
drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c +20 -12
@@ -12,7 +12,7 @@#include <linux/math64.h> #include <linux/iio/buffer.h> -#include <linux/iio/common/inv_icm42600_timestamp.h> +#include <linux/iio/common/inv_sensors_timestamp.h> #include <linux/iio/iio.h> #include <linux/iio/kfifo_buf.h>
@@ -99,7 +99,7 @@ static int inv_icm42600_accel_update_sca const unsigned long *scan_mask) {struct inv_icm42600_state *st =iio_device_get_drvdata (indio_dev); -struct inv_icm42600_timestamp *ts =iio_priv (indio_dev); +struct inv_sensors_timestamp *ts =iio_priv (indio_dev);struct inv_icm42600_sensor_conf conf =INV_ICM42600_SENSOR_CONF_INIT ;unsigned int fifo_en =0 ;unsigned int sleep_temp =0 ;
@@ -127,7 +127,7 @@ static int inv_icm42600_accel_update_sca }/* update data FIFO write */ -inv_icm42600_timestamp_apply_odr (ts,0 ,0 ,0 ); +inv_sensors_timestamp_apply_odr (ts,0 ,0 ,0 ); ret =inv_icm42600_buffer_set_fifo_en (st, fifo_en | st->fifo.en); out_unlock:
@@ -308,7 +308,7 @@ static int inv_icm42600_accel_write_odr( int val, int val2) {struct inv_icm42600_state *st =iio_device_get_drvdata (indio_dev); -struct inv_icm42600_timestamp *ts =iio_priv (indio_dev); +struct inv_sensors_timestamp *ts =iio_priv (indio_dev);struct device *dev =regmap_get_device (st->map);unsigned int idx;struct inv_icm42600_sensor_conf conf =INV_ICM42600_SENSOR_CONF_INIT ;
@@ -329,8 +329,8 @@ static int inv_icm42600_accel_write_odr(pm_runtime_get_sync (dev);mutex_lock (&st->lock); - ret =inv_icm42600_timestamp_update_odr (ts,inv_icm42600_odr_to_period (conf.odr), -iio_buffer_enabled (indio_dev)); + ret =inv_sensors_timestamp_update_odr (ts,inv_icm42600_odr_to_period (conf.odr), +iio_buffer_enabled (indio_dev));if (ret) goto out_unlock;
@@ -706,7 +706,8 @@ struct iio_dev *inv_icm42600_accel_init( {struct device *dev =regmap_get_device (st->map);const char *name; -struct inv_icm42600_timestamp *ts; +struct inv_sensors_timestamp_chip ts_chip; +struct inv_sensors_timestamp *ts;struct iio_dev *indio_dev;int ret;
@@ -718,8 +719,15 @@ struct iio_dev *inv_icm42600_accel_init(if (!indio_dev)return ERR_PTR (-ENOMEM ); +/* +* clock period is 32kHz (31250ns) +* jitter is +/- 2% (20 per mille) +*/ + ts_chip.clock_period =31250 ; + ts_chip.jitter =20 ; + ts_chip.init_period =inv_icm42600_odr_to_period (st->conf.accel.odr); ts =iio_priv (indio_dev); -inv_icm42600_timestamp_init (ts,inv_icm42600_odr_to_period (st->conf.accel.odr)); +inv_sensors_timestamp_init (ts, &ts_chip);iio_device_set_drvdata (indio_dev, st); indio_dev->name = name;
@@ -744,7 +752,7 @@ struct iio_dev *inv_icm42600_accel_init(int inv_icm42600_accel_parse_fifo (struct iio_dev *indio_dev) {struct inv_icm42600_state *st =iio_device_get_drvdata (indio_dev); -struct inv_icm42600_timestamp *ts =iio_priv (indio_dev); +struct inv_sensors_timestamp *ts =iio_priv (indio_dev);ssize_t i, size;unsigned int no;const void *accel, *gyro, *timestamp;
@@ -768,13 +776,13 @@ int inv_icm42600_accel_parse_fifo(struct/* update odr */ if (odr &INV_ICM42600_SENSOR_ACCEL ) -inv_icm42600_timestamp_apply_odr (ts, st->fifo.period, - st->fifo.nb.total, no); +inv_sensors_timestamp_apply_odr (ts, st->fifo.period, + st->fifo.nb.total, no);memcpy (&buffer.accel, accel,sizeof (buffer.accel));/* convert 8 bits FIFO temperature in high resolution format */ buffer.temp = temp ? (*temp *64 ) :0 ; - ts_val =inv_icm42600_timestamp_pop (ts); + ts_val =inv_sensors_timestamp_pop (ts);iio_push_to_buffers_with_timestamp (indio_dev, &buffer, ts_val); }
drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c +17 -17
@@ -11,7 +11,7 @@#include <linux/delay.h> #include <linux/iio/buffer.h> -#include <linux/iio/common/inv_icm42600_timestamp.h> +#include <linux/iio/common/inv_sensors_timestamp.h> #include <linux/iio/iio.h> #include "inv_icm42600.h"
@@ -276,12 +276,12 @@ static int inv_icm42600_buffer_preenable {struct inv_icm42600_state *st =iio_device_get_drvdata (indio_dev);struct device *dev =regmap_get_device (st->map); -struct inv_icm42600_timestamp *ts =iio_priv (indio_dev); +struct inv_sensors_timestamp *ts =iio_priv (indio_dev);pm_runtime_get_sync (dev);mutex_lock (&st->lock); -inv_icm42600_timestamp_reset (ts); +inv_sensors_timestamp_reset (ts);mutex_unlock (&st->lock);return 0 ;
@@ -378,7 +378,7 @@ out_unlock:static int inv_icm42600_buffer_postdisable (struct iio_dev *indio_dev) {struct inv_icm42600_state *st =iio_device_get_drvdata (indio_dev); -struct inv_icm42600_timestamp *ts =iio_priv (indio_dev); +struct inv_sensors_timestamp *ts =iio_priv (indio_dev);struct device *dev =regmap_get_device (st->map);unsigned int sensor;unsigned int *watermark;
@@ -400,7 +400,7 @@ static int inv_icm42600_buffer_postdisabmutex_lock (&st->lock); -inv_icm42600_timestamp_apply_odr (ts,0 ,0 ,0 ); +inv_sensors_timestamp_apply_odr (ts,0 ,0 ,0 ); ret =inv_icm42600_buffer_set_fifo_en (st, st->fifo.en & ~sensor);if (ret)
@@ -508,7 +508,7 @@ int inv_icm42600_buffer_fifo_read(structint inv_icm42600_buffer_fifo_parse (struct inv_icm42600_state *st) { -struct inv_icm42600_timestamp *ts; +struct inv_sensors_timestamp *ts;int ret;if (st->fifo.nb.total ==0 )
@@ -516,8 +516,8 @@ int inv_icm42600_buffer_fifo_parse(struc/* handle gyroscope timestamp and FIFO data parsing */ ts =iio_priv (st->indio_gyro); -inv_icm42600_timestamp_interrupt (ts, st->fifo.period, st->fifo.nb.total, - st->fifo.nb.gyro, st->timestamp.gyro); +inv_sensors_timestamp_interrupt (ts, st->fifo.period, st->fifo.nb.total, + st->fifo.nb.gyro, st->timestamp.gyro);if (st->fifo.nb.gyro >0 ) { ret =inv_icm42600_gyro_parse_fifo (st->indio_gyro);if (ret)
@@ -526,8 +526,8 @@ int inv_icm42600_buffer_fifo_parse(struc/* handle accelerometer timestamp and FIFO data parsing */ ts =iio_priv (st->indio_accel); -inv_icm42600_timestamp_interrupt (ts, st->fifo.period, st->fifo.nb.total, - st->fifo.nb.accel, st->timestamp.accel); +inv_sensors_timestamp_interrupt (ts, st->fifo.period, st->fifo.nb.total, + st->fifo.nb.accel, st->timestamp.accel);if (st->fifo.nb.accel >0 ) { ret =inv_icm42600_accel_parse_fifo (st->indio_accel);if (ret)
@@ -540,7 +540,7 @@ int inv_icm42600_buffer_fifo_parse(strucint inv_icm42600_buffer_hwfifo_flush (struct inv_icm42600_state *st,unsigned int count) { -struct inv_icm42600_timestamp *ts; +struct inv_sensors_timestamp *ts;int64_t gyro_ts, accel_ts;int ret;
@@ -556,9 +556,9 @@ int inv_icm42600_buffer_hwfifo_flush(strif (st->fifo.nb.gyro >0 ) { ts =iio_priv (st->indio_gyro); -inv_icm42600_timestamp_interrupt (ts, st->fifo.period, - st->fifo.nb.total, st->fifo.nb.gyro, - gyro_ts); +inv_sensors_timestamp_interrupt (ts, st->fifo.period, + st->fifo.nb.total, st->fifo.nb.gyro, + gyro_ts); ret =inv_icm42600_gyro_parse_fifo (st->indio_gyro);if (ret)return ret;
@@ -566,9 +566,9 @@ int inv_icm42600_buffer_hwfifo_flush(strif (st->fifo.nb.accel >0 ) { ts =iio_priv (st->indio_accel); -inv_icm42600_timestamp_interrupt (ts, st->fifo.period, - st->fifo.nb.total, st->fifo.nb.accel, - accel_ts); +inv_sensors_timestamp_interrupt (ts, st->fifo.period, + st->fifo.nb.total, st->fifo.nb.accel, + accel_ts); ret =inv_icm42600_accel_parse_fifo (st->indio_accel);if (ret)return ret;
drivers/iio/imu/inv_icm42600/inv_icm42600_core.c +5 -5
@@ -16,7 +16,7 @@#include <linux/property.h> #include <linux/regmap.h> -#include <linux/iio/common/inv_icm42600_timestamp.h> +#include <linux/iio/common/inv_sensors_timestamp.h> #include <linux/iio/iio.h> #include "inv_icm42600.h"
@@ -720,8 +720,8 @@ out_unlock:static int __maybe_unused inv_icm42600_resume (struct device *dev) {struct inv_icm42600_state *st =dev_get_drvdata (dev); -struct inv_icm42600_timestamp *gyro_ts =iio_priv (st->indio_gyro); -struct inv_icm42600_timestamp *accel_ts =iio_priv (st->indio_accel); +struct inv_sensors_timestamp *gyro_ts =iio_priv (st->indio_gyro); +struct inv_sensors_timestamp *accel_ts =iio_priv (st->indio_accel);int ret =0 ;mutex_lock (&st->lock);
@@ -742,8 +742,8 @@ static int __maybe_unused inv_icm42600_r/* restore FIFO data streaming */ if (st->fifo.on) { -inv_icm42600_timestamp_reset (gyro_ts); -inv_icm42600_timestamp_reset (accel_ts); +inv_sensors_timestamp_reset (gyro_ts); +inv_sensors_timestamp_reset (accel_ts); ret =regmap_write (st->map,INV_ICM42600_REG_FIFO_CONFIG ,INV_ICM42600_FIFO_CONFIG_STREAM ); }
drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c +20 -12
@@ -12,7 +12,7 @@#include <linux/math64.h> #include <linux/iio/buffer.h> -#include <linux/iio/common/inv_icm42600_timestamp.h> +#include <linux/iio/common/inv_sensors_timestamp.h> #include <linux/iio/iio.h> #include <linux/iio/kfifo_buf.h>
@@ -99,7 +99,7 @@ static int inv_icm42600_gyro_update_scan const unsigned long *scan_mask) {struct inv_icm42600_state *st =iio_device_get_drvdata (indio_dev); -struct inv_icm42600_timestamp *ts =iio_priv (indio_dev); +struct inv_sensors_timestamp *ts =iio_priv (indio_dev);struct inv_icm42600_sensor_conf conf =INV_ICM42600_SENSOR_CONF_INIT ;unsigned int fifo_en =0 ;unsigned int sleep_gyro =0 ;
@@ -127,7 +127,7 @@ static int inv_icm42600_gyro_update_scan }/* update data FIFO write */ -inv_icm42600_timestamp_apply_odr (ts,0 ,0 ,0 ); +inv_sensors_timestamp_apply_odr (ts,0 ,0 ,0 ); ret =inv_icm42600_buffer_set_fifo_en (st, fifo_en | st->fifo.en); out_unlock:
@@ -320,7 +320,7 @@ static int inv_icm42600_gyro_write_odr(s int val, int val2) {struct inv_icm42600_state *st =iio_device_get_drvdata (indio_dev); -struct inv_icm42600_timestamp *ts =iio_priv (indio_dev); +struct inv_sensors_timestamp *ts =iio_priv (indio_dev);struct device *dev =regmap_get_device (st->map);unsigned int idx;struct inv_icm42600_sensor_conf conf =INV_ICM42600_SENSOR_CONF_INIT ;
@@ -341,8 +341,8 @@ static int inv_icm42600_gyro_write_odr(spm_runtime_get_sync (dev);mutex_lock (&st->lock); - ret =inv_icm42600_timestamp_update_odr (ts,inv_icm42600_odr_to_period (conf.odr), -iio_buffer_enabled (indio_dev)); + ret =inv_sensors_timestamp_update_odr (ts,inv_icm42600_odr_to_period (conf.odr), +iio_buffer_enabled (indio_dev));if (ret) goto out_unlock;
@@ -717,7 +717,8 @@ struct iio_dev *inv_icm42600_gyro_init(s {struct device *dev =regmap_get_device (st->map);const char *name; -struct inv_icm42600_timestamp *ts; +struct inv_sensors_timestamp_chip ts_chip; +struct inv_sensors_timestamp *ts;struct iio_dev *indio_dev;int ret;
@@ -729,8 +730,15 @@ struct iio_dev *inv_icm42600_gyro_init(sif (!indio_dev)return ERR_PTR (-ENOMEM ); +/* +* clock period is 32kHz (31250ns) +* jitter is +/- 2% (20 per mille) +*/ + ts_chip.clock_period =31250 ; + ts_chip.jitter =20 ; + ts_chip.init_period =inv_icm42600_odr_to_period (st->conf.accel.odr); ts =iio_priv (indio_dev); -inv_icm42600_timestamp_init (ts,inv_icm42600_odr_to_period (st->conf.gyro.odr)); +inv_sensors_timestamp_init (ts, &ts_chip);iio_device_set_drvdata (indio_dev, st); indio_dev->name = name;
@@ -756,7 +764,7 @@ struct iio_dev *inv_icm42600_gyro_init(sint inv_icm42600_gyro_parse_fifo (struct iio_dev *indio_dev) {struct inv_icm42600_state *st =iio_device_get_drvdata (indio_dev); -struct inv_icm42600_timestamp *ts =iio_priv (indio_dev); +struct inv_sensors_timestamp *ts =iio_priv (indio_dev);ssize_t i, size;unsigned int no;const void *accel, *gyro, *timestamp;
@@ -780,13 +788,13 @@ int inv_icm42600_gyro_parse_fifo(struct/* update odr */ if (odr &INV_ICM42600_SENSOR_GYRO ) -inv_icm42600_timestamp_apply_odr (ts, st->fifo.period, - st->fifo.nb.total, no); +inv_sensors_timestamp_apply_odr (ts, st->fifo.period, + st->fifo.nb.total, no);memcpy (&buffer.gyro, gyro,sizeof (buffer.gyro));/* convert 8 bits FIFO temperature in high resolution format */ buffer.temp = temp ? (*temp *64 ) :0 ; - ts_val =inv_icm42600_timestamp_pop (ts); + ts_val =inv_sensors_timestamp_pop (ts);iio_push_to_buffers_with_timestamp (indio_dev, &buffer, ts_val); }
include/linux/iio/common/inv_icm42600_timestamp.h deleted +0 -79
79 lines not on this page · raw message
include/linux/iio/common/inv_sensors_timestamp.h new file +95 -0
95 lines not on this page · raw message
Greg Kroah-Hartman
maintainerPATCH 945/1067 iio: imu: inv_mpu6050: use the common inv_sensors timestamp module
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jean-Baptiste Maneyrol <[email protected]>
[ Upstream commit 111e1abd00455971f6a568900f033cbddec9d4e5 ]
Replace timestamping by the new common inv_sensors timestamp module. The principle behind is the same but the implementation in the new module is far better providing less jitter and a better estimation.
Stable-dep-of: affe3f077d7a ("iio: imu: inv_icm42600: fix timestamping by limiting FIFO reading")
--- drivers/iio/imu/inv_mpu6050/Kconfig | 1 drivers/iio/imu/inv_mpu6050/inv_mpu_core.c | 30 +++++++-- drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h | 18 ++--- drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c | 83 ++------------------------ drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c | 6 + 5 files changed, 45 insertions(+), 93 deletions(-)
drivers/iio/imu/inv_mpu6050/Kconfig +1 -0
@@ -7,6 +7,7 @@ config INV_MPU6050_IIO tristate select IIO_BUFFER select IIO_TRIGGERED_BUFFER + select IIO_INV_SENSORS_TIMESTAMP config INV_MPU6050_I2C tristate "Invensense MPU6050 devices (I2C)"
drivers/iio/imu/inv_mpu6050/inv_mpu_core.c +23 -7
@@ -12,12 +12,15 @@#include <linux/jiffies.h> #include <linux/irq.h> #include <linux/interrupt.h> -#include <linux/iio/iio.h> #include <linux/acpi.h> #include <linux/platform_device.h> #include <linux/regulator/consumer.h> #include <linux/pm.h> #include <linux/pm_runtime.h> + +#include <linux/iio/common/inv_sensors_timestamp.h> +#include <linux/iio/iio.h> +#include "inv_mpu_iio.h" #include "inv_mpu_magn.h"
@@ -512,6 +515,7 @@ static int inv_mpu6050_init_config(strucint result;u8 d;struct inv_mpu6050_state *st =iio_priv (indio_dev); +struct inv_sensors_timestamp_chip timestamp; result =inv_mpu6050_set_gyro_fsr (st, st->chip_config.fsr);if (result)
@@ -535,12 +539,12 @@ static int inv_mpu6050_init_config(strucif (result)return result; -/* -* Internal chip period is 1ms (1kHz). -* Let's use at the beginning the theorical value before measuring -* with interrupt timestamps. -*/ - st->chip_period =NSEC_PER_MSEC ; +/* clock jitter is +/- 2% */ + timestamp.clock_period =NSEC_PER_SEC /INV_MPU6050_INTERNAL_FREQ_HZ ; + timestamp.jitter =20 ; + timestamp.init_period = +NSEC_PER_SEC /INV_MPU6050_DIVIDER_TO_FIFO_RATE (st->chip_config.divider); +inv_sensors_timestamp_init (&st->timestamp, ×tamp);/* magn chip init, noop if not present in the chip */ result =inv_mpu_magn_probe (st);
@@ -927,6 +931,8 @@ inv_mpu6050_fifo_rate_store(struct devic const char *buf, size_t count) { int fifo_rate; +u32 fifo_period; +bool fifo_on;u8 d;int result;struct iio_dev *indio_dev =dev_to_iio_dev (dev);
@@ -943,12 +949,21 @@ inv_mpu6050_fifo_rate_store(struct devic d =INV_MPU6050_FIFO_RATE_TO_DIVIDER (fifo_rate);/* compute back the fifo rate to handle truncation cases */ fifo_rate =INV_MPU6050_DIVIDER_TO_FIFO_RATE (d); + fifo_period =NSEC_PER_SEC / fifo_rate;mutex_lock (&st->lock);if (d == st->chip_config.divider) { result =0 ; goto fifo_rate_fail_unlock; } + + fifo_on = st->chip_config.accl_fifo_enable || + st->chip_config.gyro_fifo_enable || + st->chip_config.magn_fifo_enable; + result =inv_sensors_timestamp_update_odr (&st->timestamp, fifo_period, fifo_on); +if (result) + goto fifo_rate_fail_unlock; + result =pm_runtime_resume_and_get (pdev);if (result) goto fifo_rate_fail_unlock;
@@ -1776,3 +1791,4 @@ EXPORT_SYMBOL_GPL(inv_mpu_pmops);MODULE_AUTHOR ("Invensense Corporation" );MODULE_DESCRIPTION ("Invensense device MPU6050 driver" );MODULE_LICENSE ("GPL" ); +MODULE_IMPORT_NS (IIO_INV_SENSORS_TIMESTAMP );
drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h +8 -10
@@ -9,15 +9,17 @@#include <linux/i2c.h> #include <linux/i2c-mux.h> #include <linux/mutex.h> -#include <linux/iio/iio.h> -#include <linux/iio/buffer.h> +#include <linux/platform_data/invensense_mpu6050.h> #include <linux/regmap.h> -#include <linux/iio/sysfs.h> + +#include <linux/iio/buffer.h> +#include <linux/iio/common/inv_sensors_timestamp.h> +#include <linux/iio/iio.h> #include <linux/iio/kfifo_buf.h> #include <linux/iio/trigger.h> #include <linux/iio/triggered_buffer.h> #include <linux/iio/trigger_consumer.h> -#include <linux/platform_data/invensense_mpu6050.h> +#include <linux/iio/sysfs.h> /** * struct inv_mpu6050_reg_map - Notable registers.
@@ -169,9 +171,7 @@ struct inv_mpu6050_hw {* @map regmap pointer. * @irq interrupt number. * @irq_mask the int_pin_cfg mask to configure interrupt type. -* @chip_period: chip internal period estimation (~1kHz). -* @it_timestamp: timestamp from previous interrupt. -* @data_timestamp: timestamp for next data sample. +* @timestamp: timestamping module * @vdd_supply: VDD voltage regulator for the chip. * @vddio_supply I/O voltage regulator for the chip. * @magn_disabled: magnetometer disabled for backward compatibility reason.
@@ -195,9 +195,7 @@ struct inv_mpu6050_state {int irq;u8 irq_mask;unsigned skip_samples; -s64 chip_period; -s64 it_timestamp; -s64 data_timestamp; +struct inv_sensors_timestamp timestamp;struct regulator *vdd_supply;struct regulator *vddio_supply;bool magn_disabled;
drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c +8 -75
109 lines not on this page · raw message
drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c +5 -1
@@ -4,6 +4,9 @@*/ #include <linux/pm_runtime.h> + +#include <linux/iio/common/inv_sensors_timestamp.h> +#include "inv_mpu_iio.h" static unsigned int inv_scan_query_mpu6050 (struct iio_dev *indio_dev)
@@ -106,7 +109,8 @@ int inv_mpu6050_prepare_fifo(struct inv_int ret;if (enable) { - st->it_timestamp =0 ; +/* reset timestamping */ +inv_sensors_timestamp_reset (&st->timestamp);/* reset FIFO */ d = st->chip_config.user_ctrl |INV_MPU6050_BIT_FIFO_RST ; ret =regmap_write (st->map, st->reg->user_ctrl, d);
Greg Kroah-Hartman
maintainerPATCH 946/1067 iio: invensense: remove redundant initialization of variable period
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Colin Ian King <[email protected]>
[ Upstream commit b58b13f156c00c2457035b7071eaaac105fe6836 ]
The variable period is being initialized with a value that is never read, it is being re-assigned a new value later on before it is read. The initialization is redundant and can be removed.
Cleans up clang scan build warning:
Value stored to 'period' during its initialization is never read [deadcode.DeadStores]
Stable-dep-of: affe3f077d7a ("iio: imu: inv_icm42600: fix timestamping by limiting FIFO reading")
--- drivers/iio/common/inv_sensors/inv_sensors_timestamp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/iio/common/inv_sensors/inv_sensors_timestamp.c +1 -1
@@ -105,7 +105,7 @@ void inv_sensors_timestamp_interrupt(strstruct inv_sensors_timestamp_interval *it;int64_t delta, interval;const uint32_t fifo_mult = fifo_period / ts->chip.clock_period; -uint32_t period = ts->period; +uint32_t period;int32_t m;bool valid = false;
Greg Kroah-Hartman
maintainerPATCH 947/1067 iio: invensense: fix timestamp glitches when switching frequency
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jean-Baptiste Maneyrol <[email protected]>
[ Upstream commit bf8367b00c33c64a9391c262bb2e11d274c9f2a4 ]
When a sensor is running and there is a FIFO frequency change due to another sensor turned on/off, there are glitches on timestamp. Fix that by using only interrupt timestamp when there is the corresponding sensor data in the FIFO.
Delete FIFO period handling and simplify internal functions.
Update integration inside inv_mpu6050 and inv_icm42600 drivers.
Stable-dep-of: affe3f077d7a ("iio: imu: inv_icm42600: fix timestamping by limiting FIFO reading")
--- drivers/iio/common/inv_sensors/inv_sensors_timestamp.c | 30 +++++++---------- drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c | 20 ++++------- drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c | 2 - include/linux/iio/common/inv_sensors_timestamp.h | 3 - 4 files changed, 23 insertions(+), 32 deletions(-)
drivers/iio/common/inv_sensors/inv_sensors_timestamp.c +13 -17
@@ -70,13 +70,13 @@ int inv_sensors_timestamp_update_odr(str }EXPORT_SYMBOL_NS_GPL (inv_sensors_timestamp_update_odr,IIO_INV_SENSORS_TIMESTAMP ); -static bool inv_validate_period (struct inv_sensors_timestamp *ts,uint32_t period,uint32_t mult) +static bool inv_validate_period (struct inv_sensors_timestamp *ts,uint32_t period) {uint32_t period_min, period_max;/* check that period is acceptable */ - period_min = ts->min_period * mult; - period_max = ts->max_period * mult; + period_min = ts->min_period * ts->mult; + period_max = ts->max_period * ts->mult;if (period > period_min && period < period_max)return true;else
@@ -84,32 +84,30 @@ static bool inv_validate_period(struct i }static bool inv_compute_chip_period (struct inv_sensors_timestamp *ts, -uint32_t mult,uint32_t period) +uint32_t period) {uint32_t new_chip_period; -if (!inv_validate_period (ts, period, mult)) +if (!inv_validate_period (ts, period))return false;/* update chip internal period estimation */ - new_chip_period = period / mult; + new_chip_period = period / ts->mult;inv_update_acc (&ts->chip_period, new_chip_period);return true; }void inv_sensors_timestamp_interrupt (struct inv_sensors_timestamp *ts, -uint32_t fifo_period,size_t fifo_nb, -size_t sensor_nb,int64_t timestamp) +size_t sample_nb,int64_t timestamp) {struct inv_sensors_timestamp_interval *it;int64_t delta, interval; -const uint32_t fifo_mult = fifo_period / ts->chip.clock_period;uint32_t period;int32_t m;bool valid = false; -if (fifo_nb ==0 ) +if (sample_nb ==0 )return ;/* update interrupt timestamp and compute chip and sensor periods */
@@ -119,8 +117,8 @@ void inv_sensors_timestamp_interrupt(str delta = it->up - it->lo;if (it->lo !=0 ) {/* compute period: delta time divided by number of samples */ - period =div_s64 (delta, fifo_nb); - valid =inv_compute_chip_period (ts, fifo_mult, period); + period =div_s64 (delta, sample_nb); + valid =inv_compute_chip_period (ts, period);/* update sensor period if chip internal period is updated */ if (valid) ts->period = ts->mult * ts->chip_period.val;
@@ -129,20 +127,18 @@ void inv_sensors_timestamp_interrupt(str/* no previous data, compute theoritical value from interrupt */ if (ts->timestamp ==0 ) {/* elapsed time: sensor period * sensor samples number */ - interval = (int64_t )ts->period * (int64_t )sensor_nb; + interval = (int64_t )ts->period * (int64_t )sample_nb; ts->timestamp = it->up - interval;return ; }/* if interrupt interval is valid, sync with interrupt timestamp */ if (valid) { -/* compute measured fifo_period */ - fifo_period = fifo_mult * ts->chip_period.val;/* delta time between last sample and last interrupt */ delta = it->lo - ts->timestamp;/* if there are multiple samples, go back to first one */ -while (delta >= (fifo_period *3 /2 )) - delta -= fifo_period; +while (delta >= (ts->period *3 /2 )) + delta -= ts->period;/* compute maximal adjustment value */ m =INV_SENSORS_TIMESTAMP_JITTER ((int64_t )ts->period, ts->chip.jitter);if (delta > m)
drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c +8 -12
@@ -515,20 +515,20 @@ int inv_icm42600_buffer_fifo_parse(strucreturn 0 ;/* handle gyroscope timestamp and FIFO data parsing */ - ts =iio_priv (st->indio_gyro); -inv_sensors_timestamp_interrupt (ts, st->fifo.period, st->fifo.nb.total, - st->fifo.nb.gyro, st->timestamp.gyro);if (st->fifo.nb.gyro >0 ) { + ts =iio_priv (st->indio_gyro); +inv_sensors_timestamp_interrupt (ts, st->fifo.nb.gyro, + st->timestamp.gyro); ret =inv_icm42600_gyro_parse_fifo (st->indio_gyro);if (ret)return ret; }/* handle accelerometer timestamp and FIFO data parsing */ - ts =iio_priv (st->indio_accel); -inv_sensors_timestamp_interrupt (ts, st->fifo.period, st->fifo.nb.total, - st->fifo.nb.accel, st->timestamp.accel);if (st->fifo.nb.accel >0 ) { + ts =iio_priv (st->indio_accel); +inv_sensors_timestamp_interrupt (ts, st->fifo.nb.accel, + st->timestamp.accel); ret =inv_icm42600_accel_parse_fifo (st->indio_accel);if (ret)return ret;
@@ -556,9 +556,7 @@ int inv_icm42600_buffer_hwfifo_flush(strif (st->fifo.nb.gyro >0 ) { ts =iio_priv (st->indio_gyro); -inv_sensors_timestamp_interrupt (ts, st->fifo.period, - st->fifo.nb.total, st->fifo.nb.gyro, - gyro_ts); +inv_sensors_timestamp_interrupt (ts, st->fifo.nb.gyro, gyro_ts); ret =inv_icm42600_gyro_parse_fifo (st->indio_gyro);if (ret)return ret;
@@ -566,9 +564,7 @@ int inv_icm42600_buffer_hwfifo_flush(strif (st->fifo.nb.accel >0 ) { ts =iio_priv (st->indio_accel); -inv_sensors_timestamp_interrupt (ts, st->fifo.period, - st->fifo.nb.total, st->fifo.nb.accel, - accel_ts); +inv_sensors_timestamp_interrupt (ts, st->fifo.nb.accel, accel_ts); ret =inv_icm42600_accel_parse_fifo (st->indio_accel);if (ret)return ret;
drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c +1 -1
@@ -109,7 +109,7 @@ irqreturn_t inv_mpu6050_read_fifo(int ir nb = fifo_count / bytes_per_datum;/* Each FIFO data contains all sensors, so same number for FIFO and sensor data */ fifo_period =NSEC_PER_SEC /INV_MPU6050_DIVIDER_TO_FIFO_RATE (st->chip_config.divider); -inv_sensors_timestamp_interrupt (&st->timestamp, fifo_period, nb, nb, pf->timestamp); +inv_sensors_timestamp_interrupt (&st->timestamp, nb, pf->timestamp);inv_sensors_timestamp_apply_odr (&st->timestamp, fifo_period, nb,0 );for (i =0 ; i < nb; ++i) { result = regmap_noinc_read(st->map, st->reg->fifo_r_w,
include/linux/iio/common/inv_sensors_timestamp.h +1 -2
@@ -71,8 +71,7 @@ int inv_sensors_timestamp_update_odr(str uint32_t period, bool fifo);void inv_sensors_timestamp_interrupt (struct inv_sensors_timestamp *ts, -uint32_t fifo_period,size_t fifo_nb, -size_t sensor_nb,int64_t timestamp); +size_t sample_nb,int64_t timestamp);static inline int64_t inv_sensors_timestamp_pop (struct inv_sensors_timestamp *ts) {
Greg Kroah-Hartman
maintainerPATCH 948/1067 iio: imu: inv_icm42600: stabilized timestamp in interrupt
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jean-Baptiste Maneyrol <[email protected]>
[ Upstream commit d7bd473632d07f8a54655c270c0940cc3671c548 ]
Use IRQF_ONESHOT flag to ensure the timestamp is not updated in the hard handler during the thread handler. And compute and use the effective watermark value that correspond to this first timestamp.
This way we can ensure the timestamp is always corresponding to the value used by the timestamping mechanism. Otherwise, it is possible that between FIFO count read and FIFO processing the timestamp is overwritten in the hard handler.
Stable-dep-of: affe3f077d7a ("iio: imu: inv_icm42600: fix timestamping by limiting FIFO reading")
--- drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c | 19 +++++++++++++++++-- drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.h | 2 ++ drivers/iio/imu/inv_icm42600/inv_icm42600_core.c | 1 + 3 files changed, 20 insertions(+), 2 deletions(-)
drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c +17 -2
@@ -222,10 +222,15 @@ int inv_icm42600_buffer_update_watermark latency_accel = period_accel * wm_accel;/* 0 value for watermark means that the sensor is turned off */ +if (wm_gyro ==0 && wm_accel ==0 ) +return 0 ; +if (latency_gyro ==0 ) { watermark = wm_accel; + st->fifo.watermark.eff_accel = wm_accel; }else if (latency_accel ==0 ) { watermark = wm_gyro; + st->fifo.watermark.eff_gyro = wm_gyro; }else {/* compute the smallest latency that is a multiple of both */ if (latency_gyro <= latency_accel)
@@ -241,6 +246,13 @@ int inv_icm42600_buffer_update_watermark watermark = latency / period;if (watermark <1 ) watermark =1 ; +/* update effective watermark */ + st->fifo.watermark.eff_gyro = latency / period_gyro; +if (st->fifo.watermark.eff_gyro <1 ) + st->fifo.watermark.eff_gyro =1 ; + st->fifo.watermark.eff_accel = latency / period_accel; +if (st->fifo.watermark.eff_accel <1 ) + st->fifo.watermark.eff_accel =1 ; }/* compute watermark value in bytes */
@@ -517,7 +529,7 @@ int inv_icm42600_buffer_fifo_parse(struc/* handle gyroscope timestamp and FIFO data parsing */ if (st->fifo.nb.gyro >0 ) { ts =iio_priv (st->indio_gyro); -inv_sensors_timestamp_interrupt (ts, st->fifo.nb.gyro, +inv_sensors_timestamp_interrupt (ts, st->fifo.watermark.eff_gyro, st->timestamp.gyro); ret =inv_icm42600_gyro_parse_fifo (st->indio_gyro);if (ret)
@@ -527,7 +539,7 @@ int inv_icm42600_buffer_fifo_parse(struc/* handle accelerometer timestamp and FIFO data parsing */ if (st->fifo.nb.accel >0 ) { ts =iio_priv (st->indio_accel); -inv_sensors_timestamp_interrupt (ts, st->fifo.nb.accel, +inv_sensors_timestamp_interrupt (ts, st->fifo.watermark.eff_accel, st->timestamp.accel); ret =inv_icm42600_accel_parse_fifo (st->indio_accel);if (ret)
@@ -578,6 +590,9 @@ int inv_icm42600_buffer_init(struct inv_unsigned int val;int ret; + st->fifo.watermark.eff_gyro =1 ; + st->fifo.watermark.eff_accel =1 ; +/* * Default FIFO configuration (bits 7 to 5) * - use invalid value
drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.h +2 -0
@@ -32,6 +32,8 @@ struct inv_icm42600_fifo {struct {unsigned int gyro;unsigned int accel; +unsigned int eff_gyro; +unsigned int eff_accel; } watermark;size_t count;struct {
drivers/iio/imu/inv_icm42600/inv_icm42600_core.c +1 -0
@@ -518,6 +518,7 @@ static int inv_icm42600_irq_init(structif (ret)return ret; + irq_type |=IRQF_ONESHOT ;return devm_request_threaded_irq (dev, irq, inv_icm42600_irq_timestamp, inv_icm42600_irq_handler, irq_type,"inv_icm42600" , st);
Greg Kroah-Hartman
maintainerPATCH 949/1067 iio: imu: inv_icm42600: fix timestamping by limiting FIFO reading
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jean-Baptiste Maneyrol <[email protected]>
[ Upstream commit affe3f077d7a4eeb25937f5323ff059a54b4712c ]
Timestamps are made by measuring the chip clock using the watermark interrupts. If we read more than watermark samples as done today, we are reducing the period between interrupts and distort the time measurement. Fix that by reading only watermark samples in the interrupt case.
--- drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c | 9 +++++---- drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.h | 1 + 2 files changed, 6 insertions(+), 4 deletions(-)
drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c +5 -4
@@ -257,6 +257,7 @@ int inv_icm42600_buffer_update_watermark/* compute watermark value in bytes */ wm_size = watermark * packet_size; + st->fifo.watermark.value = watermark;/* changing FIFO watermark requires to turn off watermark interrupt */ ret = regmap_update_bits_check(st->map, INV_ICM42600_REG_INT_SOURCE0,
@@ -476,11 +477,10 @@ int inv_icm42600_buffer_fifo_read(struct st->fifo.nb.accel =0 ; st->fifo.nb.total =0 ; -/* compute maximum FIFO read size */ +/* compute maximum FIFO read size (watermark for max = 0 interrupt case) */ if (max ==0 ) - max_count =sizeof (st->fifo.data); -else - max_count = max *inv_icm42600_get_packet_size (st->fifo.en); + max = st->fifo.watermark.value; + max_count = max *inv_icm42600_get_packet_size (st->fifo.en);/* read FIFO count value */ raw_fifo_count = (__be16 *)st->buffer;
@@ -592,6 +592,7 @@ int inv_icm42600_buffer_init(struct inv_ st->fifo.watermark.eff_gyro =1 ; st->fifo.watermark.eff_accel =1 ; + st->fifo.watermark.value =1 ;/* * Default FIFO configuration (bits 7 to 5)
drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.h +1 -0
@@ -34,6 +34,7 @@ struct inv_icm42600_fifo {unsigned int accel;unsigned int eff_gyro;unsigned int eff_accel; +unsigned int value; } watermark;size_t count;struct {
Greg Kroah-Hartman
maintainerPATCH 950/1067 ALSA: aoa: check snd_ctl_new1() return value
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zhao Dongdong <[email protected]>
[ Upstream commit 8df560fefe6fed6a20b7e06720eeaeccec349ac0 ]
snd_ctl_new1() can return NULL when memory allocation fails. In layout.c, the function does not check the return value before dereferencing ctl->id.name or passing to aoa_snd_ctl_add(), which can lead to a NULL pointer dereference.
Add NULL checks after snd_ctl_new1() calls and return early if any fails.
--- sound/aoa/fabrics/layout.c | 6 ++++++ 1 file changed, 6 insertions(+)
sound/aoa/fabrics/layout.c +6 -0
@@ -947,6 +947,8 @@ static void layout_attached_codec(structif (lineout ==1 ) ldev->gpio.methods->set_lineout (codec->gpio,1 ); ctl =snd_ctl_new1 (&lineout_ctl, codec->gpio); +if (!ctl) +return ;if (cc->connected &CC_LINEOUT_LABELLED_HEADPHONE )strscpy (ctl->id.name,"Headphone Switch" ,sizeof (ctl->id.name));
@@ -961,6 +963,8 @@ static void layout_attached_codec(structif (ldev->have_lineout_detect) { ctl =snd_ctl_new1 (&lineout_detect_choice, ldev); +if (!ctl) +return ;if (cc->connected &CC_LINEOUT_LABELLED_HEADPHONE ) strscpy(ctl->id.name, "Headphone Detect Autoswitch",
@@ -968,6 +972,8 @@ static void layout_attached_codec(structaoa_snd_ctl_add (ctl); ctl =snd_ctl_new1 (&lineout_detected, ldev); +if (!ctl) +return ;if (cc->connected &CC_LINEOUT_LABELLED_HEADPHONE ) strscpy(ctl->id.name, "Headphone Detected",
Greg Kroah-Hartman
maintainerPATCH 951/1067 bitops: make BYTES_TO_BITS() treewide-available
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alexander Lobakin <[email protected]>
[ Upstream commit 7d8296b250f2eed73f1758607926d4d258dea5d4 ]
Avoid open-coding that simple expression each time by moving BYTES_TO_BITS() from the probes code to <linux/bitops.h> to export it to the rest of the kernel.
Simplify the macro while at it. BITS_PER_LONG / sizeof(long) always equals to %BITS_PER_BYTE, regardless of the target architecture. Do the same for the tools ecosystem as well (incl. its version of bitops.h). The previous implementation had its implicit type of long, while the new one is int, so adjust the format literal accordingly in the perf code.
Stable-dep-of: 55052184ac90 ("iio: common: st_sensors: honour channel endianness in read_axis_data")
--- include/linux/bitops.h | 2 ++ kernel/trace/trace_probe.c | 2 -- tools/include/linux/bitops.h | 2 ++ tools/perf/util/probe-finder.c | 4 +--- 4 files changed, 5 insertions(+), 5 deletions(-)
include/linux/bitops.h +2 -0
@@ -21,6 +21,8 @@#define BITS_TO_U32 (nr) __KERNEL_DIV_ROUND_UP(nr, BITS_PER_TYPE(u32))#define BITS_TO_BYTES (nr) __KERNEL_DIV_ROUND_UP(nr, BITS_PER_TYPE(char)) +#define BYTES_TO_BITS (nb) ((nb) * BITS_PER_BYTE) +extern unsigned int __sw_hweight8 (unsigned int w);extern unsigned int __sw_hweight16 (unsigned int w);extern unsigned int __sw_hweight32 (unsigned int w);
kernel/trace/trace_probe.c +0 -2
@@ -535,8 +535,6 @@ parse_probe_arg(char *arg, const structreturn ret; } -#define BYTES_TO_BITS (nb) ((BITS_PER_LONG * (nb)) / sizeof(long)) -/* Bitfield type needs to be parsed into a fetch function */ static int __parse_bitfield_probe_arg(const char *bf, const struct fetch_type *t,
tools/include/linux/bitops.h +2 -0
@@ -20,6 +20,8 @@#define BITS_TO_U32 (nr) DIV_ROUND_UP(nr, BITS_PER_TYPE(u32))#define BITS_TO_BYTES (nr) DIV_ROUND_UP(nr, BITS_PER_TYPE(char)) +#define BYTES_TO_BITS (nb) ((nb) * BITS_PER_BYTE) +extern unsigned int __sw_hweight8 (unsigned int w);extern unsigned int __sw_hweight16 (unsigned int w);extern unsigned int __sw_hweight32 (unsigned int w);
tools/perf/util/probe-finder.c +1 -3
@@ -304,8 +304,6 @@ static_var:return ret2; } -#define BYTES_TO_BITS (nb) ((nb) * BITS_PER_LONG / sizeof(long)) -static int convert_variable_type (Dwarf_Die *vr_die,struct probe_trace_arg *tvar,const char *cast,bool user_access)
@@ -335,7 +333,7 @@ static int convert_variable_type(Dwarf_D total =dwarf_bytesize (vr_die);if (boffs <0 || total <0 )return -ENOENT ; - ret =snprintf (buf,16 ,"b%d@%d/%zd" , bsize, boffs, + ret =snprintf (buf,16 ,"b%d@%d/%d" , bsize, boffs,BYTES_TO_BITS (total)); goto formatted; }
Greg Kroah-Hartman
maintainerPATCH 952/1067 iio: common: st_sensors: honour channel endianness in read_axis_data
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Herman van Hazendonk <[email protected]>
[ Upstream commit 55052184ac9011db2ea983e54d6c21f0b1079a12 ]
st_sensors_read_axis_data() unconditionally decoded multi-byte results with get_unaligned_le16() / get_unaligned_le24() regardless of the channel's declared scan_type.endianness.
For every ST sensor that has used this helper since it was introduced this happened to be fine because the ST IMU/accel/gyro/pressure families publish their data registers as little-endian and the channel specs in those drivers declare IIO_LE accordingly.
The LSM303DLH magnetometer however publishes its X/Y/Z output as a pair of big-endian bytes (the H register sits at the lower address, 0x03/0x05/0x07, and the L register immediately after), and its channel specs in st_magn_core.c correctly declare IIO_BE -- but read_axis_data() ignored that and decoded as little-endian, swapping the high and low bytes of every magnetometer sample. The LSM303DLHC and LSM303DLM share the same st_magn_16bit_channels (IIO_BE) and were therefore byte-swapped by the same bug; users of those parts will see different in_magn_*_raw values after this fix lands.
The bug is most visible on a stationary chip: in earth's field the true X reading is small and the high byte sits at 0x00, so swapping the bytes pins sysfs X at exactly the low byte's pattern (e.g. 0x00F0 = 240). Y and Z still appear "to vary" because their magnitudes are larger and the noise in the low byte produces big swings in the swapped high byte:
before (LSM303DLH flat, sysfs in_magn_*_raw):
X=240 (stuck), Y= 12032..23296, Z=-16128..-9728
after (direct i2c-dev big-endian decode, same chip same orientation):
X≈-4096, Y≈210, Z≈80 (sensible values reflecting earth's
ambient field at low gauss range)
Fix read_axis_data() to dispatch on ch->scan_type.endianness and call get_unaligned_be16() / get_unaligned_be24() when the channel declares IIO_BE. Existing IIO_LE consumers (st_accel, st_gyro, st_pressure, st_lsm6dsx and others) are unaffected because their channel specs already declare IIO_LE and the LE path is unchanged.
While restructuring the branches, replace the previously implicit silent-success-with-uninitialised-*data fall-through for byte_for_channel outside 1..3 with an explicit return -EINVAL. No in-tree ST sensor publishes such a channel, but the new behaviour is strictly safer than handing userspace garbage.
--- drivers/iio/common/st_sensors/st_sensors_core.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-)
drivers/iio/common/st_sensors/st_sensors_core.c +17 -6
@@ -524,6 +524,7 @@ static int st_sensors_read_axis_data(stru8 *outdata;struct st_sensor_data *sdata =iio_priv (indio_dev);unsigned int byte_for_channel; +u32 tmp; byte_for_channel =DIV_ROUND_UP (ch->scan_type.realbits + ch->scan_type.shift,8 );
@@ -536,12 +537,22 @@ static int st_sensors_read_axis_data(strif (err <0 ) goto st_sensors_free_memory; -if (byte_for_channel ==1 ) - *data = (s8)*outdata; -else if (byte_for_channel ==2 ) - *data = (s16 )get_unaligned_le16 (outdata); -else if (byte_for_channel ==3 ) - *data = (s32 )sign_extend32 (get_unaligned_le24 (outdata),23 ); +if (byte_for_channel ==1 ) { + tmp = *outdata; + }else if (byte_for_channel ==2 ) { +if (ch->scan_type.endianness ==IIO_BE ) + tmp =get_unaligned_be16 (outdata); +else + tmp =get_unaligned_le16 (outdata); + }else if (byte_for_channel ==3 ) { +if (ch->scan_type.endianness ==IIO_BE ) + tmp =get_unaligned_be24 (outdata); +else + tmp =get_unaligned_le24 (outdata); + }else { +return -EINVAL ; + } + *data =sign_extend32 (tmp,BYTES_TO_BITS (byte_for_channel) -1 ); st_sensors_free_memory:kfree (outdata);
Greg Kroah-Hartman
maintainerPATCH 953/1067 vfio/pci: Use bitfield for struct vfio_pci_core_device flags
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Reinette Chatre <[email protected]>
[ Upstream commit 9cd0f6d5cbb6fda09aa83beb8146c287a552017e ]
struct vfio_pci_core_device contains eleven boolean flags. Boolean flags clearly indicate their usage but space usage starts to be a concern when there are many.
An upcoming change adds another boolean flag to struct vfio_pci_core_device, thereby increasing the concern that the boolean flags are consuming unnecessary space.
Transition the boolean flags to use bitfields. On a system that uses one byte per boolean this reduces the space consumed by existing flags from 11 bytes to 2 bytes with room for a few more flags without increasing the structure's size.
Stable-dep-of: 40ef3edf151e ("vfio/pci: Use a private flag to prevent power state change with VFs")
--- include/linux/vfio_pci_core.h | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-)
include/linux/vfio_pci_core.h +11 -11
@@ -69,17 +69,17 @@ struct vfio_pci_core_device {u16 msix_size;u32 msix_offset;u32 rbar[7 ]; -bool pci_2_3; -bool virq_disabled; -bool reset_works; -bool extended_caps; -bool bardirty; -bool has_vga; -bool needs_reset; -bool nointx; -bool needs_pm_restore; -bool pm_intx_masked; -bool pm_runtime_engaged; + bool pci_2_3:1; + bool virq_disabled:1; + bool reset_works:1; + bool extended_caps:1; + bool bardirty:1; + bool has_vga:1; + bool needs_reset:1; + bool nointx:1; + bool needs_pm_restore:1; + bool pm_intx_masked:1; + bool pm_runtime_engaged:1;struct pci_saved_state *pci_saved_state;struct pci_saved_state *pm_save;int ioeventfds_nr;
Greg Kroah-Hartman
maintainerPATCH 954/1067 vfio/pci: Use a private flag to prevent power state change with VFs
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Raghavendra Rao Ananta <[email protected]>
[ Upstream commit 40ef3edf151e184d021917a5c4c771cc0870844a ]
The current implementation uses pci_num_vf() while holding the memory_lock to prevent changing the power state of a PF when VFs are enabled. This creates a lockdep circular dependency warning because memory_lock is held during device probing.
[ 286.997167] ======================================================
[ 287.003363] WARNING: possible circular locking dependency detected
[ 287.009562] 7.0.0-dbg-DEV #3 Tainted: G S
[ 287.015074] ------------------------------------------------------
[ 287.021270] vfio_pci_sriov_/18636 is trying to acquire lock:
[ 287.026942] ff45bea2294d4968 (&vdev->memory_lock){+.+.}-{4:4}, at:
vfio_pci_core_runtime_resume+0x1f/0xa0
[ 287.036530]
[ 287.036530] but task is already holding lock:
[ 287.042383] ff45bea3a96b8230 (&new_dev_set->lock){+.+.}-{4:4}, at:
vfio_group_fops_unl_ioctl+0x44d/0x7b0
[ 287.051879]
[ 287.051879] which lock already depends on the new lock.
[ 287.051879]
[ 287.060070]
[ 287.060070] the existing dependency chain (in reverse order) is:
[ 287.067568]
[ 287.067568] -> #2 (&new_dev_set->lock){+.+.}-{4:4}:
[ 287.073941] __mutex_lock+0x92/0xb80
[ 287.078058] vfio_assign_device_set+0x66/0x1b0
[ 287.083042] vfio_pci_core_register_device+0xd1/0x2a0
[ 287.088638] vfio_pci_probe+0xd2/0x100
[ 287.092933] local_pci_probe_callback+0x4d/0xa0
[ 287.098001] process_scheduled_works+0x2ca/0x680
[ 287.103158] worker_thread+0x1e8/0x2f0
[ 287.107452] kthread+0x10c/0x140
[ 287.111230] ret_from_fork+0x18e/0x360
[ 287.115519] ret_from_fork_asm+0x1a/0x30
[ 287.119983]
[ 287.119983] -> #1 ((work_completion)(&arg.work)){+.+.}-{0:0}:
[ 287.127219] __flush_work+0x345/0x490
[ 287.131429] pci_device_probe+0x2e3/0x490
[ 287.135979] really_probe+0x1f9/0x4e0
[ 287.140180] __driver_probe_device+0x77/0x100
[ 287.145079] driver_probe_device+0x1e/0x110
[ 287.149803] __device_attach_driver+0xe3/0x170
[ 287.154789] bus_for_each_drv+0x125/0x150
[ 287.159346] __device_attach+0xca/0x1a0
[ 287.163720] device_initial_probe+0x34/0x50
[ 287.168445] pci_bus_add_device+0x6e/0x90
[ 287.172995] pci_iov_add_virtfn+0x3c9/0x3e0
[ 287.177719] sriov_add_vfs+0x2c/0x60
[ 287.181838] sriov_enable+0x306/0x4a0
[ 287.186038] vfio_pci_core_sriov_configure+0x184/0x220
[ 287.191715] sriov_numvfs_store+0xd9/0x1c0
[ 287.196351] kernfs_fop_write_iter+0x13f/0x1d0
[ 287.201338] vfs_write+0x2be/0x3b0
[ 287.205286] ksys_write+0x73/0x100
[ 287.209233] do_syscall_64+0x14d/0x750
[ 287.213529] entry_SYSCALL_64_after_hwframe+0x77/0x7f
[ 287.219120]
[ 287.219120] -> #0 (&vdev->memory_lock){+.+.}-{4:4}:
[ 287.225491] __lock_acquire+0x14c6/0x2800
[ 287.230048] lock_acquire+0xd3/0x2f0
[ 287.234168] down_write+0x3a/0xc0
[ 287.238019] vfio_pci_core_runtime_resume+0x1f/0xa0
[ 287.243436] __rpm_callback+0x8c/0x310
[ 287.247730] rpm_resume+0x529/0x6f0
[ 287.251765] __pm_runtime_resume+0x68/0x90
[ 287.256402] vfio_pci_core_enable+0x44/0x310
[ 287.261216] vfio_pci_open_device+0x1c/0x80
[ 287.265947] vfio_df_open+0x10f/0x150
[ 287.270148] vfio_group_fops_unl_ioctl+0x4a4/0x7b0
[ 287.275476] __se_sys_ioctl+0x71/0xc0
[ 287.279679] do_syscall_64+0x14d/0x750
[ 287.283975] entry_SYSCALL_64_after_hwframe+0x77/0x7f
[ 287.289559]
[ 287.289559] other info that might help us debug this:
[ 287.289559]
[ 287.297582] Chain exists of:
[ 287.297582] &vdev->memory_lock --> (work_completion)(&arg.work)
--> &new_dev_set->lock
[ 287.297582]
[ 287.310023] Possible unsafe locking scenario:
[ 287.310023]
[ 287.315961] CPU0 CPU1
[ 287.320510] ---- ----
[ 287.325059] lock(&new_dev_set->lock);
[ 287.328917]
lock((work_completion)(&arg.work));
[ 287.336153] lock(&new_dev_set->lock);
[ 287.342523] lock(&vdev->memory_lock);
[ 287.346382]
[ 287.346382] *** DEADLOCK ***
[ 287.346382]
[ 287.352315] 2 locks held by vfio_pci_sriov_/18636:
[ 287.357125] #0: ff45bea208ed3e18 (&group->group_lock){+.+.}-{4:4},
at: vfio_group_fops_unl_ioctl+0x3e3/0x7b0
[ 287.367048] #1: ff45bea3a96b8230 (&new_dev_set->lock){+.+.}-{4:4},
at: vfio_group_fops_unl_ioctl+0x44d/0x7b0
[ 287.376976]
[ 287.376976] stack backtrace:
[ 287.381353] CPU: 191 UID: 0 PID: 18636 Comm: vfio_pci_sriov_
Tainted: G S 7.0.0-dbg-DEV #3 PREEMPTLAZY
[ 287.381355] Tainted: [S]=CPU_OUT_OF_SPEC
[ 287.381356] Call Trace:
[ 287.381357] <TASK>
[ 287.381358] dump_stack_lvl+0x54/0x70
[ 287.381361] print_circular_bug+0x2e1/0x300
[ 287.381363] check_noncircular+0xf9/0x120
[ 287.381364] ? __lock_acquire+0x5b4/0x2800
[ 287.381366] __lock_acquire+0x14c6/0x2800
[ 287.381368] ? pci_mmcfg_read+0x4f/0x220
[ 287.381370] ? pci_mmcfg_write+0x57/0x220
[ 287.381371] ? lock_acquire+0xd3/0x2f0
[ 287.381373] ? pci_mmcfg_write+0x57/0x220
[ 287.381374] ? lock_release+0xef/0x360
[ 287.381376] ? vfio_pci_core_runtime_resume+0x1f/0xa0
[ 287.381377] lock_acquire+0xd3/0x2f0
[ 287.381378] ? vfio_pci_core_runtime_resume+0x1f/0xa0
[ 287.381379] ? lock_is_held_type+0x76/0x100
[ 287.381382] down_write+0x3a/0xc0
[ 287.381382] ? vfio_pci_core_runtime_resume+0x1f/0xa0
[ 287.381383] vfio_pci_core_runtime_resume+0x1f/0xa0
[ 287.381384] ? __pfx_pci_pm_runtime_resume+0x10/0x10
[ 287.381385] __rpm_callback+0x8c/0x310
[ 287.381386] ? ktime_get_mono_fast_ns+0x3d/0xb0
[ 287.381389] ? __pfx_pci_pm_runtime_resume+0x10/0x10
[ 287.381390] rpm_resume+0x529/0x6f0
[ 287.381392] ? lock_is_held_type+0x76/0x100
[ 287.381394] __pm_runtime_resume+0x68/0x90
[ 287.381396] vfio_pci_core_enable+0x44/0x310
[ 287.381398] vfio_pci_open_device+0x1c/0x80
[ 287.381399] vfio_df_open+0x10f/0x150
[ 287.381401] vfio_group_fops_unl_ioctl+0x4a4/0x7b0
[ 287.381402] __se_sys_ioctl+0x71/0xc0
[ 287.381404] do_syscall_64+0x14d/0x750
[ 287.381405] ? entry_SYSCALL_64_after_hwframe+0x77/0x7f
[ 287.381406] ? trace_irq_disable+0x25/0xd0
[ 287.381409] entry_SYSCALL_64_after_hwframe+0x77/0x7fIntroduce a private flag 'sriov_active' in the vfio_pci_core_device struct. This allows the driver to track the SR-IOV power state requirement without relying on pci_num_vf() while holding the memory_lock. The lock is now only held to set the flag and ensure the device is in D0, after which pci_enable_sriov() can be called without the lock.
--- drivers/vfio/pci/vfio_pci_core.c | 17 ++++++++++++++--- include/linux/vfio_pci_core.h | 1 + 2 files changed, 15 insertions(+), 3 deletions(-)
drivers/vfio/pci/vfio_pci_core.c +14 -3
@@ -237,8 +237,11 @@ int vfio_pci_set_power_state(struct vfioint ret;/* Prevent changing power state for PFs with VFs enabled */ -if (pci_num_vf (pdev) && state >PCI_D0 ) -return -EBUSY ; +if (state >PCI_D0 ) { +lockdep_assert_held_write (&vdev->memory_lock); +if (vdev->sriov_active) +return -EBUSY ; + }if (vdev->needs_pm_restore) {if (pdev->current_state < PCI_D3hot && state >= PCI_D3hot) {
@@ -2271,8 +2274,9 @@ int vfio_pci_core_sriov_configure(structdown_write (&vdev->memory_lock);vfio_pci_set_power_state (vdev,PCI_D0 ); - ret =pci_enable_sriov (pdev, nr_virtfn); + vdev->sriov_active = true;up_write (&vdev->memory_lock); + ret =pci_enable_sriov (pdev, nr_virtfn);if (ret) {pm_runtime_put (&pdev->dev); goto out_del;
@@ -2286,6 +2290,13 @@ int vfio_pci_core_sriov_configure(struct } out_del: +/* +* Avoid taking the memory_lock intentionally. A race with a power +* state transition would at most result in an -EBUSY, leaving the +* device in PCI_D0. +*/ + vdev->sriov_active = false; +mutex_lock (&vfio_pci_sriov_pfs_mutex);list_del_init (&vdev->sriov_pfs_item); out_unlock:
include/linux/vfio_pci_core.h +1 -0
@@ -80,6 +80,7 @@ struct vfio_pci_core_device { bool needs_pm_restore:1; bool pm_intx_masked:1; bool pm_runtime_engaged:1; +bool sriov_active;struct pci_saved_state *pci_saved_state;struct pci_saved_state *pm_save;int ioeventfds_nr;
Greg Kroah-Hartman
maintainerPATCH 955/1067 vfio/pci: Latch disable_idle_d3 per device
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alex Williamson <[email protected]>
[ Upstream commit 4575e9aac5336d1365138c0284773bf8da4b1fa3 ]
When disable_idle_d3 was introduced in vfio-pci, it directly manipulated the device power state with pci_set_power_state(). There were no refcounts to maintain or balanced operations, we could unconditionally bring the device to D0 and conditionally move it to D3hot. Therefore the module parameter was made writable.
Later, in commit c61302aa48f7 ("vfio/pci: Move module parameters to vfio_pci.c"), as part of the vfio-pci-core split, the writable aspect of the module parameter was nullified. The parameter value could still be changed through sysfs, but the vfio-pci driver latched the values into vfio-pci-core globals at module init. Loading the vfio-pci module, or unloading and reloading, with non-default or different values could change the globals relative to existing devices bound to vfio-pci variant drivers.
Runtime PM was introduced in commit 7ab5e10eda02 ("vfio/pci: Move the unused device into low power state with runtime PM"), which marks the point where power states became refcounted. PM get and put operations need to be balanced, but the same module operations noted above can change the global variables relative to those devices already bound to vfio-pci variant drivers. This introduces a window where PM operations can now become unbalanced.
To resolve this with a narrow footprint for stable backports, the disable_idle_d3 flag is latched into the vfio_pci_core_device at the time of initialization, such that the device always operates with a consistent value.
NB. vfio_pci_dev_set_try_reset() now unconditionally raises the runtime PM usage count around bus reset to account for disable_idle_d3 becoming a per-device rather than global flag. When this flag is set, the additional get/put pair is harmless and allows continued use of the shared vfio_pci_dev_set_pm_runtime_get() helper.
--- drivers/vfio/pci/vfio_pci_core.c | 19 ++++++++++--------- include/linux/vfio_pci_core.h | 1 + 2 files changed, 11 insertions(+), 9 deletions(-)
drivers/vfio/pci/vfio_pci_core.c +10 -9
@@ -472,7 +472,7 @@ int vfio_pci_core_enable(struct vfio_pciu16 cmd;u8 msix_pos; -if (!disable_idle_d3) { +if (!vdev->disable_idle_d3) { ret =pm_runtime_resume_and_get (&pdev->dev);if (ret <0 )return ret;
@@ -547,7 +547,7 @@ out_free_state: out_disable_device:pci_disable_device (pdev); out_power: -if (!disable_idle_d3) +if (!vdev->disable_idle_d3)pm_runtime_put (&pdev->dev);return ret; }
@@ -674,7 +674,7 @@ out:vfio_pci_dev_set_try_reset (vdev->vdev.dev_set);/* Put the pm-runtime usage counter acquired during enable */ -if (!disable_idle_d3) +if (!vdev->disable_idle_d3)pm_runtime_put (&pdev->dev); }EXPORT_SYMBOL_GPL (vfio_pci_core_disable);
@@ -2091,6 +2091,8 @@ int vfio_pci_core_init_dev(struct vfio_dINIT_LIST_HEAD (&vdev->sriov_pfs_item);init_rwsem (&vdev->memory_lock); + vdev->disable_idle_d3 = disable_idle_d3; +return 0 ; }EXPORT_SYMBOL_GPL (vfio_pci_core_init_dev);
@@ -2183,7 +2185,7 @@ int vfio_pci_core_register_device(struct dev->driver->pm = &vfio_pci_core_pm_ops;pm_runtime_allow (dev); -if (!disable_idle_d3) +if (!vdev->disable_idle_d3)pm_runtime_put (dev); ret =vfio_register_group_dev (&vdev->vdev);
@@ -2192,7 +2194,7 @@ int vfio_pci_core_register_device(structreturn 0 ; out_power: -if (!disable_idle_d3) +if (!vdev->disable_idle_d3)pm_runtime_get_noresume (dev);pm_runtime_forbid (dev);
@@ -2212,7 +2214,7 @@ void vfio_pci_core_unregister_device(strvfio_pci_vf_uninit (vdev);vfio_pci_vga_uninit (vdev); -if (!disable_idle_d3) +if (!vdev->disable_idle_d3)pm_runtime_get_noresume (&vdev->pdev->dev);pm_runtime_forbid (&vdev->pdev->dev);
@@ -2527,7 +2529,7 @@ static void vfio_pci_dev_set_try_reset(s* state. Increment the usage count for all the devices in the dev_set * before reset and decrement the same after reset. */ -if (!disable_idle_d3 &&vfio_pci_dev_set_pm_runtime_get (dev_set)) +if (vfio_pci_dev_set_pm_runtime_get (dev_set))return ;if (!pci_reset_bus (pdev))
@@ -2537,8 +2539,7 @@ static void vfio_pci_dev_set_try_reset(sif (reset_done) cur->needs_reset = false; -if (!disable_idle_d3) -pm_runtime_put (&cur->pdev->dev); +pm_runtime_put (&cur->pdev->dev); } }
include/linux/vfio_pci_core.h +1 -0
@@ -80,6 +80,7 @@ struct vfio_pci_core_device { bool needs_pm_restore:1; bool pm_intx_masked:1; bool pm_runtime_engaged:1; + bool disable_idle_d3:1;bool sriov_active;struct pci_saved_state *pci_saved_state;struct pci_saved_state *pm_save;
Greg Kroah-Hartman
maintainerPATCH 956/1067 PCI: altera: Fix resource leaks on probe failure
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mahesh Vaidya <[email protected]>
[ Upstream commit 7a94138caeb27f3c49c1dbd93bf422098925bb28 ]
The chained IRQ handler is set during probe, but is only removed during the driver remove(). If pci_host_probe() fails, the handler and INTx IRQ domain remain set even though the devm-managed host bridge storage containing struct altera_pcie will be released, leaving the handler with a stale data pointer.
Interrupts are also enabled before pci_host_probe() is called. If probe fails after that point, the controller interrupt source should be disabled before the chained handler and INTx domain are removed.
So set the chained handler only after the INTx domain has been created. Disable controller interrupts during IRQ teardown, and tear the IRQ setup down if pci_host_probe() fails.
--- drivers/pci/controller/pcie-altera.c | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-)
drivers/pci/controller/pcie-altera.c +26 -2
@@ -681,8 +681,18 @@ static int altera_pcie_init_irq_domain(sreturn 0 ; } +static void altera_pcie_disable_irq (struct altera_pcie *pcie) +{ +if (pcie->pcie_data->version ==ALTERA_PCIE_V1 || + pcie->pcie_data->version ==ALTERA_PCIE_V2 ) { +/* Disable all P2A interrupts */ +cra_writel (pcie,0 ,P2A_INT_ENABLE ); + } +} +static void altera_pcie_irq_teardown (struct altera_pcie *pcie) { +altera_pcie_disable_irq (pcie);irq_set_chained_handler_and_data (pcie->irq,NULL ,NULL );irq_domain_remove (pcie->irq_domain); }
@@ -707,7 +717,6 @@ static int altera_pcie_parse_dt(struct aif (pcie->irq <0 )return pcie->irq; -irq_set_chained_handler_and_data (pcie->irq, altera_pcie_isr, pcie);return 0 ; }
@@ -792,6 +801,12 @@ static int altera_pcie_probe(struct platreturn ret; } +/* +* The chained handler uses pcie->irq_domain, so set it only after the +* INTx domain has been created. +*/ +irq_set_chained_handler_and_data (pcie->irq, altera_pcie_isr, pcie); +/* clear all interrupts */ cra_writel (pcie,P2A_INT_STS_ALL ,P2A_INT_STATUS );/* enable all interrupts */
@@ -802,7 +817,16 @@ static int altera_pcie_probe(struct plat bridge->busnr = pcie->root_bus_nr; bridge->ops = &altera_pcie_ops; -return pci_host_probe (bridge); + ret =pci_host_probe (bridge); +if (ret) + goto err_teardown_irq; + +return 0 ; + +err_teardown_irq: +altera_pcie_irq_teardown (pcie); + +return ret; }static int altera_pcie_remove (struct platform_device *pdev)
Greg Kroah-Hartman
maintainerPATCH 957/1067 vfio/mlx5: Fix racy bitfields and tighten struct layout
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alex Williamson <[email protected]>
[ Upstream commit f2365a63b02ddea32e7db78b742c2503ec7b81f1 ]
Bitfield operations are not atomic, they use a read-modify-write pattern, therefore we should be careful not to pack bitfields that can be concurrently updated into the same storage unit.
This split takes a binary approach: flags that are only modified pre/post open/close remain bitfields, flags modified from user action, including actions that reach across to another device (ex. reset) use dedicated storage units.
Note mlx5_vhca_page_tracker.status is relocated to fill the alignment hole this split exposes.
Bitfield justifications:
migrate_cap: written only in mlx5vf_cmd_set_migratable() at probe
chunk_mode: written only in mlx5vf_cmd_set_migratable() at probe
mig_state_cap: written only in mlx5vf_cmd_set_migratable() at probe
Dedicated storage units:
mdev_detach: written in the VF attach/detach event notifier
mlx5fv_vf_event() at runtime
log_active: written in mlx5vf_start_page_tracker()/
mlx5vf_stop_page_tracker() during runtime dirty tracking
deferred_reset: written in mlx5vf_state_mutex_unlock()/
mlx5vf_pci_aer_reset_done() during runtime reset handling
is_err: set by tracker error handling and dirty-log polling at runtime
object_changed: set by tracker event handling and cleared by dirty-log
polling at runtime
--- drivers/vfio/pci/mlx5/cmd.h | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-)
drivers/vfio/pci/mlx5/cmd.h +8 -5
@@ -82,23 +82,26 @@ struct mlx5_vhca_qp {struct mlx5_vhca_page_tracker {u32 id;u32 pdn; -u8 is_err:1 ; +/* Flags modified at runtime - dedicated storage unit */ +u8 is_err; +int status;struct mlx5_uars_page *uar;struct mlx5_vhca_cq cq;struct mlx5_vhca_qp *host_qp;struct mlx5_vhca_qp *fw_qp;struct mlx5_nb nb; -int status; };struct mlx5vf_pci_core_device {struct vfio_pci_core_device core_device;int vf_id;u16 vhca_id; +/* Flags only modified on setup/release - bitfield ok */ u8 migrate_cap:1 ; -u8 deferred_reset:1 ; -u8 mdev_detach:1 ; -u8 log_active:1 ; +/* Flags modified at runtime - dedicated storage unit */ +u8 mdev_detach; +u8 log_active; +u8 deferred_reset;struct completion tracker_comp;/* protect migration state */ struct mutex state_mutex;
Greg Kroah-Hartman
maintainerPATCH 958/1067 PCI: imx6: Fix IMX6SX_GPR12_PCIE_TEST_POWERDOWN handling
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Richard Zhu <[email protected]>
[ Upstream commit aad953fb4eed0df5486cd54ccad80ac197678e01 ]
The IMX6SX_GPR12_PCIE_TEST_POWERDOWN bit does not control the PCIe reference clock on i.MX6SX. Instead, it is part of i.MX6SX PCIe core reset sequence.
Move the IMX6SX_GPR12_PCIE_TEST_POWERDOWN assertion/deassertion into the core reset functions to properly reflect its purpose. Remove the .enable_ref_clk() callback for i.MX6SX since it was incorrectly manipulating this bit.
--- drivers/pci/controller/dwc/pci-imx6.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-)
drivers/pci/controller/dwc/pci-imx6.c +2 -3
@@ -539,9 +539,6 @@ static int imx6_pcie_enable_ref_clk(strudev_err (dev,"unable to enable pcie_axi clock\n" );break ; } - -regmap_update_bits (imx6_pcie->iomuxc_gpr,IOMUXC_GPR12 , -IMX6SX_GPR12_PCIE_TEST_POWERDOWN ,0 );break ;case IMX6QP :case IMX6Q :
@@ -740,6 +737,8 @@ static int imx6_pcie_deassert_core_resetimx7d_pcie_wait_for_phy_pll_lock (imx6_pcie);break ;case IMX6SX : +regmap_clear_bits (imx6_pcie->iomuxc_gpr,IOMUXC_GPR12 , +IMX6SX_GPR12_PCIE_TEST_POWERDOWN );regmap_update_bits (imx6_pcie->iomuxc_gpr,IOMUXC_GPR5 ,IMX6SX_GPR5_PCIE_BTNRST_RESET ,0 );break ;
Greg Kroah-Hartman
maintainerPATCH 959/1067 staging: rtl8723bs: Fix indentation issues
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Franziska Naepelt <[email protected]>
[ Upstream commit b59cba2309b16fa364df03e3693b8d45c3fadbd6 ]
Fix the following checkpatch indentation issues:
- WARNING: suspect code indent for conditional statements (32, 48)
- WARNING: suspect code indent for conditional statements (24, 24)
- ERROR: code indent should use tabs where possible
Stable-dep-of: 5a752a616e75 ("staging: rtl8723bs: fix heap buffer overflow in rtw_cfg80211_set_wpa_ie()")
--- drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c +3 -3
@@ -582,7 +582,7 @@ static int rtw_cfg80211_ap_set_encryptio psecuritypriv->dot118021XGrpPrivacy = _WEP40_;if (param->u.crypt.key_len ==13 ) - psecuritypriv->dot118021XGrpPrivacy = _WEP104_; + psecuritypriv->dot118021XGrpPrivacy = _WEP104_; }else if (strcmp (param->u.crypt.alg,"TKIP" ) ==0 ) { psecuritypriv->dot118021XGrpPrivacy = _TKIP_;
@@ -1306,7 +1306,7 @@ static int cfg80211_rtw_scan(struct wiph }else if (request->n_channels <=4 ) {for (j = request->n_channels -1 ; j >=0 ; j--)for (i =0 ; i < survey_times; i++) -memcpy (&ch[j*survey_times+i], &ch[j],sizeof (struct rtw_ieee80211_channel )); +memcpy (&ch[j*survey_times+i], &ch[j],sizeof (struct rtw_ieee80211_channel )); _status =rtw_sitesurvey_cmd (padapter, ssid,RTW_SSID_SCAN_AMOUNT , ch, survey_times * request->n_channels); }else { _status =rtw_sitesurvey_cmd (padapter, ssid,RTW_SSID_SCAN_AMOUNT ,NULL ,0 );
@@ -2811,7 +2811,7 @@ int rtw_wdev_alloc(struct adapter *padap wdev->netdev = pnetdev; wdev->iftype =NL80211_IFTYPE_STATION ;/* will be init in rtw_hal_init() */ -/* Must sync with _rtw_init_mlme_priv() */ +/* Must sync with _rtw_init_mlme_priv() */ /* pmlmepriv->fw_state = WIFI_STATION_STATE */ padapter->rtw_wdev = wdev; pnetdev->ieee80211_ptr = wdev;
Greg Kroah-Hartman
maintainerPATCH 960/1067 staging: rtl8723bs: Fix space issues
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Franziska Naepelt <[email protected]>
[ Upstream commit c4b811b9361b0d7ea167a79587f5ec0a13fd8863 ]
Fix the following checkpatch space issues:
- CHECK: spaces preferred around that '*' (ctx:VxV)
- CHECK: spaces preferred around that '+' (ctx:VxV)
- CHECK: spaces preferred around that '-' (ctx:VxV)
- CHECK: spaces preferred around that '|' (ctx:VxV)
- CHECK: No space is necessary after a cast
- WARNING: please, no spaces at the start of a line
Stable-dep-of: 5a752a616e75 ("staging: rtl8723bs: fix heap buffer overflow in rtw_cfg80211_set_wpa_ie()")
--- drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c | 76 +++++++++++----------- 1 file changed, 38 insertions(+), 38 deletions(-)
drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c +38 -38
@@ -95,14 +95,14 @@ static struct ieee80211_channel rtw_2ghzstatic void rtw_2g_channels_init (struct ieee80211_channel *channels) {memcpy ((void *)channels, (void *)rtw_2ghz_channels, -sizeof (struct ieee80211_channel )*RTW_2G_CHANNELS_NUM +sizeof (struct ieee80211_channel ) *RTW_2G_CHANNELS_NUM ); }static void rtw_2g_rates_init (struct ieee80211_rate *rates) {memcpy (rates, rtw_g_rates, -sizeof (struct ieee80211_rate )*RTW_G_RATES_NUM +sizeof (struct ieee80211_rate ) *RTW_G_RATES_NUM ); }
@@ -126,8 +126,8 @@ static struct ieee80211_supported_band *if (!spt_band) goto exit; - spt_band->channels = (struct ieee80211_channel *)(((u8 *)spt_band)+sizeof (struct ieee80211_supported_band )); - spt_band->bitrates = (struct ieee80211_rate *)(((u8 *)spt_band->channels)+sizeof (struct ieee80211_channel )*n_channels); + spt_band->channels = (struct ieee80211_channel *)(((u8 *)spt_band) +sizeof (struct ieee80211_supported_band )); + spt_band->bitrates = (struct ieee80211_rate *)(((u8 *)spt_band->channels) +sizeof (struct ieee80211_channel ) * n_channels); spt_band->band = band; spt_band->n_channels = n_channels; spt_band->n_bitrates = n_bitrates;
@@ -247,10 +247,10 @@ struct cfg80211_bss *rtw_cfg80211_informu32 wpsielen =0 ;u8 *wpsie =NULL ; - wpsie =rtw_get_wps_ie (pnetwork->network.ies+_FIXED_IE_LENGTH_, pnetwork->network.ie_length-_FIXED_IE_LENGTH_,NULL , &wpsielen); + wpsie =rtw_get_wps_ie (pnetwork->network.ies + _FIXED_IE_LENGTH_, pnetwork->network.ie_length - _FIXED_IE_LENGTH_,NULL , &wpsielen);if (wpsie && wpsielen >0 ) - psr =rtw_get_wps_attr_content (wpsie, wpsielen,WPS_ATTR_SELECTED_REGISTRAR , (u8 *)(&sr),NULL ); + psr =rtw_get_wps_attr_content (wpsie, wpsielen,WPS_ATTR_SELECTED_REGISTRAR , (u8 *)(&sr),NULL );if (sr !=0 ) {/* it means under processing WPS */
@@ -277,9 +277,9 @@ struct cfg80211_bss *rtw_cfg80211_inform/* We've set wiphy's signal_type as CFG80211_SIGNAL_TYPE_MBM: signal strength in mBm (100*dBm) */ if (check_fwstate (pmlmepriv, _FW_LINKED) == true &&is_same_network (&pmlmepriv->cur_network.network, &pnetwork->network,0 )) { - notify_signal =100 *translate_percentage_to_dbm (padapter->recvpriv.signal_strength);/* dbm */ + notify_signal =100 *translate_percentage_to_dbm (padapter->recvpriv.signal_strength);/* dbm */ }else { - notify_signal =100 *translate_percentage_to_dbm (pnetwork->network.phy_info.signal_strength);/* dbm */ + notify_signal =100 *translate_percentage_to_dbm (pnetwork->network.phy_info.signal_strength);/* dbm */ } buf =kzalloc (MAX_BSSINFO_LEN ,GFP_ATOMIC );
@@ -453,20 +453,20 @@ check_bss: roam_info.links[0 ].channel = notify_channel; roam_info.links[0 ].bssid = cur_network->network.mac_address; roam_info.req_ie = - pmlmepriv->assoc_req+sizeof (struct ieee80211_hdr_3addr )+2 ; + pmlmepriv->assoc_req +sizeof (struct ieee80211_hdr_3addr ) +2 ; roam_info.req_ie_len = - pmlmepriv->assoc_req_len-sizeof (struct ieee80211_hdr_3addr )-2 ; + pmlmepriv->assoc_req_len -sizeof (struct ieee80211_hdr_3addr ) -2 ; roam_info.resp_ie = - pmlmepriv->assoc_rsp+sizeof (struct ieee80211_hdr_3addr )+6 ; + pmlmepriv->assoc_rsp +sizeof (struct ieee80211_hdr_3addr ) +6 ; roam_info.resp_ie_len = - pmlmepriv->assoc_rsp_len-sizeof (struct ieee80211_hdr_3addr )-6 ; + pmlmepriv->assoc_rsp_len -sizeof (struct ieee80211_hdr_3addr ) -6 ;cfg80211_roamed (padapter->pnetdev, &roam_info,GFP_ATOMIC ); }else {cfg80211_connect_result (padapter->pnetdev, cur_network->network.mac_address - , pmlmepriv->assoc_req+sizeof (struct ieee80211_hdr_3addr )+2 - , pmlmepriv->assoc_req_len-sizeof (struct ieee80211_hdr_3addr )-2 - , pmlmepriv->assoc_rsp+sizeof (struct ieee80211_hdr_3addr )+6 - , pmlmepriv->assoc_rsp_len-sizeof (struct ieee80211_hdr_3addr )-6 + , pmlmepriv->assoc_req +sizeof (struct ieee80211_hdr_3addr ) +2 + , pmlmepriv->assoc_req_len -sizeof (struct ieee80211_hdr_3addr ) -2 + , pmlmepriv->assoc_rsp +sizeof (struct ieee80211_hdr_3addr ) +6 + , pmlmepriv->assoc_rsp_len -sizeof (struct ieee80211_hdr_3addr ) -6 ,WLAN_STATUS_SUCCESS ,GFP_ATOMIC ); } }
@@ -721,7 +721,7 @@ static int rtw_cfg80211_set_encryption(s param->u.crypt.err =0 ; param->u.crypt.alg[IEEE_CRYPT_ALG_NAME_LEN -1 ] ='\0' ; -if (param_len < (u32) ((u8 *) param->u.crypt.key - (u8 *) param) + param->u.crypt.key_len) { +if (param_len < (u32)((u8 *)param->u.crypt.key - (u8 *)param) + param->u.crypt.key_len) { ret = -EINVAL ; goto exit; }
@@ -1036,10 +1036,10 @@ static int cfg80211_rtw_get_station(stru }/* for Ad-Hoc/AP mode */ -if ((check_fwstate (pmlmepriv,WIFI_ADHOC_STATE ) - ||check_fwstate (pmlmepriv,WIFI_ADHOC_MASTER_STATE ) - ||check_fwstate (pmlmepriv,WIFI_AP_STATE )) - &&check_fwstate (pmlmepriv, _FW_LINKED)) { +if ((check_fwstate (pmlmepriv,WIFI_ADHOC_STATE ) || +check_fwstate (pmlmepriv,WIFI_ADHOC_MASTER_STATE ) || +check_fwstate (pmlmepriv,WIFI_AP_STATE )) && +check_fwstate (pmlmepriv, _FW_LINKED)) {/* TODO: should acquire station info... */ }
@@ -1238,7 +1238,7 @@ static int cfg80211_rtw_scan(struct wiphspin_unlock_bh (&pwdev_priv->scan_req_lock);if (check_fwstate (pmlmepriv,WIFI_AP_STATE ) == true) { -if (check_fwstate (pmlmepriv,WIFI_UNDER_WPS |_FW_UNDER_SURVEY|_FW_UNDER_LINKING) == true) { +if (check_fwstate (pmlmepriv,WIFI_UNDER_WPS | _FW_UNDER_SURVEY | _FW_UNDER_LINKING) == true) { need_indicate_scan_done = true; goto check_need_indicate_scan_done; }
@@ -1292,7 +1292,7 @@ static int cfg80211_rtw_scan(struct wiph }/* parsing channels, n_channels */ -memset (ch,0 ,sizeof (struct rtw_ieee80211_channel )*RTW_CHANNEL_SCAN_AMOUNT ); +memset (ch,0 ,sizeof (struct rtw_ieee80211_channel ) *RTW_CHANNEL_SCAN_AMOUNT );for (i =0 ; i < request->n_channels && i <RTW_CHANNEL_SCAN_AMOUNT ; i++) { ch[i].hw_value = request->channels[i]->hw_value; ch[i].flags = request->channels[i]->flags;
@@ -1306,7 +1306,7 @@ static int cfg80211_rtw_scan(struct wiph }else if (request->n_channels <=4 ) {for (j = request->n_channels -1 ; j >=0 ; j--)for (i =0 ; i < survey_times; i++) -memcpy (&ch[j*survey_times+i], &ch[j],sizeof (struct rtw_ieee80211_channel )); +memcpy (&ch[j * survey_times + i], &ch[j],sizeof (struct rtw_ieee80211_channel )); _status =rtw_sitesurvey_cmd (padapter, ssid,RTW_SSID_SCAN_AMOUNT , ch, survey_times * request->n_channels); }else { _status =rtw_sitesurvey_cmd (padapter, ssid,RTW_SSID_SCAN_AMOUNT ,NULL ,0 );
@@ -1462,7 +1462,7 @@ static int rtw_cfg80211_set_wpa_ie(struc goto exit; } -if (ielen >MAX_WPA_IE_LEN +MAX_WPS_IE_LEN +MAX_P2P_IE_LEN ) { +if (ielen >MAX_WPA_IE_LEN +MAX_WPS_IE_LEN +MAX_P2P_IE_LEN ) { ret = -EINVAL ; goto exit; }
@@ -1482,19 +1482,19 @@ static int rtw_cfg80211_set_wpa_ie(struc pwpa =rtw_get_wpa_ie (buf, &wpa_ielen, ielen);if (pwpa && wpa_ielen >0 ) { -if (rtw_parse_wpa_ie (pwpa, wpa_ielen+2 , &group_cipher, &pairwise_cipher,NULL ) == _SUCCESS) { +if (rtw_parse_wpa_ie (pwpa, wpa_ielen +2 , &group_cipher, &pairwise_cipher,NULL ) == _SUCCESS) { padapter->securitypriv.dot11AuthAlgrthm = dot11AuthAlgrthm_8021X; padapter->securitypriv.ndisauthtype = Ndis802_11AuthModeWPAPSK; -memcpy (padapter->securitypriv.supplicant_ie, &pwpa[0 ], wpa_ielen+2 ); +memcpy (padapter->securitypriv.supplicant_ie, &pwpa[0 ], wpa_ielen +2 ); } } pwpa2 =rtw_get_wpa2_ie (buf, &wpa2_ielen, ielen);if (pwpa2 && wpa2_ielen >0 ) { -if (rtw_parse_wpa2_ie (pwpa2, wpa2_ielen+2 , &group_cipher, &pairwise_cipher,NULL ) == _SUCCESS) { +if (rtw_parse_wpa2_ie (pwpa2, wpa2_ielen +2 , &group_cipher, &pairwise_cipher,NULL ) == _SUCCESS) { padapter->securitypriv.dot11AuthAlgrthm = dot11AuthAlgrthm_8021X; padapter->securitypriv.ndisauthtype = Ndis802_11AuthModeWPA2PSK; -memcpy (padapter->securitypriv.supplicant_ie, &pwpa2[0 ], wpa2_ielen+2 ); +memcpy (padapter->securitypriv.supplicant_ie, &pwpa2[0 ], wpa2_ielen +2 ); } }
@@ -1893,7 +1893,7 @@ static int cfg80211_rtw_set_pmksa(structmemcpy (psecuritypriv->PMKIDList[index].PMKID, (u8 *)pmksa->pmkid,WLAN_PMKID_LEN ); psecuritypriv->PMKIDList[index].bUsed = true; - psecuritypriv->PMKIDIndex = index+1 ; + psecuritypriv->PMKIDIndex = index +1 ; blInserted = true;break ; }
@@ -2095,8 +2095,8 @@ static netdev_tx_t rtw_cfg80211_monitor__rtw_xmit_entry (skb, padapter->pnetdev);return NETDEV_TX_OK ; - }else if ((frame_control & (IEEE80211_FCTL_FTYPE |IEEE80211_FCTL_STYPE )) == - (IEEE80211_FTYPE_MGMT |IEEE80211_STYPE_ACTION )) { + }else if ((frame_control & (IEEE80211_FCTL_FTYPE |IEEE80211_FCTL_STYPE )) == + (IEEE80211_FTYPE_MGMT |IEEE80211_STYPE_ACTION )) {/* only for action frames */ struct xmit_frame *pmgntframe;struct pkt_attrib *pattrib;
@@ -2210,7 +2210,7 @@ static int rtw_cfg80211_add_monitor_if(s goto out; *ndev = pwdev_priv->pmon_ndev = mon_ndev; -memcpy (pwdev_priv->ifname_mon, name,IFNAMSIZ +1 ); +memcpy (pwdev_priv->ifname_mon, name,IFNAMSIZ +1 ); out:if (ret && mon_wdev) {
@@ -2304,14 +2304,14 @@ static int rtw_add_beacon(struct adapterif (head_len <24 )return -EINVAL ; - pbuf =rtw_zmalloc (head_len+tail_len); + pbuf =rtw_zmalloc (head_len + tail_len);if (!pbuf)return -ENOMEM ; -memcpy (pbuf, (void *)head+24 , head_len-24 );/* 24 =beacon header len. */ -memcpy (pbuf+head_len-24 , (void *)tail, tail_len); +memcpy (pbuf, (void *)head +24 , head_len -24 );/* 24 =beacon header len. */ +memcpy (pbuf + head_len -24 , (void *)tail, tail_len); - len = head_len+tail_len-24 ; + len = head_len + tail_len -24 ;/* check wps ie if inclued */ rtw_get_wps_ie (pbuf + _FIXED_IE_LENGTH_, len - _FIXED_IE_LENGTH_,NULL , &wps_ielen);
@@ -2601,7 +2601,7 @@ static int cfg80211_rtw_mgmt_tx(struct w pwdev_priv =adapter_wdev_data (padapter);/* cookie generation */ - *cookie = (unsigned long ) buf; + *cookie = (unsigned long )buf;/* indicate ack before issue frame to avoid racing with rsp frame */ rtw_cfg80211_mgmt_tx_status (padapter, *cookie, buf, len, ack,GFP_KERNEL );
Greg Kroah-Hartman
maintainerPATCH 961/1067 staging: rtl8723bs: fix heap buffer overflow in rtw_cfg80211_set_wpa_ie()
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alexandru Hossu <[email protected]>
[ Upstream commit 5a752a616e756844388a1a45404db9fc29fec655 ]
supplicant_ie is a 256-byte array in struct security_priv. The WPA and WPA2 IE copy paths use:
memcpy(padapter->securitypriv.supplicant_ie, &pwpa[0], wpa_ielen + 2);
where wpa_ielen is the raw IE length field (u8, 0-255). When a local user supplies a connect request via nl80211 with a crafted WPA IE of length 255, wpa_ielen + 2 equals 257, overflowing the 256-byte buffer by one byte into the adjacent last_mic_err_time field.
rtw_parse_wpa_ie() does not prevent this: its length consistency check compares *(wpa_ie+1) against (u8)(wpa_ie_len-2), which is (u8)(255) == 255 when wpa_ie_len = 257, so the check passes silently.
Add explicit bounds checks for both the WPA and WPA2 paths before the memcpy, rejecting any IE whose total size (wpa_ielen + 2) exceeds the supplicant_ie buffer.
--- drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c | 8 ++++++++ 1 file changed, 8 insertions(+)
drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c +8 -0
@@ -1482,6 +1482,10 @@ static int rtw_cfg80211_set_wpa_ie(struc pwpa =rtw_get_wpa_ie (buf, &wpa_ielen, ielen);if (pwpa && wpa_ielen >0 ) { +if (wpa_ielen +2 >sizeof (padapter->securitypriv.supplicant_ie)) { + ret = -EINVAL ; + goto exit; + }if (rtw_parse_wpa_ie (pwpa, wpa_ielen +2 , &group_cipher, &pairwise_cipher,NULL ) == _SUCCESS) { padapter->securitypriv.dot11AuthAlgrthm = dot11AuthAlgrthm_8021X; padapter->securitypriv.ndisauthtype = Ndis802_11AuthModeWPAPSK;
@@ -1491,6 +1495,10 @@ static int rtw_cfg80211_set_wpa_ie(struc pwpa2 =rtw_get_wpa2_ie (buf, &wpa2_ielen, ielen);if (pwpa2 && wpa2_ielen >0 ) { +if (wpa2_ielen +2 >sizeof (padapter->securitypriv.supplicant_ie)) { + ret = -EINVAL ; + goto exit; + }if (rtw_parse_wpa2_ie (pwpa2, wpa2_ielen +2 , &group_cipher, &pairwise_cipher,NULL ) == _SUCCESS) { padapter->securitypriv.dot11AuthAlgrthm = dot11AuthAlgrthm_8021X; padapter->securitypriv.ndisauthtype = Ndis802_11AuthModeWPA2PSK;
Greg Kroah-Hartman
maintainerPATCH 962/1067 PCI: controller: Use dev_fwnode() instead of of_fwnode_handle()
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: "Jiri Slaby (SUSE)" <[email protected]>
[ Upstream commit a103d2dede5683dabbac2c3374bc24b6a9434478 ]
All irq_domain functions now accept fwnode instead of of_node. But many PCI controllers still extract dev to of_node and then of_node to fwnode.
Instead, clean this up and simply use the dev_fwnode() helper to extract fwnode directly from dev. Internally, it still does dev => of_node => fwnode steps, but it's now hidden from the users.
In the case of altera, this also removes an unused 'node' variable that is only used when CONFIG_OF is enabled:
drivers/pci/controller/pcie-altera.c: In function 'altera_pcie_init_irq_domain':
drivers/pci/controller/pcie-altera.c:855:29: error: unused variable 'node' [-Werror=unused-variable]
855 | struct device_node *node = dev->of_node;
Stable-dep-of: f865a57896bd ("PCI: mediatek: Fix IRQ domain leak when port fails to enable")
--- drivers/pci/controller/dwc/pcie-designware-host.c | 2 +- drivers/pci/controller/mobiveil/pcie-mobiveil-host.c | 8 +++----- drivers/pci/controller/pcie-altera-msi.c | 2 +- drivers/pci/controller/pcie-altera.c | 3 +-- drivers/pci/controller/pcie-mediatek-gen3.c | 4 ++-- drivers/pci/controller/pcie-mediatek.c | 2 +- drivers/pci/controller/pcie-xilinx-nwl.c | 2 +- 7 files changed, 10 insertions(+), 13 deletions(-)
drivers/pci/controller/dwc/pcie-designware-host.c +1 -1
@@ -232,7 +232,7 @@ static const struct irq_domain_ops dw_pcint dw_pcie_allocate_domains (struct dw_pcie_rp *pp) {struct dw_pcie *pci =to_dw_pcie_from_pp (pp); -struct fwnode_handle *fwnode =of_node_to_fwnode (pci->dev->of_node); +struct fwnode_handle *fwnode =dev_fwnode (pci->dev); pp->irq_domain =irq_domain_create_linear (fwnode, pp->num_vectors, &dw_pcie_msi_domain_ops, pp);
drivers/pci/controller/mobiveil/pcie-mobiveil-host.c +3 -5
@@ -445,7 +445,7 @@ static const struct irq_domain_ops msi_dstatic int mobiveil_allocate_msi_domains (struct mobiveil_pcie *pcie) {struct device *dev = &pcie->pdev->dev; -struct fwnode_handle *fwnode =of_node_to_fwnode (dev->of_node); +struct fwnode_handle *fwnode =dev_fwnode (dev);struct mobiveil_msi *msi = &pcie->rp.msi;mutex_init (&msi->lock);
@@ -471,13 +471,11 @@ static int mobiveil_allocate_msi_domainsstatic int mobiveil_pcie_init_irq_domain (struct mobiveil_pcie *pcie) {struct device *dev = &pcie->pdev->dev; -struct device_node *node = dev->of_node;struct mobiveil_root_port *rp = &pcie->rp;/* setup INTx */ - rp->intx_domain =irq_domain_add_linear (node,PCI_NUM_INTX , - &intx_domain_ops, pcie); - + rp->intx_domain =irq_domain_create_linear (dev_fwnode (dev),PCI_NUM_INTX , &intx_domain_ops, + pcie);if (!rp->intx_domain) {dev_err (dev,"Failed to get a INTx IRQ domain\n" );return -ENOMEM ;
drivers/pci/controller/pcie-altera-msi.c +1 -1
@@ -171,7 +171,7 @@ static const struct irq_domain_ops msi_dstatic int altera_allocate_domains (struct altera_msi *msi) { -struct fwnode_handle *fwnode =of_node_to_fwnode (msi->pdev->dev.of_node); +struct fwnode_handle *fwnode =dev_fwnode (&msi->pdev->dev); msi->inner_domain =irq_domain_add_linear (NULL , msi->num_of_vectors, &msi_domain_ops, msi);
drivers/pci/controller/pcie-altera.c +1 -2
@@ -668,10 +668,9 @@ static void altera_pcie_isr(struct irq_dstatic int altera_pcie_init_irq_domain (struct altera_pcie *pcie) {struct device *dev = &pcie->pdev->dev; -struct device_node *node = dev->of_node;/* Setup INTx */ - pcie->irq_domain =irq_domain_add_linear (node,PCI_NUM_INTX , + pcie->irq_domain =irq_domain_create_linear (dev_fwnode (dev),PCI_NUM_INTX , &intx_domain_ops, pcie);if (!pcie->irq_domain) {dev_err (dev,"Failed to get a INTx IRQ domain\n" );
drivers/pci/controller/pcie-mediatek-gen3.c +2 -2
@@ -661,8 +661,8 @@ static int mtk_pcie_init_irq_domains(str/* Setup MSI */ mutex_init (&pcie->lock); - pcie->msi_bottom_domain =irq_domain_add_linear (node,PCIE_MSI_IRQS_NUM , - &mtk_msi_bottom_domain_ops, pcie); + pcie->msi_bottom_domain =irq_domain_create_linear (dev_fwnode (dev),PCIE_MSI_IRQS_NUM , + &mtk_msi_bottom_domain_ops, pcie);if (!pcie->msi_bottom_domain) {dev_err (dev,"failed to create MSI bottom domain\n" ); ret = -ENODEV ;
drivers/pci/controller/pcie-mediatek.c +1 -1
@@ -496,7 +496,7 @@ static struct msi_domain_info mtk_msi_dostatic int mtk_pcie_allocate_msi_domains (struct mtk_pcie_port *port) { -struct fwnode_handle *fwnode =of_node_to_fwnode (port->pcie->dev->of_node); +struct fwnode_handle *fwnode =dev_fwnode (port->pcie->dev);mutex_init (&port->lock);
drivers/pci/controller/pcie-xilinx-nwl.c +1 -1
@@ -503,7 +503,7 @@ static int nwl_pcie_init_msi_irq_domain( {#ifdef CONFIG_PCI_MSI struct device *dev = pcie->dev; -struct fwnode_handle *fwnode =of_node_to_fwnode (dev->of_node); +struct fwnode_handle *fwnode =dev_fwnode (dev);struct nwl_msi *msi = &pcie->msi; msi->dev_domain = irq_domain_add_linear(NULL, INT_PCI_MSI_NR,
Greg Kroah-Hartman
maintainerPATCH 963/1067 PCI: mediatek: Convert bool to single quirks entry and bitmap
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christian Marangi <[email protected]>
[ Upstream commit 04305367fab7ec9c98eeba315ad09c8b20abce93 ]
To clean Mediatek SoC PCIe struct, convert all the bool to a bitmap and use a single quirks to reference all the values. This permits cleaner addition of new quirk without having to define a new bool in the struct.
Stable-dep-of: f865a57896bd ("PCI: mediatek: Fix IRQ domain leak when port fails to enable")
--- drivers/pci/controller/pcie-mediatek.c | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-)
drivers/pci/controller/pcie-mediatek.c +20 -13
@@ -143,23 +143,31 @@struct mtk_pcie_port ;/** +* enum mtk_pcie_quirks - MTK PCIe quirks +* @MTK_PCIE_FIX_CLASS_ID: host's class ID needed to be fixed +* @MTK_PCIE_FIX_DEVICE_ID: host's device ID needed to be fixed +* @MTK_PCIE_NO_MSI: Bridge has no MSI support, and relies on an external block +*/ +enum mtk_pcie_quirks { +MTK_PCIE_FIX_CLASS_ID =BIT (0 ), +MTK_PCIE_FIX_DEVICE_ID =BIT (1 ), +MTK_PCIE_NO_MSI =BIT (2 ), +}; + +/** * struct mtk_pcie_soc - differentiate between host generations -* @need_fix_class_id: whether this host's class ID needed to be fixed or not -* @need_fix_device_id: whether this host's device ID needed to be fixed or not -* @no_msi: Bridge has no MSI support, and relies on an external block * @device_id: device ID which this host need to be fixed * @ops: pointer to configuration access functions * @startup: pointer to controller setting functions * @setup_irq: pointer to initialize IRQ functions +* @quirks: PCIe device quirks. */ struct mtk_pcie_soc { -bool need_fix_class_id; -bool need_fix_device_id; -bool no_msi;unsigned int device_id;struct pci_ops *ops;int (*startup)(struct mtk_pcie_port *port);int (*setup_irq)(struct mtk_pcie_port *port,struct device_node *node); +enum mtk_pcie_quirks quirks; };/**
@@ -720,7 +728,7 @@ static int mtk_pcie_startup_port_v2(struwritel (val, port->base +PCIE_RST_CTRL );/* Set up vendor ID and class code */ -if (soc->need_fix_class_id) { +if (soc->quirks &MTK_PCIE_FIX_CLASS_ID ) { val =PCI_VENDOR_ID_MEDIATEK ;writew (val, port->base +PCIE_CONF_VEND_ID );
@@ -728,7 +736,7 @@ static int mtk_pcie_startup_port_v2(struwritew (val, port->base +PCIE_CONF_CLASS_ID ); } -if (soc->need_fix_device_id) +if (soc->quirks &MTK_PCIE_FIX_DEVICE_ID )writew (soc->device_id, port->base +PCIE_CONF_DEVICE_ID );/* 100ms timeout value should be enough for Gen1/2 training */
@@ -1129,7 +1137,7 @@ static int mtk_pcie_probe(struct platfor host->ops = pcie->soc->ops; host->sysdata = pcie; - host->msi_domain = pcie->soc->no_msi; + host->msi_domain = !!(pcie->soc->quirks &MTK_PCIE_NO_MSI ); err =pci_host_probe (host);if (err)
@@ -1219,9 +1227,9 @@ static const struct dev_pm_ops mtk_pcie_ };static const struct mtk_pcie_soc mtk_pcie_soc_v1 = { - .no_msi = true, .ops = &mtk_pcie_ops, .startup = mtk_pcie_startup_port, + .quirks =MTK_PCIE_NO_MSI , };static const struct mtk_pcie_soc mtk_pcie_soc_mt2712 = {
@@ -1231,19 +1239,18 @@ static const struct mtk_pcie_soc mtk_pci };static const struct mtk_pcie_soc mtk_pcie_soc_mt7622 = { - .need_fix_class_id = true, .ops = &mtk_pcie_ops_v2, .startup = mtk_pcie_startup_port_v2, .setup_irq = mtk_pcie_setup_irq, + .quirks =MTK_PCIE_FIX_CLASS_ID , };static const struct mtk_pcie_soc mtk_pcie_soc_mt7629 = { - .need_fix_class_id = true, - .need_fix_device_id = true, .device_id =PCI_DEVICE_ID_MEDIATEK_7629 , .ops = &mtk_pcie_ops_v2, .startup = mtk_pcie_startup_port_v2, .setup_irq = mtk_pcie_setup_irq, + .quirks =MTK_PCIE_FIX_CLASS_ID |MTK_PCIE_FIX_DEVICE_ID , };static const struct of_device_id mtk_pcie_ids[] = {
Greg Kroah-Hartman
maintainerPATCH 964/1067 PCI: mediatek: Use generic MACRO for TPVPERL delay
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christian Marangi <[email protected]>
[ Upstream commit 2d58bc777728bfc37aa35dce7b90e72296cceb9f ]
Use the generic PCI MACRO for TPVPERL delay to wait for clock and power stabilization after PERST# Signal instead of the raw value of 100 ms.
Stable-dep-of: f865a57896bd ("PCI: mediatek: Fix IRQ domain leak when port fails to enable")
--- drivers/pci/controller/pcie-mediatek.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-)
drivers/pci/controller/pcie-mediatek.c +1 -6
@@ -714,12 +714,7 @@ static int mtk_pcie_startup_port_v2(stru*/ writel (PCIE_LINKDOWN_RST_EN , port->base +PCIE_RST_CTRL ); -/* -* Described in PCIe CEM specification sections 2.2 (PERST# Signal) and -* 2.2.1 (Initial Power-Up (G3 to S0)). The deassertion of PERST# should -* be delayed 100ms (TPVPERL) for the power and clock to become stable. -*/ -msleep (100 ); +msleep (PCIE_T_PVPERL_MS );/* De-assert PHY, PE, PIPE, MAC and configuration reset */ val =readl (port->base +PCIE_RST_CTRL );
Greg Kroah-Hartman
maintainerPATCH 965/1067 PCI: mediatek: Fix IRQ domain leak when port fails to enable
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Manivannan Sadhasivam <[email protected]>
[ Upstream commit f865a57896bd92d7662eb2818d8f48872e2cbbc7 ]
When mtk_pcie_enable_port() fails, mtk_pcie_port_free() removes the port from pcie->ports and frees the port structure. However, the IRQ domains set up earlier by mtk_pcie_init_irq_domain() are never freed.
Fix this by refactoring mtk_pcie_irq_teardown() into a per-port helper, mtk_pcie_irq_teardown_port(), and calling it from mtk_pcie_setup() when mtk_pcie_enable_port() fails. Since the IRQ teardown must only happen in the probe error path (during resume, child devices may have active MSI mappings and the NOIRQ context prohibits sleeping locks), mtk_pcie_enable_port() is changed to return an error code so callers can distinguish the two paths and act accordingly.
This issue was reported by Sashiko while reviewing the EcoNet EN7528 SoC support series.
--- drivers/pci/controller/pcie-mediatek.c | 67 ++++++++++++++++++++------------- drivers/pci/pci.h | 3 + 2 files changed, 45 insertions(+), 25 deletions(-)
drivers/pci/controller/pcie-mediatek.c +42 -25
@@ -540,25 +540,29 @@ static void mtk_pcie_enable_msi(struct mwritel (val, port->base +PCIE_INT_MASK ); } -static void mtk_pcie_irq_teardown (struct mtk_pcie *pcie) +static void mtk_pcie_irq_teardown_port (struct mtk_pcie_port *port) { -struct mtk_pcie_port *port, *tmp; +irq_set_chained_handler_and_data (port->irq,NULL ,NULL ); -list_for_each_entry_safe (port, tmp, &pcie->ports, list) { -irq_set_chained_handler_and_data (port->irq,NULL ,NULL ); +if (port->irq_domain) +irq_domain_remove (port->irq_domain); -if (port->irq_domain) -irq_domain_remove (port->irq_domain); +if (IS_ENABLED (CONFIG_PCI_MSI )) { +if (port->msi_domain) +irq_domain_remove (port->msi_domain); +if (port->inner_domain) +irq_domain_remove (port->inner_domain); + } -if (IS_ENABLED (CONFIG_PCI_MSI )) { -if (port->msi_domain) -irq_domain_remove (port->msi_domain); -if (port->inner_domain) -irq_domain_remove (port->inner_domain); - } +irq_dispose_mapping (port->irq); +} -irq_dispose_mapping (port->irq); - } +static void mtk_pcie_irq_teardown (struct mtk_pcie *pcie) +{ +struct mtk_pcie_port *port, *tmp; + +list_for_each_entry_safe (port, tmp, &pcie->ports, list) +mtk_pcie_irq_teardown_port (port); } static int mtk_pcie_intx_map(struct irq_domain *domain, unsigned int irq,
@@ -841,7 +845,7 @@ static int mtk_pcie_startup_port(structreturn 0 ; } -static void mtk_pcie_enable_port (struct mtk_pcie_port *port) +static int mtk_pcie_enable_port (struct mtk_pcie_port *port) {struct mtk_pcie *pcie = port->pcie;struct device *dev = pcie->dev;
@@ -850,7 +854,7 @@ static void mtk_pcie_enable_port(struct err =clk_prepare_enable (port->sys_ck);if (err) {dev_err (dev,"failed to enable sys_ck%d clock\n" , port->slot); - goto err_sys_clk; +return err; } err =clk_prepare_enable (port->ahb_ck);
@@ -898,11 +902,15 @@ static void mtk_pcie_enable_port(struct goto err_phy_on; } -if (!pcie->soc->startup (port)) -return ; + err = pcie->soc->startup (port); +if (err) { +dev_info (dev,"Port%d link down\n" , port->slot); + goto err_soc_startup; + } -dev_info (dev,"Port%d link down\n" , port->slot); +return 0 ; +err_soc_startup:phy_power_off (port->phy); err_phy_on:phy_exit (port->phy);
@@ -918,8 +926,8 @@ err_aux_clk:clk_disable_unprepare (port->ahb_ck); err_ahb_clk:clk_disable_unprepare (port->sys_ck); -err_sys_clk: -mtk_pcie_port_free (port); + +return err; } static int mtk_pcie_parse_port(struct mtk_pcie *pcie,
@@ -1095,8 +1103,13 @@ static int mtk_pcie_setup(struct mtk_pcireturn err;/* enable each port, and then check link status */ -list_for_each_entry_safe (port, tmp, &pcie->ports, list) -mtk_pcie_enable_port (port); +list_for_each_entry_safe (port, tmp, &pcie->ports, list) { + err =mtk_pcie_enable_port (port); +if (err) { +mtk_pcie_irq_teardown_port (port); +mtk_pcie_port_free (port); + } + }/* power down PCIe subsys if slots are all empty (link down) */ if (list_empty (&pcie->ports))
@@ -1200,14 +1213,18 @@ static int mtk_pcie_resume_noirq(struct {struct mtk_pcie *pcie =dev_get_drvdata (dev);struct mtk_pcie_port *port, *tmp; +int err;if (list_empty (&pcie->ports))return 0 ;clk_prepare_enable (pcie->free_ck); -list_for_each_entry_safe (port, tmp, &pcie->ports, list) -mtk_pcie_enable_port (port); +list_for_each_entry_safe (port, tmp, &pcie->ports, list) { + err =mtk_pcie_enable_port (port); +if (err) +mtk_pcie_port_free (port); + }/* In case of EP was removed while system suspend. */ if (list_empty (&pcie->ports))
drivers/pci/pci.h +3 -0
@@ -14,6 +14,9 @@#define PCI_EXP_AER_FLAGS (PCI_EXP_DEVCTL_CERE | PCI_EXP_DEVCTL_NFERE | \ PCI_EXP_DEVCTL_FERE | PCI_EXP_DEVCTL_URRE) +/* Power stable to PERST# inactive from PCIe card Electromechanical Spec */ +#define PCIE_T_PVPERL_MS 100 +/* * PCIe r6.0, sec 5.3.3.2.1 <PME Synchronization> * Recommends 1ms to 10ms timeout to check L2 ready.
Greg Kroah-Hartman
maintainerPATCH 966/1067 staging: rtl8723bs: core: move constants to right side in comparison
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: William Hansen-Baird <[email protected]>
[ Upstream commit cf0f2680c30d4b438a5e84b73dc70be405936b54 ]
Move constants to right side in if-statement conditions.
Stable-dep-of: 1463ca3ec660 ("staging: rtl8723bs: fix OOB reads in rtw_get_sec_ie(), rtw_get_wapi_ie(), and rtw_get_wps_attr()")
--- drivers/staging/rtl8723bs/core/rtw_ieee80211.c | 4 ++-- drivers/staging/rtl8723bs/core/rtw_security.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-)
drivers/staging/rtl8723bs/core/rtw_ieee80211.c +2 -2
@@ -1012,7 +1012,7 @@ static int rtw_get_cipher_info(struct wl pbuf =rtw_get_wpa_ie (&pnetwork->network.ies[12 ], &wpa_ielen, pnetwork->network.ie_length-12 );if (pbuf && (wpa_ielen >0 )) { -if (_SUCCESS ==rtw_parse_wpa_ie (pbuf, wpa_ielen+2 , &group_cipher, &pairwise_cipher, &is8021x)) { +if (rtw_parse_wpa_ie (pbuf, wpa_ielen+2 , &group_cipher, &pairwise_cipher, &is8021x) == _SUCCESS) { pnetwork->bcn_info.pairwise_cipher = pairwise_cipher; pnetwork->bcn_info.group_cipher = group_cipher; pnetwork->bcn_info.is_8021x = is8021x;
@@ -1022,7 +1022,7 @@ static int rtw_get_cipher_info(struct wl pbuf =rtw_get_wpa2_ie (&pnetwork->network.ies[12 ], &wpa_ielen, pnetwork->network.ie_length-12 );if (pbuf && (wpa_ielen >0 )) { -if (_SUCCESS ==rtw_parse_wpa2_ie (pbuf, wpa_ielen+2 , &group_cipher, &pairwise_cipher, &is8021x)) { +if (rtw_parse_wpa2_ie (pbuf, wpa_ielen+2 , &group_cipher, &pairwise_cipher, &is8021x) == _SUCCESS) { pnetwork->bcn_info.pairwise_cipher = pairwise_cipher; pnetwork->bcn_info.group_cipher = group_cipher; pnetwork->bcn_info.is_8021x = is8021x;
drivers/staging/rtl8723bs/core/rtw_security.c +1 -1
@@ -1538,7 +1538,7 @@ void rtw_sec_restore_wep_key(struct adapstruct security_priv *securitypriv = &(adapter->securitypriv);signed int keyid; -if ((_WEP40_ == securitypriv->dot11PrivacyAlgrthm) || (_WEP104_ == securitypriv->dot11PrivacyAlgrthm)) { +if ((securitypriv->dot11PrivacyAlgrthm == _WEP40_) || (securitypriv->dot11PrivacyAlgrthm == _WEP104_)) {for (keyid =0 ; keyid <4 ; keyid++) {if (securitypriv->key_mask &BIT (keyid)) {if (keyid == securitypriv->dot11PrivacyKeyIndex)
Greg Kroah-Hartman
maintainerPATCH 967/1067 staging: rtl8723bs: fix spaces around binary operators
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Nikolay Kulikov <[email protected]>
[ Upstream commit 0c9d1b56f9af0762a5be5118e1bb962f23784dc4 ]
Add missing spaces and fix line length to comply with kernel coding style.
Stable-dep-of: 1463ca3ec660 ("staging: rtl8723bs: fix OOB reads in rtw_get_sec_ie(), rtw_get_wapi_ie(), and rtw_get_wps_attr()")
--- drivers/staging/rtl8723bs/core/rtw_ieee80211.c | 78 +++++++++++++------------ 1 file changed, 42 insertions(+), 36 deletions(-)
drivers/staging/rtl8723bs/core/rtw_ieee80211.c +42 -36
@@ -455,10 +455,10 @@ int rtw_parse_wpa_ie(u8 *wpa_ie, int wpareturn _FAIL; } -if ((*wpa_ie !=WLAN_EID_VENDOR_SPECIFIC ) || (*(wpa_ie+1 ) != (u8)(wpa_ie_len -2 )) || - (memcmp (wpa_ie+2 ,RTW_WPA_OUI_TYPE ,WPA_SELECTOR_LEN ))) { +if ((*wpa_ie !=WLAN_EID_VENDOR_SPECIFIC ) || + (*(wpa_ie +1 ) != (u8)(wpa_ie_len -2 )) || + (memcmp (wpa_ie +2 ,RTW_WPA_OUI_TYPE ,WPA_SELECTOR_LEN )))return _FAIL; - } pos = wpa_ie;
@@ -518,7 +518,7 @@ int rtw_parse_wpa2_ie(u8 *rsn_ie, int rsreturn _FAIL; } -if ((*rsn_ie !=WLAN_EID_RSN ) || (*(rsn_ie+1 ) != (u8)(rsn_ie_len -2 ))) +if ((*rsn_ie !=WLAN_EID_RSN ) || (*(rsn_ie +1 ) != (u8)(rsn_ie_len -2 )))return _FAIL; pos = rsn_ie;
@@ -586,18 +586,18 @@ int rtw_get_wapi_ie(u8 *in_ie, uint in_lwhile (cnt < in_len) { authmode = in_ie[cnt]; -/* if (authmode == WLAN_EID_BSS_AC_ACCESS_DELAY) */ -if (authmode ==WLAN_EID_BSS_AC_ACCESS_DELAY && (!memcmp (&in_ie[cnt+6 ], wapi_oui1,4 ) || - !memcmp (&in_ie[cnt+6 ], wapi_oui2,4 ))) { +if (authmode ==WLAN_EID_BSS_AC_ACCESS_DELAY && + (!memcmp (&in_ie[cnt +6 ], wapi_oui1,4 ) || + !memcmp (&in_ie[cnt +6 ], wapi_oui2,4 ))) {if (wapi_ie) -memcpy (wapi_ie, &in_ie[cnt], in_ie[cnt+1 ]+2 ); +memcpy (wapi_ie, &in_ie[cnt], in_ie[cnt +1 ] +2 );if (wapi_len) - *wapi_len = in_ie[cnt+1 ]+2 ; + *wapi_len = in_ie[cnt +1 ] +2 ; - cnt += in_ie[cnt+1 ]+2 ;/* get next */ + cnt += in_ie[cnt +1 ] +2 ;/* get next */ }else { - cnt += in_ie[cnt+1 ]+2 ;/* get next */ + cnt += in_ie[cnt +1 ] +2 ;/* get next */ } }
@@ -621,9 +621,10 @@ void rtw_get_sec_ie(u8 *in_ie, uint in_lwhile (cnt < in_len) { authmode = in_ie[cnt]; -if ((authmode ==WLAN_EID_VENDOR_SPECIFIC ) && (!memcmp (&in_ie[cnt+2 ], &wpa_oui[0 ],4 ))) { +if ((authmode ==WLAN_EID_VENDOR_SPECIFIC ) && + (!memcmp (&in_ie[cnt +2 ], &wpa_oui[0 ],4 ))) {if (wpa_ie) -memcpy (wpa_ie, &in_ie[cnt], in_ie[cnt+1 ]+2 ); +memcpy (wpa_ie, &in_ie[cnt], in_ie[cnt +1 ] +2 ); *wpa_len = in_ie[cnt +1 ] +2 ; cnt += in_ie[cnt +1 ] +2 ;/* get next */
@@ -632,10 +633,10 @@ void rtw_get_sec_ie(u8 *in_ie, uint in_lif (rsn_ie)memcpy (rsn_ie, &in_ie[cnt], in_ie[cnt +1 ] +2 ); - *rsn_len = in_ie[cnt+1 ]+2 ; - cnt += in_ie[cnt+1 ]+2 ;/* get next */ + *rsn_len = in_ie[cnt +1 ] +2 ; + cnt += in_ie[cnt +1 ] +2 ;/* get next */ }else { - cnt += in_ie[cnt+1 ]+2 ;/* get next */ + cnt += in_ie[cnt +1 ] +2 ;/* get next */ } } }
@@ -667,20 +668,20 @@ u8 *rtw_get_wps_ie(u8 *in_ie, uint in_lewhile (cnt < in_len) { eid = in_ie[cnt]; -if ((eid ==WLAN_EID_VENDOR_SPECIFIC ) && (!memcmp (&in_ie[cnt+2 ], wps_oui,4 ))) { +if ((eid ==WLAN_EID_VENDOR_SPECIFIC ) && (!memcmp (&in_ie[cnt +2 ], wps_oui,4 ))) { wpsie_ptr = &in_ie[cnt];if (wps_ie) -memcpy (wps_ie, &in_ie[cnt], in_ie[cnt+1 ]+2 ); +memcpy (wps_ie, &in_ie[cnt], in_ie[cnt +1 ] +2 );if (wps_ielen) - *wps_ielen = in_ie[cnt+1 ]+2 ; + *wps_ielen = in_ie[cnt +1 ] +2 ; - cnt += in_ie[cnt+1 ]+2 ; + cnt += in_ie[cnt +1 ] +2 ;break ; } - cnt += in_ie[cnt+1 ]+2 ;/* goto next */ + cnt += in_ie[cnt +1 ] +2 ;/* goto next */ }return wpsie_ptr;
@@ -758,12 +759,12 @@ u8 *rtw_get_wps_attr_content(u8 *wps_ie,if (attr_ptr && attr_len) {if (buf_content) -memcpy (buf_content, attr_ptr+4 , attr_len-4 ); +memcpy (buf_content, attr_ptr +4 , attr_len -4 );if (len_content) - *len_content = attr_len-4 ; + *len_content = attr_len -4 ; -return attr_ptr+4 ; +return attr_ptr +4 ; }return NULL ;
@@ -1009,20 +1010,25 @@ static int rtw_get_cipher_info(struct wlint group_cipher =0 , pairwise_cipher =0 , is8021x =0 ;int ret = _FAIL; - pbuf =rtw_get_wpa_ie (&pnetwork->network.ies[12 ], &wpa_ielen, pnetwork->network.ie_length-12 ); + pbuf =rtw_get_wpa_ie (&pnetwork->network.ies[12 ], + &wpa_ielen, + pnetwork->network.ie_length -12 );if (pbuf && (wpa_ielen >0 )) { -if (rtw_parse_wpa_ie (pbuf, wpa_ielen+2 , &group_cipher, &pairwise_cipher, &is8021x) == _SUCCESS) { +if (rtw_parse_wpa_ie (pbuf, wpa_ielen +2 , &group_cipher, + &pairwise_cipher, &is8021x) == _SUCCESS) { pnetwork->bcn_info.pairwise_cipher = pairwise_cipher; pnetwork->bcn_info.group_cipher = group_cipher; pnetwork->bcn_info.is_8021x = is8021x; ret = _SUCCESS; } }else { - pbuf =rtw_get_wpa2_ie (&pnetwork->network.ies[12 ], &wpa_ielen, pnetwork->network.ie_length-12 ); + pbuf =rtw_get_wpa2_ie (&pnetwork->network.ies[12 ], &wpa_ielen, + pnetwork->network.ie_length -12 );if (pbuf && (wpa_ielen >0 )) { -if (rtw_parse_wpa2_ie (pbuf, wpa_ielen+2 , &group_cipher, &pairwise_cipher, &is8021x) == _SUCCESS) { +if (rtw_parse_wpa2_ie (pbuf, wpa_ielen +2 , &group_cipher, + &pairwise_cipher, &is8021x) == _SUCCESS) { pnetwork->bcn_info.pairwise_cipher = pairwise_cipher; pnetwork->bcn_info.group_cipher = group_cipher; pnetwork->bcn_info.is_8021x = is8021x;
@@ -1091,21 +1097,21 @@ u16 rtw_mcs_rate(u8 bw_40MHz, u8 short_Gu16 max_rate =0 ;if (MCS_rate[0 ] &BIT (7 )) - max_rate = (bw_40MHz) ? ((short_GI)?1500 :1350 ):((short_GI)?722 :650 ); + max_rate = (bw_40MHz) ? ((short_GI) ?1500 :1350 ) : ((short_GI) ?722 :650 );else if (MCS_rate[0 ] &BIT (6 )) - max_rate = (bw_40MHz) ? ((short_GI)?1350 :1215 ):((short_GI)?650 :585 ); + max_rate = (bw_40MHz) ? ((short_GI) ?1350 :1215 ) : ((short_GI) ?650 :585 );else if (MCS_rate[0 ] &BIT (5 )) - max_rate = (bw_40MHz) ? ((short_GI)?1200 :1080 ):((short_GI)?578 :520 ); + max_rate = (bw_40MHz) ? ((short_GI) ?1200 :1080 ) : ((short_GI) ?578 :520 );else if (MCS_rate[0 ] &BIT (4 )) - max_rate = (bw_40MHz) ? ((short_GI)?900 :810 ):((short_GI)?433 :390 ); + max_rate = (bw_40MHz) ? ((short_GI) ?900 :810 ) : ((short_GI) ?433 :390 );else if (MCS_rate[0 ] &BIT (3 )) - max_rate = (bw_40MHz) ? ((short_GI)?600 :540 ):((short_GI)?289 :260 ); + max_rate = (bw_40MHz) ? ((short_GI) ?600 :540 ) : ((short_GI) ?289 :260 );else if (MCS_rate[0 ] &BIT (2 )) - max_rate = (bw_40MHz) ? ((short_GI)?450 :405 ):((short_GI)?217 :195 ); + max_rate = (bw_40MHz) ? ((short_GI) ?450 :405 ) : ((short_GI) ?217 :195 );else if (MCS_rate[0 ] &BIT (1 )) - max_rate = (bw_40MHz) ? ((short_GI)?300 :270 ):((short_GI)?144 :130 ); + max_rate = (bw_40MHz) ? ((short_GI) ?300 :270 ) : ((short_GI) ?144 :130 );else if (MCS_rate[0 ] &BIT (0 )) - max_rate = (bw_40MHz) ? ((short_GI)?150 :135 ):((short_GI)?72 :65 ); + max_rate = (bw_40MHz) ? ((short_GI) ?150 :135 ) : ((short_GI) ?72 :65 );return max_rate; }
Greg Kroah-Hartman
maintainerPATCH 968/1067 staging: rtl8723bs: fix OOB reads in rtw_get_sec_ie(), rtw_get_wapi_ie(), and rtw_get_wps_attr()
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alexandru Hossu <[email protected]>
[ Upstream commit 1463ca3ec6601cbb097d8d87dbf5dcf1cb86a344 ]
Three IE/attribute parsing functions have missing bounds checks.
rtw_get_sec_ie() and rtw_get_wapi_ie() iterate over a raw IE buffer without verifying that the header bytes (tag + length) are within the remaining buffer before reading them. Additionally, rtw_get_sec_ie() compares the 4-byte WPA OUI at cnt+2 without checking that at least 6 bytes remain, and rtw_get_wapi_ie() compares a 4-byte WAPI OUI at cnt+6 without checking that at least 10 bytes remain.
rtw_get_wps_attr() reads wps_ie[0] and wps_ie+2 unconditionally at entry, before verifying that wps_ielen is large enough to contain the 6-byte WPS IE header (element_id + length + 4-byte OUI). Inside the attribute loop, get_unaligned_be16() is called on attr_ptr and attr_ptr+2 without checking that 4 bytes remain in the buffer.
Add a cnt+2 bounds check before each loop body in rtw_get_sec_ie() and rtw_get_wapi_ie(), guard each multi-byte comparison with a minimum IE length requirement, add a wps_ielen < 6 early return in rtw_get_wps_attr(), and add a 4-byte bounds check in its inner loop.
--- drivers/staging/rtl8723bs/core/rtw_ieee80211.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+)
drivers/staging/rtl8723bs/core/rtw_ieee80211.c +15 -0
@@ -584,9 +584,14 @@ int rtw_get_wapi_ie(u8 *in_ie, uint in_l cnt = (_TIMESTAMP_ + _BEACON_ITERVAL_ + _CAPABILITY_);while (cnt < in_len) { +if (cnt +2 > in_len) +break ; +if (cnt +2 + in_ie[cnt +1 ] > in_len) +break ; authmode = in_ie[cnt];if (authmode ==WLAN_EID_BSS_AC_ACCESS_DELAY && + in_ie[cnt +1 ] >=8 && (!memcmp (&in_ie[cnt +6 ], wapi_oui1,4 ) || !memcmp (&in_ie[cnt +6 ], wapi_oui2,4 ))) {if (wapi_ie)
@@ -619,9 +624,14 @@ void rtw_get_sec_ie(u8 *in_ie, uint in_l cnt = (_TIMESTAMP_ + _BEACON_ITERVAL_ + _CAPABILITY_);while (cnt < in_len) { +if (cnt +2 > in_len) +break ; +if (cnt +2 + in_ie[cnt +1 ] > in_len) +break ; authmode = in_ie[cnt];if ((authmode ==WLAN_EID_VENDOR_SPECIFIC ) && + in_ie[cnt +1 ] >=4 && (!memcmp (&in_ie[cnt +2 ], &wpa_oui[0 ],4 ))) {if (wpa_ie)memcpy (wpa_ie, &in_ie[cnt], in_ie[cnt +1 ] +2 );
@@ -706,6 +716,9 @@ u8 *rtw_get_wps_attr(u8 *wps_ie, uint wpif (len_attr) *len_attr =0 ; +if (wps_ielen <6 ) +return attr_ptr; +if ((wps_ie[0 ] !=WLAN_EID_VENDOR_SPECIFIC ) || (memcmp (wps_ie +2 , wps_oui,4 ))) {return attr_ptr;
@@ -716,6 +729,8 @@ u8 *rtw_get_wps_attr(u8 *wps_ie, uint wpwhile (attr_ptr - wps_ie < wps_ielen) {/* 4 = 2(Attribute ID) + 2(Length) */ +if (attr_ptr +4 > wps_ie + wps_ielen) +break ;u16 attr_id =get_unaligned_be16 (attr_ptr);u16 attr_data_len =get_unaligned_be16 (attr_ptr +2 );u16 attr_len = attr_data_len +4 ;
Greg Kroah-Hartman
maintainerPATCH 969/1067 crypto: qat - fix VF2PF work teardown race in adf_disable_sriov()
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Giovanni Cabiddu <[email protected]>
[ Upstream commit 277281c10c63791067d24d421f7c43a15faa9096 ]
The VF2PF interrupt handler queues PF-side response work that stores a raw pointer to per-VF state (struct adf_accel_vf_info). Currently, adf_disable_sriov() destroys per-VF mutexes and frees vf_info without stopping new VF2PF work or waiting for in-flight workers to complete. A concurrently scheduled or already queued worker can then dereference freed memory.
This manifests as a use-after-free when KASAN is enabled:
BUG: KASAN: null-ptr-deref in mutex_lock+0x76/0xe0
Write of size 8 at addr 0000000000000260 by task kworker/24:2/...
Workqueue: qat_pf2vf_resp_wq adf_iov_send_resp [intel_qat]
Call Trace:
kasan_report+0x119/0x140
mutex_lock+0x76/0xe0
adf_gen4_pfvf_send+0xd4/0x1f0 [intel_qat]
adf_recv_and_handle_vf2pf_msg+0x290/0x360 [intel_qat]
adf_iov_send_resp+0x8c/0xe0 [intel_qat]
process_one_work+0x6ac/0xfd0
worker_thread+0x4dd/0xd30
kthread+0x326/0x410
ret_from_fork+0x33b/0x670Add a PF-local flag, vf2pf_disabled, that gates work queueing, worker processing, and interrupt re-enabling during teardown. Set this flag atomically with the hardware interrupt mask inside adf_disable_all_vf2pf_interrupts(). After masking, synchronize the AE cluster MSI-X interrupt and flush the PF response workqueue before tearing down per-VF locks and state so all in-flight work completes before vf_info is destroyed.
Introduce adf_enable_all_vf2pf_interrupts() to clear the flag and unmask all VF2PF interrupts under the same lock when SR-IOV is re-enabled. This ensures the software flag and hardware state transition atomically on both the enable and disable paths.
--- drivers/crypto/qat/qat_common/adf_accel_devices.h | 2 + drivers/crypto/qat/qat_common/adf_common_drv.h | 2 + drivers/crypto/qat/qat_common/adf_isr.c | 39 ++++++++++++++++++++++ drivers/crypto/qat/qat_common/adf_sriov.c | 20 ++++++++++- 4 files changed, 61 insertions(+), 2 deletions(-)
drivers/crypto/qat/qat_common/adf_accel_devices.h +2 -0
@@ -284,6 +284,8 @@ struct adf_accel_dev {struct {/* protects VF2PF interrupts access */ spinlock_t vf2pf_ints_lock; +/* prevents VF2PF handling from racing with VF state teardown */ +bool vf2pf_disabled;/* vf_info is non-zero when SR-IOV is init'ed */ struct adf_accel_vf_info *vf_info; } pf;
drivers/crypto/qat/qat_common/adf_common_drv.h +2 -0
@@ -124,6 +124,7 @@ void qat_asym_algs_unregister(void);int adf_isr_resource_alloc (struct adf_accel_dev *accel_dev);void adf_isr_resource_free (struct adf_accel_dev *accel_dev); +void adf_isr_sync_ae_cluster (struct adf_accel_dev *accel_dev);int adf_vf_isr_resource_alloc (struct adf_accel_dev *accel_dev);void adf_vf_isr_resource_free (struct adf_accel_dev *accel_dev);
@@ -196,6 +197,7 @@ void adf_misc_wq_flush(void);int adf_sriov_configure (struct pci_dev *pdev,int numvfs);void adf_disable_sriov (struct adf_accel_dev *accel_dev);void adf_enable_vf2pf_interrupts (struct adf_accel_dev *accel_dev,u32 vf_mask); +void adf_enable_all_vf2pf_interrupts (struct adf_accel_dev *accel_dev,u32 num_vfs);void adf_disable_all_vf2pf_interrupts (struct adf_accel_dev *accel_dev);bool adf_recv_and_handle_pf2vf_msg (struct adf_accel_dev *accel_dev);bool adf_recv_and_handle_vf2pf_msg (struct adf_accel_dev *accel_dev,u32 vf_nr);
drivers/crypto/qat/qat_common/adf_isr.c +39 -0
@@ -62,6 +62,23 @@ void adf_enable_vf2pf_interrupts(structunsigned long flags;spin_lock_irqsave (&accel_dev->pf.vf2pf_ints_lock, flags); +if (!READ_ONCE (accel_dev->pf.vf2pf_disabled)) +GET_PFVF_OPS (accel_dev)->enable_vf2pf_interrupts (pmisc_addr, vf_mask); +spin_unlock_irqrestore (&accel_dev->pf.vf2pf_ints_lock, flags); +} + +void adf_enable_all_vf2pf_interrupts (struct adf_accel_dev *accel_dev,u32 num_vfs) +{ +void __iomem *pmisc_addr =adf_get_pmisc_base (accel_dev); +unsigned long flags; +u32 vf_mask; + + vf_mask =BIT_ULL (num_vfs) -1 ; +if (!vf_mask) +return ; + +spin_lock_irqsave (&accel_dev->pf.vf2pf_ints_lock, flags); +WRITE_ONCE (accel_dev->pf.vf2pf_disabled, false);GET_PFVF_OPS (accel_dev)->enable_vf2pf_interrupts (pmisc_addr, vf_mask);spin_unlock_irqrestore (&accel_dev->pf.vf2pf_ints_lock, flags); }
@@ -72,6 +89,7 @@ void adf_disable_all_vf2pf_interrupts(stunsigned long flags;spin_lock_irqsave (&accel_dev->pf.vf2pf_ints_lock, flags); +WRITE_ONCE (accel_dev->pf.vf2pf_disabled, true);GET_PFVF_OPS (accel_dev)->disable_all_vf2pf_interrupts (pmisc_addr);spin_unlock_irqrestore (&accel_dev->pf.vf2pf_ints_lock, flags); }
@@ -151,6 +169,27 @@ static irqreturn_t adf_msix_isr_ae(int ireturn IRQ_NONE ; } +void adf_isr_sync_ae_cluster (struct adf_accel_dev *accel_dev) +{ +struct adf_accel_pci *pci_dev_info = &accel_dev->accel_pci_dev; +struct adf_hw_device_data *hw_data =GET_HW_DATA (accel_dev); +u32 num_entries = pci_dev_info->msix_entries.num_entries; +struct adf_irq *irqs = pci_dev_info->msix_entries.irqs; +u32 irq_idx; +int irq; + +if (!test_bit (ADF_STATUS_IRQ_ALLOCATED , &accel_dev->status) || !irqs) +return ; + + irq_idx = num_entries >1 ? hw_data->num_banks :0 ; +if (irq_idx >= num_entries || !irqs[irq_idx].enabled) +return ; + + irq =pci_irq_vector (pci_dev_info->pci_dev, hw_data->num_banks); +if (irq >0 ) +synchronize_irq (irq); +} +static void adf_free_irqs (struct adf_accel_dev *accel_dev) {struct adf_accel_pci *pci_dev_info = &accel_dev->accel_pci_dev;
drivers/crypto/qat/qat_common/adf_sriov.c +18 -2
@@ -26,18 +26,26 @@ static void adf_iov_send_resp(struct woru32 vf_nr = vf_info->vf_nr;bool ret; +if (READ_ONCE (accel_dev->pf.vf2pf_disabled)) + goto out; + ret =adf_recv_and_handle_vf2pf_msg (accel_dev, vf_nr);if (ret)/* re-enable interrupt on PF from this VF */ adf_enable_vf2pf_interrupts (accel_dev,1 << vf_nr); +out:kfree (pf2vf_resp); }void adf_schedule_vf2pf_handler (struct adf_accel_vf_info *vf_info) { +struct adf_accel_dev *accel_dev = vf_info->accel_dev;struct adf_pf2vf_resp *pf2vf_resp; +if (READ_ONCE (accel_dev->pf.vf2pf_disabled)) +return ; + pf2vf_resp =kzalloc (sizeof (*pf2vf_resp),GFP_ATOMIC );if (!pf2vf_resp)return ;
@@ -47,6 +55,12 @@ void adf_schedule_vf2pf_handler(struct aqueue_work (pf2vf_resp_wq, &pf2vf_resp->pf2vf_resp_work); } +static void adf_flush_pf2vf_resp_wq (void ) +{ +if (pf2vf_resp_wq) +flush_workqueue (pf2vf_resp_wq); +} +static int adf_enable_sriov (struct adf_accel_dev *accel_dev) {struct pci_dev *pdev =accel_to_pci_dev (accel_dev);
@@ -73,7 +87,7 @@ static int adf_enable_sriov(struct adf_a hw_data->configure_iov_threads (accel_dev, true);/* Enable VF to PF interrupts for all VFs */ -adf_enable_vf2pf_interrupts (accel_dev,BIT_ULL (totalvfs) -1 ); +adf_enable_all_vf2pf_interrupts (accel_dev, totalvfs);/* * Due to the hardware design, when SR-IOV and the ring arbiter
@@ -105,8 +119,10 @@ void adf_disable_sriov(struct adf_accel_adf_pf2vf_notify_restarting (accel_dev);pci_disable_sriov (accel_to_pci_dev (accel_dev)); -/* Disable VF to PF interrupts */ +/* Block VF2PF work and disable VF to PF interrupts */ adf_disable_all_vf2pf_interrupts (accel_dev); +adf_isr_sync_ae_cluster (accel_dev); +adf_flush_pf2vf_resp_wq ();/* Clear Valid bits in AE Thread to PCIe Function Mapping */ if (hw_data->configure_iov_threads)
Greg Kroah-Hartman
maintainerPATCH 970/1067 PCI: Prevent resource tree corruption when BAR resize fails
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ilpo Järvinen <[email protected]>
[ Upstream commit 91c4c89db41499eea1b29c56655f79c3bae66e93 ]
pbus_reassign_bridge_resources() saves bridge windows into the saved list before attempting to adjust resource assignments to perform a BAR resize operation. If resource adjustments cannot be completed fully, rollback is attempted by restoring the resource from the saved list.
The rollback, however, does not check whether the resources it restores were assigned by the partial resize attempt. If restore changes addresses of the resource, it can result in corrupting the resource tree.
An example of a corrupted resource tree with overlapping addresses:
6200000000000-6203fbfffffff : pciex@620c3c0000000
6200000000000-6203fbff0ffff : PCI Bus 0030:01
6200020000000-62000207fffff : 0030:01:00.0
6200000000000-6203fbff0ffff : PCI Bus 0030:02A resource that are assigned into the resource tree must remain unchanged. Thus, release such a resource before attempting to restore and claim it back.
For simplicity, always do the release and claim back for the resource even in the cases where it is restored to the same address range.
Note: this fix may "break" some cases where devices "worked" because the resource tree corruption allowed address space double counting to fit more resource than what can now be assigned without double counting. The upcoming changes to BAR resizing should address those scenarios (to the extent possible).
Stable-dep-of: ee7471fe968d ("PCI: Skip Resizable BAR restore on read error")
--- drivers/pci/setup-bus.c | 5 +++++ 1 file changed, 5 insertions(+)
drivers/pci/setup-bus.c +5 -0
@@ -2314,6 +2314,11 @@ cleanup: bridge = dev_res->dev; i = res - bridge->resource; +if (res->parent) { +release_child_resources (res); +pci_release_resource (bridge, i); + } + res->start = dev_res->start; res->end = dev_res->end; res->flags = dev_res->flags;
Greg Kroah-Hartman
maintainerPATCH 971/1067 PCI: Free saved list without holding pci_bus_sem
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ilpo Järvinen <[email protected]>
[ Upstream commit 1d8a0506f69895b7cfd9d5c4546761c508231a8a ]
Freeing the saved list does not require holding pci_bus_sem, so the critical section can be made shorter.
Stable-dep-of: ee7471fe968d ("PCI: Skip Resizable BAR restore on read error")
--- drivers/pci/setup-bus.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/pci/setup-bus.c +1 -1
@@ -2326,8 +2326,8 @@ cleanup:pci_claim_resource (bridge, i);pci_setup_bridge (bridge->subordinate); } -free_list (&saved);up_read (&pci_bus_sem); +free_list (&saved);return ret; }
Greg Kroah-Hartman
maintainerPATCH 972/1067 PCI: Fix restoring BARs on BAR resize rollback path
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ilpo Järvinen <[email protected]>
[ Upstream commit 337b1b566db087347194e4543ddfdfa5645275cc ]
BAR resize operation is implemented in the pci_resize_resource() and pbus_reassign_bridge_resources() functions. pci_resize_resource() can be called either from __resource_resize_store() from sysfs or directly by the driver for the Endpoint Device.
The pci_resize_resource() requires that caller has released the device resources that share the bridge window with the BAR to be resized as otherwise the bridge window is pinned in place and cannot be changed.
pbus_reassign_bridge_resources() rolls back resources if the resize operation fails, but rollback is performed only for the bridge windows. Because releasing the device resources are done by the caller of the BAR resize interface, these functions performing the BAR resize do not have access to the device resources as they were before the resize.
pbus_reassign_bridge_resources() could try __pci_bridge_assign_resources() after rolling back the bridge windows as they were, however, it will not guarantee the resource are assigned due to differences in how FW and the kernel assign the resources (alignment of the start address and tail).
To perform rollback robustly, the BAR resize interface has to be altered to also release the device resources that share the bridge window with the BAR to be resized.
Also, remove restoring from the entries failed list as saved list should now contain both the bridge windows and device resources so the extra restore is duplicated work.
Some drivers (currently only amdgpu) want to prevent releasing some resources. Add exclude_bars param to pci_resize_resource() and make amdgpu pass its register BAR (BAR 2 or 5), which should never be released during resize operation. Normally 64-bit prefetchable resources do not share a bridge window with the 32-bit only register BAR, but there are various fallbacks in the resource assignment logic which may make the resources share the bridge window in rare cases.
This change (together with the driver side changes) is to counter the resource releases that had to be done to prevent resource tree corruption in the ("PCI: Release assigned resource before restoring them") change. As such, it likely restores functionality in cases where device resources were released to avoid resource tree conflicts which appeared to be "working" when such conflicts were not correctly detected by the kernel.
[bhelgaas: squash amdgpu BAR selection from
https: //lore.kernel.org/r/[email protected]]
Stable-dep-of: ee7471fe968d ("PCI: Skip Resizable BAR restore on read error")
--- drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 4 - drivers/gpu/drm/i915/gt/intel_region_lmem.c | 2 drivers/pci/pci-sysfs.c | 14 --- drivers/pci/pci.h | 3 drivers/pci/setup-bus.c | 101 +++++++++++++++++++--------- drivers/pci/setup-res.c | 19 +---- include/linux/pci.h | 4 - 7 files changed, 87 insertions(+), 60 deletions(-)
drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +3 -1
@@ -1272,7 +1272,9 @@ int amdgpu_device_resize_fb_bar(struct apci_release_resource (adev->pdev,0 ); - r =pci_resize_resource (adev->pdev,0 , rbar_size); + r =pci_resize_resource (adev->pdev,0 , rbar_size, + (adev->asic_type >=CHIP_BONAIRE ) ?1 <<5 + :1 <<2 );if (r == -ENOSPC )DRM_INFO ("Not enough PCI address space for a large BAR." );else if (r && r != -ENOTSUPP )
drivers/gpu/drm/i915/gt/intel_region_lmem.c +1 -1
@@ -37,7 +37,7 @@ _resize_bar(struct drm_i915_private *i91_release_bars (pdev); - ret =pci_resize_resource (pdev, resno, bar_size); + ret =pci_resize_resource (pdev, resno, bar_size,0 );if (ret) {drm_info (&i915->drm,"Failed to resize BAR%d to %dM (%pe)\n" , resno,1 << bar_size,ERR_PTR (ret));
drivers/pci/pci-sysfs.c +3 -11
@@ -1455,8 +1455,8 @@ static ssize_t resource##n##_resize_stor const char *buf, size_t count)\ { \struct pci_dev *pdev =to_pci_dev (dev); \ -unsigned long size, flags; \ -int ret, i; \ +unsigned long size; \ +int ret; \u16 cmd; \ \if (kstrtoul (buf,0 , &size) <0 ) \
@@ -1481,17 +1481,9 @@ static ssize_t resource##n##_resize_storpci_write_config_word (pdev,PCI_COMMAND , \ cmd & ~PCI_COMMAND_MEMORY ); \ \ - flags =pci_resource_flags (pdev, n); \ - \pci_remove_resource_files (pdev); \ \ -for (i =0 ; i <PCI_STD_NUM_BARS ; i++) { \ -if (pci_resource_len (pdev, i) && \ -pci_resource_flags (pdev, i) == flags) \ -pci_release_resource (pdev, i); \ - } \ - \ - ret =pci_resize_resource (pdev, n, size); \ + ret =pci_resize_resource (pdev, n, size,0 ); \ \pci_assign_unassigned_bus_resources (pdev->bus); \ \
drivers/pci/pci.h +3 -0
@@ -245,6 +245,9 @@ enum pci_bar_type {struct device *pci_get_host_bridge_device (struct pci_dev *dev);void pci_put_host_bridge_device (struct device *dev); +int pci_do_resource_release_and_resize (struct pci_dev *dev,int resno,int size, +int exclude_bars); +int pci_configure_extended_tags (struct pci_dev *dev,void *ign);bool pci_bus_read_dev_vendor_id (struct pci_bus *bus,int devfn,u32 *pl,int crs_timeout);
drivers/pci/setup-bus.c +70 -31
164 lines not on this page · raw message
drivers/pci/setup-res.c +5 -14
@@ -427,9 +427,9 @@ void pci_release_resource(struct pci_dev }EXPORT_SYMBOL (pci_release_resource); -int pci_resize_resource (struct pci_dev *dev,int resno,int size) +int pci_resize_resource (struct pci_dev *dev,int resno,int size, +int exclude_bars) { -struct resource *res = dev->resource + resno;struct pci_host_bridge *host;int old, ret;u32 sizes;
@@ -440,10 +440,6 @@ int pci_resize_resource(struct pci_dev *if (host->preserve_config)return -ENOTSUPP ; -/* Make sure the resource isn't assigned before resizing it. */ -if (!(res->flags &IORESOURCE_UNSET )) -return -EBUSY ; -pci_read_config_word (dev,PCI_COMMAND , &cmd);if (cmd &PCI_COMMAND_MEMORY )return -EBUSY ;
@@ -463,19 +459,14 @@ int pci_resize_resource(struct pci_dev *if (ret)return ret; - res->end = res->start +pci_rebar_size_to_bytes (size) -1 ; + ret =pci_do_resource_release_and_resize (dev, resno, size, exclude_bars); +if (ret) + goto error_resize; -/* Check if the new config works by trying to assign everything. */ -if (dev->bus->self) { - ret =pci_reassign_bridge_resources (dev->bus->self, res->flags); -if (ret) - goto error_resize; - }return 0 ; error_resize:pci_rebar_set_size (dev, resno, old); - res->end = res->start +pci_rebar_size_to_bytes (old) -1 ;return ret; }EXPORT_SYMBOL (pci_resize_resource);
include/linux/pci.h +2 -2
@@ -1380,7 +1380,8 @@ static inline int pci_rebar_bytes_to_siz }u32 pci_rebar_get_possible_sizes (struct pci_dev *pdev,int bar); -int __must_check pci_resize_resource (struct pci_dev *dev,int i,int size); +int __must_check pci_resize_resource (struct pci_dev *dev,int i,int size, +int exclude_bars);int pci_select_bars (struct pci_dev *dev,unsigned long flags);bool pci_device_is_present (struct pci_dev *pdev);void pci_ignore_hotplug (struct pci_dev *dev);
@@ -1450,7 +1451,6 @@ void pci_assign_unassigned_resources(voivoid pci_assign_unassigned_bridge_resources (struct pci_dev *bridge);void pci_assign_unassigned_bus_resources (struct pci_bus *bus);void pci_assign_unassigned_root_bus_resources (struct pci_bus *bus); -int pci_reassign_bridge_resources (struct pci_dev *bridge,unsigned long type);void pdev_enable_device (struct pci_dev *);int pci_enable_resources (struct pci_dev *,int mask);void pci_assign_irq (struct pci_dev *dev);
Greg Kroah-Hartman
maintainerPATCH 973/1067 PCI: Add kerneldoc for pci_resize_resource()
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ilpo Järvinen <[email protected]>
[ Upstream commit d787018e2dfdc4c1331538e7a8717690d1b7c9b3 ]
As pci_resize_resource() is meant to be used also outside of PCI core, document the interface with kerneldoc.
Stable-dep-of: ee7471fe968d ("PCI: Skip Resizable BAR restore on read error")
--- drivers/pci/setup-res.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+)
drivers/pci/setup-res.c +20 -0
@@ -427,6 +427,26 @@ void pci_release_resource(struct pci_dev }EXPORT_SYMBOL (pci_release_resource); +/** +* pci_resize_resource - reconfigure a Resizable BAR and resources +* @dev: the PCI device +* @resno: index of the BAR to be resized +* @size: new size as defined in the spec (0=1MB, 31=128TB) +* @exclude_bars: a mask of BARs that should not be released +* +* Reconfigure @resno to @size and re-run resource assignment algorithm +* with the new size. +* +* Prior to resize, release @dev resources that share a bridge window with +* @resno. This unpins the bridge window resource to allow changing it. +* +* The caller may prevent releasing a particular BAR by providing +* @exclude_bars mask, but this may result in the resize operation failing +* due to insufficient space. +* +* Return: 0 on success, or negative on error. In case of an error, the +* resources are restored to their original places. +*/ int pci_resize_resource (struct pci_dev *dev,int resno,int size,int exclude_bars) {
Greg Kroah-Hartman
maintainerPATCH 974/1067 PCI: Move Resizable BAR code to rebar.c
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ilpo Järvinen <[email protected]>
[ Upstream commit 9f71938cd77f32a448f40a288e409eca60e55486 ]
For lack of a better place to put it, Resizable BAR code has been placed inside pci.c and setup-res.c that do not use it for anything. Upcoming changes are going to add more Resizable BAR related functions, increasing the code size.
As pci.c is huge as is, move the Resizable BAR related code and the BAR resize code from setup-res.c to rebar.c.
Stable-dep-of: ee7471fe968d ("PCI: Skip Resizable BAR restore on read error")
--- Documentation/driver-api/pci/pci.rst | 3 drivers/pci/Makefile | 2 drivers/pci/pci.c | 136 ---------------------- drivers/pci/pci.h | 1 drivers/pci/rebar.c | 212 +++++++++++++++++++++++++++++++++++ drivers/pci/setup-res.c | 64 ---------- 6 files changed, 217 insertions(+), 201 deletions(-) create mode 100644 drivers/pci/rebar.c
Documentation/driver-api/pci/pci.rst +3 -0
@@ -31,6 +31,9 @@ PCI Support Library .. kernel-doc:: drivers/pci/slot.c :export: +.. kernel-doc:: drivers/pci/rebar.c + :export: + .. kernel-doc:: drivers/pci/rom.c :export:
drivers/pci/Makefile +1 -1
@@ -4,7 +4,7 @@ obj-$(CONFIG_PCI) += access.o bus.o probe.o host-bridge.o \ remove.o pci.o pci-driver.o search.o \ - pci-sysfs.o rom.o setup-res.o irq.o vpd.o \ + pci-sysfs.o rebar.o rom.o setup-res.o irq.o vpd.o \ setup-bus.o vc.o mmap.o setup-irq.o obj-$(CONFIG_PCI) += msi/
drivers/pci/pci.c +0 -136
148 lines not on this page · raw message
drivers/pci/pci.h +1 -0
@@ -615,6 +615,7 @@ static inline int acpi_get_rc_resources( }#endif +void pci_restore_rebar_state (struct pci_dev *pdev);int pci_rebar_get_current_size (struct pci_dev *pdev,int bar);int pci_rebar_set_size (struct pci_dev *pdev,int bar,int size);static inline u64 pci_rebar_size_to_bytes (int size)
drivers/pci/rebar.c new file +212 -0
212 lines not on this page · raw message
drivers/pci/setup-res.c +0 -64
70 lines not on this page · raw message
Greg Kroah-Hartman
maintainerPATCH 975/1067 PCI: Skip Resizable BAR restore on read error
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Marco Nenciarini <[email protected]>
[ Upstream commit ee7471fe968d210939be9046089a924cd23c8c3b ]
pci_restore_rebar_state() uses the Resizable BAR Control register to decide how many BARs to restore (nbars) and which BAR each iteration addresses (bar_idx).
When a device does not respond, config reads typically return PCI_ERROR_RESPONSE (~0). Both fields are 3 bits wide, so nbars and bar_idx both evaluate to 7, past the spec's valid ranges for both fields. pci_resource_n() then returns an unrelated resource slot, whose size is used to derive a nonsensical value written back to the Resizable BAR Control register.
Bail out if any Resizable BAR Control read returns PCI_ERROR_RESPONSE. No further BARs are touched, which is safe because a config read that returns PCI_ERROR_RESPONSE indicates the device is unreachable and restoration is pointless.
--- drivers/pci/rebar.c | 6 ++++++ 1 file changed, 6 insertions(+)
drivers/pci/rebar.c +6 -0
@@ -131,6 +131,9 @@ void pci_restore_rebar_state(struct pci_return ;pci_read_config_dword (pdev, pos +PCI_REBAR_CTRL , &ctrl); +if (PCI_POSSIBLE_ERROR (ctrl)) +return ; + nbars =FIELD_GET (PCI_REBAR_CTRL_NBAR_MASK , ctrl);for (i =0 ; i < nbars; i++, pos +=8 ) {
@@ -138,6 +141,9 @@ void pci_restore_rebar_state(struct pci_int bar_idx, size;pci_read_config_dword (pdev, pos +PCI_REBAR_CTRL , &ctrl); +if (PCI_POSSIBLE_ERROR (ctrl)) +return ; + bar_idx = ctrl &PCI_REBAR_CTRL_BAR_IDX ; res =pci_resource_n (pdev, bar_idx); size =pci_rebar_bytes_to_size (resource_size (res));
Greg Kroah-Hartman
maintainerPATCH 976/1067 mm: shrinker: remove redundant shrinker_rwsem in debugfs operations
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Qi Zheng <[email protected]>
[ Upstream commit 1dd49e58f966b1eecd935dc28458a8369ae94ad1 ]
debugfs_remove_recursive() will wait for debugfs_file_put() to return, so the shrinker will not be freed when doing debugfs operations (such as shrinker_debugfs_count_show() and shrinker_debugfs_scan_write()), so there is no need to hold shrinker_rwsem during debugfs operations.
Stable-dep-of: b902890c62d2 ("mm/shrinker: do not hold RCU lock in shrinker_debugfs_count_show()")
--- mm/shrinker_debug.c | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-)
mm/shrinker_debug.c +1 -15
@@ -49,17 +49,12 @@ static int shrinker_debugfs_count_show(sstruct mem_cgroup *memcg;unsigned long total;bool memcg_aware; -int ret, nid; +int ret =0 , nid; count_per_node =kcalloc (nr_node_ids,sizeof (unsigned long ),GFP_KERNEL );if (!count_per_node)return -ENOMEM ; - ret =down_read_killable (&shrinker_rwsem); -if (ret) { -kfree (count_per_node); -return ret; - }rcu_read_lock (); memcg_aware = shrinker->flags &SHRINKER_MEMCG_AWARE ;
@@ -92,7 +87,6 @@ static int shrinker_debugfs_count_show(s }while ((memcg =mem_cgroup_iter (NULL , memcg,NULL )) !=NULL );rcu_read_unlock (); -up_read (&shrinker_rwsem);kfree (count_per_node);return ret;
@@ -117,7 +111,6 @@ static ssize_t shrinker_debugfs_scan_wristruct mem_cgroup *memcg =NULL ;int nid;char kbuf[72 ]; -ssize_t ret; read_len = size < (sizeof (kbuf) -1 ) ? size : (sizeof (kbuf) -1 );if (copy_from_user (kbuf, buf, read_len))
@@ -146,12 +139,6 @@ static ssize_t shrinker_debugfs_scan_wrireturn -EINVAL ; } - ret =down_read_killable (&shrinker_rwsem); -if (ret) { -mem_cgroup_put (memcg); -return ret; - } - sc.nid = nid; sc.memcg = memcg; sc.nr_to_scan = nr_to_scan;
@@ -159,7 +146,6 @@ static ssize_t shrinker_debugfs_scan_wri shrinker->scan_objects (shrinker, &sc); -up_read (&shrinker_rwsem);mem_cgroup_put (memcg);return size;
Greg Kroah-Hartman
maintainerPATCH 977/1067 mm/shrinker: do not hold RCU lock in shrinker_debugfs_count_show()
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Shakeel Butt <[email protected]>
[ Upstream commit b902890c62d200b3509cb5e09cf1e0a66553c128 ]
Reading the debugfs "count" file of a memcg-aware shrinker can sleep inside an RCU read-side critical section:
BUG: sleeping function called from invalid context at kernel/cgroup/rstat.c:421 RCU nest depth: 1, expected: 0 css_rstat_flush mem_cgroup_flush_stats zswap_shrinker_count shrinker_debugfs_count_show
shrinker_debugfs_count_show() invokes the ->count_objects() callback under rcu_read_lock(). The zswap callback flushes memcg stats via css_rstat_flush(), which may sleep, so it must not run under RCU.
The RCU lock is not needed here. mem_cgroup_iter() takes RCU internally and returns a memcg holding a css reference (dropped on the next iteration or by mem_cgroup_iter_break()), so the memcg stays alive without it. The shrinker is kept alive by the open debugfs file: shrinker_free() removes the debugfs entries via debugfs_remove_recursive(), which waits for in-flight readers to drain, before call_rcu(..., shrinker_free_rcu_cb). The sibling "scan" handler already invokes the sleeping ->scan_objects() callback with no RCU section.
Drop the rcu_read_lock()/rcu_read_unlock().
--- mm/shrinker_debug.c | 4 ---- 1 file changed, 4 deletions(-)
mm/shrinker_debug.c +0 -4
@@ -55,8 +55,6 @@ static int shrinker_debugfs_count_show(sif (!count_per_node)return -ENOMEM ; -rcu_read_lock (); - memcg_aware = shrinker->flags &SHRINKER_MEMCG_AWARE ; memcg =mem_cgroup_iter (NULL ,NULL ,NULL );
@@ -86,8 +84,6 @@ static int shrinker_debugfs_count_show(s } }while ((memcg =mem_cgroup_iter (NULL , memcg,NULL )) !=NULL ); -rcu_read_unlock (); -kfree (count_per_node);return ret; }
Greg Kroah-Hartman
maintainerPATCH 978/1067 coresight: etb10: restore atomic_t for shared reading state
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Runyu Xiao <[email protected]>
[ Upstream commit fa09f08ede3db3050ae16ae1ed92c902d0cada23 ]
The etb10 miscdevice uses drvdata->reading as a shared exclusivity gate for userspace buffer access. etb_open() claims that gate with local_cmpxchg(), and etb_release() clears it with local_set().
That gate is shared per-device state rather than CPU-local state. A running system can reach it whenever /dev/<etb> is opened, closed, and reopened by different tasks while the device remains registered, so the same drvdata->reading variable may be claimed on one CPU and later cleared on another.
This code used to use atomic_t for the same gate, but commit 27b10da8fff2 ("coresight: etb10: moving to local atomic operations") changed it to local_t even though the access pattern remained cross-task and cross-CPU. Restore atomic_t together with atomic_cmpxchg() and atomic_set() so the exclusivity gate again uses a primitive intended for shared state.
The issue was found on Linux v6.18.21 by our static analysis tool while scanning surviving local_t-on-shared-state sites, and then manually reviewed against the live etb10 file-op path.
It was runtime-validated with a reproducible QEMU no-device KCSAN PoC that kept the same report-local contract:
1. use one shared struct etb_drvdata carrier and its
drvdata->reading gate;
2. call etb_open() and etb_release() sequentially on that gate to
confirm the original claim/clear path;
3. bind the open side to CPU0 and the release side to CPU1 for the
same gate to show cross-CPU ownership; 4. run bound workers that repeatedly race etb_open() and etb_release() on the same gate until KCSAN reports a target hit.
The harness recorded:
L1 passed open=1 release=1
reading_after_open=1 reading_after_release=0
L2 passed open_cpu=0 release_cpu=1
cross_cpu_release=1 reading_after=0 open_ret=0
Representative KCSAN excerpt from the no-device validation run:
BUG: KCSAN: data-race in etb_open.constprop.0.isra.0 [vuln_msv]
write to 0xffffffffc0003810 of 4 bytes by task 216 on cpu 1:
etb_open.constprop.0.isra.0+0x38/0x80 [vuln_msv] l3_worker_thread_fn+0x4f/0xf0 [vuln_msv] kthread+0x17e/0x1c0 ret_from_fork+0x22/0x30
read to 0xffffffffc0003810 of 4 bytes by task 215 on cpu 0:
etb_open.constprop.0.isra.0+0x18/0x80 [vuln_msv] l3_worker_thread_fn+0x4f/0xf0 [vuln_msv] kthread+0x17e/0x1c0 ret_from_fork+0x22/0x30
value changed: 0x00000000 -> 0x00000001
Reported by Kernel Concurrency Sanitizer on:
CPU: 0 PID: 215 Comm: etb10_l3_a Tainted: G O 6.1.66 #2
This no-device harness is not a real ETB10 hardware end-to-end run, but it preserves the same shared drvdata->reading gate and the same etb_open()/etb_release() claim/clear contract. No real ETB10 hardware was available for runtime testing.
Build-tested with:
make olddefconfig
make -j"$(nproc)" drivers/hwtracing/coresight/coresight-etb10.o
--- drivers/hwtracing/coresight/coresight-etb10.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
drivers/hwtracing/coresight/coresight-etb10.c +3 -3
@@ -86,7 +86,7 @@ struct etb_drvdata {struct coresight_device *csdev;struct miscdevice miscdev;spinlock_t spinlock; -local_t reading; +atomic_t reading;pid_t pid;u8 *buf;u32 mode;
@@ -603,7 +603,7 @@ static int etb_open(struct inode *inode,struct etb_drvdata *drvdata =container_of (file->private_data, struct etb_drvdata, miscdev); -if (local_cmpxchg (&drvdata->reading,0 ,1 )) +if (atomic_cmpxchg (&drvdata->reading,0 ,1 ))return -EBUSY ;dev_dbg (&drvdata->csdev->dev,"%s: successfully opened\n" , __func__);
@@ -641,7 +641,7 @@ static int etb_release(struct inode *ino {struct etb_drvdata *drvdata =container_of (file->private_data, struct etb_drvdata, miscdev); -local_set (&drvdata->reading,0 ); +atomic_set (&drvdata->reading,0 );dev_dbg (&drvdata->csdev->dev,"%s: released\n" , __func__);return 0 ;
Greg Kroah-Hartman
maintainerPATCH 979/1067 gpio: sch: use raw_spinlock_t in the irq startup path
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Runyu Xiao <[email protected]>
[ Upstream commit 286533cb14a3c8a8bd39ff64ea2fc8e1aa0f638b ]
sch_irq_unmask() enables the GPIO IRQ and then updates the controller state through sch_irq_mask_unmask(), which takes sch->lock with spin_lock_irqsave(). The callback can be reached from irq_startup() while setting up a requested IRQ. That path is not sleepable, but on PREEMPT_RT a regular spinlock_t becomes a sleeping lock.
This issue was found by our static analysis tool and then manually reviewed against the current tree.
The grounded PoC kept the request_threaded_irq() -> __setup_irq() -> irq_startup() -> sch_irq_unmask() -> sch_irq_mask_unmask() carrier and used the original spin_lock_irqsave(&sch->lock) edge. Lockdep reported:
BUG: sleeping function called from invalid context hardirqs last disabled at ... __setup_irq.constprop.0 ... [vuln_msv] sch_rt_spin_lock_irqsave+0x1c/0x30 [vuln_msv] sch_irq_mask_unmask.constprop.0+0x31/0x70 [vuln_msv] __setup_irq.constprop.0+0xd/0x30 [vuln_msv]
Convert the SCH controller lock to raw_spinlock_t. The same lock is also used by the GPIO direction and value callbacks, but those critical sections only update MMIO-backed GPIO registers and do not contain sleepable operations. Keeping this register lock non-sleeping is therefore appropriate for the irqchip callbacks and does not change the GPIO-side locking contract.
--- drivers/gpio/gpio-sch.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-)
drivers/gpio/gpio-sch.c +16 -16
@@ -38,7 +38,7 @@struct sch_gpio {struct gpio_chip chip; -spinlock_t lock; +raw_spinlock_t lock;unsigned short iobase;unsigned short resume_base;
@@ -102,9 +102,9 @@ static int sch_gpio_direction_in(structstruct sch_gpio *sch =gpiochip_get_data (gc);unsigned long flags; -spin_lock_irqsave (&sch->lock, flags); +raw_spin_lock_irqsave (&sch->lock, flags);sch_gpio_reg_set (sch, gpio_num,GIO ,1 ); -spin_unlock_irqrestore (&sch->lock, flags); +raw_spin_unlock_irqrestore (&sch->lock, flags);return 0 ; }
@@ -120,9 +120,9 @@ static void sch_gpio_set(struct gpio_chistruct sch_gpio *sch =gpiochip_get_data (gc);unsigned long flags; -spin_lock_irqsave (&sch->lock, flags); +raw_spin_lock_irqsave (&sch->lock, flags);sch_gpio_reg_set (sch, gpio_num,GLV , val); -spin_unlock_irqrestore (&sch->lock, flags); +raw_spin_unlock_irqrestore (&sch->lock, flags); } static int sch_gpio_direction_out(struct gpio_chip *gc, unsigned int gpio_num,
@@ -131,9 +131,9 @@ static int sch_gpio_direction_out(structstruct sch_gpio *sch =gpiochip_get_data (gc);unsigned long flags; -spin_lock_irqsave (&sch->lock, flags); +raw_spin_lock_irqsave (&sch->lock, flags);sch_gpio_reg_set (sch, gpio_num,GIO ,0 ); -spin_unlock_irqrestore (&sch->lock, flags); +raw_spin_unlock_irqrestore (&sch->lock, flags);/* * according to the datasheet, writing to the level register has no
@@ -193,14 +193,14 @@ static int sch_irq_type(struct irq_datareturn -EINVAL ; } -spin_lock_irqsave (&sch->lock, flags); +raw_spin_lock_irqsave (&sch->lock, flags);sch_gpio_reg_set (sch, gpio_num,GTPE , rising);sch_gpio_reg_set (sch, gpio_num,GTNE , falling);irq_set_handler_locked (d, handle_edge_irq); -spin_unlock_irqrestore (&sch->lock, flags); +raw_spin_unlock_irqrestore (&sch->lock, flags);return 0 ; }
@@ -212,9 +212,9 @@ static void sch_irq_ack(struct irq_datairq_hw_number_t gpio_num =irqd_to_hwirq (d);unsigned long flags; -spin_lock_irqsave (&sch->lock, flags); +raw_spin_lock_irqsave (&sch->lock, flags);sch_gpio_reg_set (sch, gpio_num,GTS ,1 ); -spin_unlock_irqrestore (&sch->lock, flags); +raw_spin_unlock_irqrestore (&sch->lock, flags); }static void sch_irq_mask_unmask (struct gpio_chip *gc,irq_hw_number_t gpio_num,int val)
@@ -222,9 +222,9 @@ static void sch_irq_mask_unmask(struct gstruct sch_gpio *sch =gpiochip_get_data (gc);unsigned long flags; -spin_lock_irqsave (&sch->lock, flags); +raw_spin_lock_irqsave (&sch->lock, flags);sch_gpio_reg_set (sch, gpio_num,GGPE , val); -spin_unlock_irqrestore (&sch->lock, flags); +raw_spin_unlock_irqrestore (&sch->lock, flags); }static void sch_irq_mask (struct irq_data *d)
@@ -265,12 +265,12 @@ static u32 sch_gpio_gpe_handler(acpi_hanint offset;u32 ret; -spin_lock_irqsave (&sch->lock, flags); +raw_spin_lock_irqsave (&sch->lock, flags); core_status =inl (sch->iobase +CORE_BANK_OFFSET +GTS ); resume_status =inl (sch->iobase +RESUME_BANK_OFFSET +GTS ); -spin_unlock_irqrestore (&sch->lock, flags); +raw_spin_unlock_irqrestore (&sch->lock, flags); pending = (resume_status << sch->resume_base) | core_status;for_each_set_bit (offset, &pending, sch->chip.ngpio)
@@ -336,7 +336,7 @@ static int sch_gpio_probe(struct platfor pdev->name))return -EBUSY ; -spin_lock_init (&sch->lock); +raw_spin_lock_init (&sch->lock); sch->iobase = res->start; sch->chip = sch_gpio_chip; sch->chip.label =dev_name (&pdev->dev);
Greg Kroah-Hartman
maintainerPATCH 980/1067 mm: shrinkers: fix debugfs file permissions
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: John Keeping <[email protected]>
[ Upstream commit 2124f79de6a909630d1a62b01ecc32db9f967181 ]
The permissions for the files here are swapped as "count" is read-only and "scan" is write-only. While this doesn't really matter as these permissions don't stop the files being opened for reading/writing as appropriate, they are shown by "ls -l" and are confusing.
Stable-dep-of: e30453c61e18 ("mm: shrinker: fix NULL pointer dereference in debugfs")
--- mm/shrinker_debug.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
mm/shrinker_debug.c +2 -2
@@ -180,9 +180,9 @@ int shrinker_debugfs_add(struct shrinker } shrinker->debugfs_entry = entry; -debugfs_create_file ("count" ,0220 , entry, shrinker, +debugfs_create_file ("count" ,0440 , entry, shrinker, &shrinker_debugfs_count_fops); -debugfs_create_file ("scan" ,0440 , entry, shrinker, +debugfs_create_file ("scan" ,0220 , entry, shrinker, &shrinker_debugfs_scan_fops);return 0 ; }
Greg Kroah-Hartman
maintainerPATCH 981/1067 mm: shrinker: fix NULL pointer dereference in debugfs
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Qi Zheng <[email protected]>
[ Upstream commit e30453c61e185e914fde83c650e268067b140218 ]
shrinker_debugfs_add() creates both "count" and "scan" debugfs files unconditionally.
That assumes every shrinker implements both count_objects() and scan_objects(), which is not guaranteed. For example, the xen-backend shrinker sets count_objects() but leaves scan_objects() NULL, so writing to its scan file calls through a NULL function pointer and panics the kernel:
BUG: kernel NULL pointer dereference, address: 0000000000000000 RIP: 0010:0x0 Code: Unable to access opcode bytes at 0xffffffffffffffd6. Call Trace: <TASK> shrinker_debugfs_scan_write+0x12e/0x270 full_proxy_write+0x5f/0x90 vfs_write+0xde/0x420 ? filp_flush+0x75/0x90 ? filp_close+0x1d/0x30 ? do_dup2+0xb8/0x120 ksys_write+0x68/0xf0 ? filp_flush+0x75/0x90 do_syscall_64+0xb3/0x5b0 entry_SYSCALL_64_after_hwframe+0x76/0x7e
The count path has the same issue in principle if a shrinker omits count_objects().
To fix it, only create "count" and "scan" debugfs files when the corresponding callbacks are present.
--- mm/shrinker_debug.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-)
mm/shrinker_debug.c +6 -4
@@ -180,10 +180,12 @@ int shrinker_debugfs_add(struct shrinker } shrinker->debugfs_entry = entry; -debugfs_create_file ("count" ,0440 , entry, shrinker, - &shrinker_debugfs_count_fops); -debugfs_create_file ("scan" ,0220 , entry, shrinker, - &shrinker_debugfs_scan_fops); +if (shrinker->count_objects) +debugfs_create_file ("count" ,0440 , entry, shrinker, + &shrinker_debugfs_count_fops); +if (shrinker->scan_objects) +debugfs_create_file ("scan" ,0220 , entry, shrinker, + &shrinker_debugfs_scan_fops);return 0 ; }
Greg Kroah-Hartman
maintainerPATCH 982/1067 netfilter: ebtables: Use vmalloc_array() to improve code
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Qianfeng Rong <[email protected]>
[ Upstream commit 46015e6b3ea75297b28d4806564f3f692cf11861 ]
Remove array_size() calls and replace vmalloc() with vmalloc_array() to simplify the code. vmalloc_array() is also optimized better, uses fewer instructions, and handles overflow more concisely[1].
[1]: https://lore.kernel.org/lkml/[email protected]/
Stable-dep-of: cbfe53599eeb ("netfilter: ebtables: zero chainstack array")
--- net/bridge/netfilter/ebtables.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-)
net/bridge/netfilter/ebtables.c +7 -7
@@ -923,8 +923,8 @@ static int translate_table(struct net *n* if an error occurs */ newinfo->chainstack = -vmalloc (array_size (nr_cpu_ids, -sizeof (*(newinfo->chainstack)))); +vmalloc_array (nr_cpu_ids, +sizeof (*(newinfo->chainstack)));if (!newinfo->chainstack)return -ENOMEM ;for_each_possible_cpu (i) {
@@ -941,7 +941,7 @@ static int translate_table(struct net *n } } - cl_s =vmalloc (array_size (udc_cnt,sizeof (*cl_s))); + cl_s =vmalloc_array (udc_cnt,sizeof (*cl_s));if (!cl_s)return -ENOMEM ; i =0 ;/* the i'th udc */
@@ -1021,8 +1021,8 @@ static int do_replace_finish(struct net* the check on the size is done later, when we have the lock */ if (repl->num_counters) { -unsigned long size = repl->num_counters *sizeof (*counterstmp); - counterstmp =vmalloc (size); + counterstmp =vmalloc_array (repl->num_counters, +sizeof (*counterstmp));if (!counterstmp)return -ENOMEM ; }
@@ -1389,7 +1389,7 @@ static int do_update_counters(struct netif (num_counters ==0 )return -EINVAL ; - tmp =vmalloc (array_size (num_counters,sizeof (*tmp))); + tmp =vmalloc_array (num_counters,sizeof (*tmp));if (!tmp)return -ENOMEM ;
@@ -1531,7 +1531,7 @@ static int copy_counters_to_user(structif (num_counters != nentries)return -EINVAL ; - counterstmp =vmalloc (array_size (nentries,sizeof (*counterstmp))); + counterstmp =vmalloc_array (nentries,sizeof (*counterstmp));if (!counterstmp)return -ENOMEM ;
Greg Kroah-Hartman
maintainerPATCH 983/1067 netfilter: ebtables: zero chainstack array
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Florian Westphal <[email protected]>
[ Upstream commit cbfe53599eebffd188938ab6774cc41794f6f9d5 ]
sashiko reports:
looking at ebtables table
translation, could a sparse cpu_possible_mask lead to an uninitialized pointer free?
If cpu_possible_mask is sparse (for example, CPU 0 and CPU 2 are possible, but CPU 1 is not), the allocation loop skips CPU 1. If vmalloc_node() fails at CPU 2, the cleanup loop will blindly decrement and call vfree() on newinfo->chainstack[1].
Not a real-world bug, such allocation isn't expected to fail in the first place.
--- net/bridge/netfilter/ebtables.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
net/bridge/netfilter/ebtables.c +1 -2
@@ -923,8 +923,7 @@ static int translate_table(struct net *n* if an error occurs */ newinfo->chainstack = -vmalloc_array (nr_cpu_ids, -sizeof (*(newinfo->chainstack))); +vcalloc (nr_cpu_ids,sizeof (*(newinfo->chainstack)));if (!newinfo->chainstack)return -ENOMEM ;for_each_possible_cpu (i) {
Greg Kroah-Hartman
maintainerPATCH 984/1067 Bluetooth: Make handle of hci_conn be unique
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ziyang Xuan <[email protected]>
[ Upstream commit 181a42edddf51d5d9697ecdf365d72ebeab5afb0 ]
The handle of new hci_conn is always HCI_CONN_HANDLE_MAX + 1 if the handle of the first hci_conn entry in hci_dev->conn_hash->list is not HCI_CONN_HANDLE_MAX + 1. Use ida to manage the allocation of hci_conn->handle to make it be unique.
Stable-dep-of: 6fef032af009 ("Bluetooth: L2CAP: Fix use-after-free in l2cap_sock_new_connection_cb()")
--- include/net/bluetooth/hci_core.h | 8 ++++- net/bluetooth/amp.c | 3 -- net/bluetooth/hci_conn.c | 45 +++++++++++++++++++++++-------- net/bluetooth/hci_core.c | 3 ++ net/bluetooth/hci_event.c | 56 ++++++++++++++++++++++++--------------- 5 files changed, 79 insertions(+), 36 deletions(-)
include/net/bluetooth/hci_core.h +6 -2
@@ -322,8 +322,8 @@ struct adv_monitor {#define HCI_MAX_SHORT_NAME_LENGTH 10 -#define HCI_CONN_HANDLE_UNSET 0xffff#define HCI_CONN_HANDLE_MAX 0x0eff +#define HCI_CONN_HANDLE_UNSET (_handle) (_handle > HCI_CONN_HANDLE_MAX)/* Min encryption key size to match with SMP */ #define HCI_MIN_ENC_KEY_SIZE 7
@@ -352,6 +352,8 @@ struct hci_dev {struct srcu_struct srcu;struct mutex lock; +struct ida unset_handle_ida; +const char *name;unsigned long flags;__u16 id;
@@ -1332,7 +1334,9 @@ bool hci_iso_setup_path(struct hci_connint hci_le_create_cis (struct hci_conn *conn);struct hci_conn *hci_conn_add (struct hci_dev *hdev,int type,bdaddr_t *dst, -u8 role); +u8 role,u16 handle); +struct hci_conn *hci_conn_add_unset (struct hci_dev *hdev,int type, +bdaddr_t *dst,u8 role);int hci_conn_del (struct hci_conn *conn);void hci_conn_hash_flush (struct hci_dev *hdev);void hci_conn_check_pending (struct hci_dev *hdev);
net/bluetooth/amp.c +1 -2
@@ -109,7 +109,7 @@ struct hci_conn *phylink_add(struct hci_struct hci_conn *hcon;u8 role = out ?HCI_ROLE_MASTER :HCI_ROLE_SLAVE ; - hcon =hci_conn_add (hdev,AMP_LINK , dst, role); + hcon =hci_conn_add (hdev,AMP_LINK , dst, role,__next_handle (mgr));if (!hcon)return NULL ;
@@ -117,7 +117,6 @@ struct hci_conn *phylink_add(struct hci_ hcon->state =BT_CONNECT ; hcon->attempt++; - hcon->handle =__next_handle (mgr); hcon->remote_id = remote_id; hcon->amp_mgr =amp_mgr_get (mgr);
net/bluetooth/hci_conn.c +34 -11
121 lines not on this page · raw message
net/bluetooth/hci_core.c +3 -0
@@ -2525,6 +2525,8 @@ struct hci_dev *hci_alloc_dev_priv(int smutex_init (&hdev->lock);mutex_init (&hdev->req_lock); +ida_init (&hdev->unset_handle_ida); +INIT_LIST_HEAD (&hdev->mesh_pending);INIT_LIST_HEAD (&hdev->mgmt_pending);INIT_LIST_HEAD (&hdev->reject_list);
@@ -2784,6 +2786,7 @@ void hci_release_dev(struct hci_dev *hdemsft_release (hdev);hci_dev_unlock (hdev); +ida_destroy (&hdev->unset_handle_ida);ida_simple_remove (&hci_index_ida, hdev->id);kfree_skb (hdev->sent_cmd);kfree_skb (hdev->recv_event);
net/bluetooth/hci_event.c +35 -21
157 lines not on this page · raw message
Greg Kroah-Hartman
maintainerPATCH 985/1067 Bluetooth: Remove BT_HS
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Luiz Augusto von Dentz <[email protected]>
[ Upstream commit e7b02296fb400ee64822fbdd81a0718449066333 ]
High Speed, Alternate MAC and PHY (AMP) extension, has been removed from Bluetooth Core specification on 5.3:
https://www.bluetooth.com/blog/new-core-specification-v5-3-feature-enhancements/
Stable-dep-of: 6fef032af009 ("Bluetooth: L2CAP: Fix use-after-free in l2cap_sock_new_connection_cb()")
--- include/net/bluetooth/hci.h | 1 include/net/bluetooth/l2cap.h | 42 - net/bluetooth/Kconfig | 8 net/bluetooth/Makefile | 1 net/bluetooth/a2mp.c | 1054 ----------------------------------------- net/bluetooth/a2mp.h | 154 ------ net/bluetooth/amp.c | 590 ----------------------- net/bluetooth/amp.h | 61 -- net/bluetooth/hci_conn.c | 4 net/bluetooth/hci_event.c | 2 net/bluetooth/l2cap_core.c | 1069 ------------------------------------------ net/bluetooth/l2cap_sock.c | 18 net/bluetooth/mgmt.c | 73 -- 13 files changed, 20 insertions(+), 3057 deletions(-) delete mode 100644 net/bluetooth/a2mp.c delete mode 100644 net/bluetooth/a2mp.h delete mode 100644 net/bluetooth/amp.c delete mode 100644 net/bluetooth/amp.h
include/net/bluetooth/hci.h +0 -1
@@ -382,7 +382,6 @@ enum {HCI_LIMITED_PRIVACY ,HCI_RPA_EXPIRED ,HCI_RPA_RESOLVING , -HCI_HS_ENABLED ,HCI_LE_ENABLED ,HCI_ADVERTISING , HCI_ADVERTISING_CONNECTABLE,
include/net/bluetooth/l2cap.h +0 -42
90 lines not on this page · raw message
net/bluetooth/Kconfig +0 -8
@@ -62,14 +62,6 @@ source "net/bluetooth/cmtp/Kconfig" source "net/bluetooth/hidp/Kconfig" -config BT_HS - bool "Bluetooth High Speed (HS) features" - depends on BT_BREDR - help - Bluetooth High Speed includes support for off-loading - Bluetooth connections via 802.11 (wifi) physical layer - available with Bluetooth version 3.0 or later. - config BT_LE bool "Bluetooth Low Energy (LE) features" depends on BT
net/bluetooth/Makefile +0 -1
@@ -19,7 +19,6 @@ bluetooth-y := af_bluetooth.o hci_core.o bluetooth-$(CONFIG_BT_BREDR) += sco.o bluetooth-$(CONFIG_BT_LE) += iso.o -bluetooth-$(CONFIG_BT_HS) += a2mp.o amp.o bluetooth-$(CONFIG_BT_LEDS) += leds.o bluetooth-$(CONFIG_BT_MSFTEXT) += msft.o bluetooth-$(CONFIG_BT_AOSPEXT) += aosp.o
net/bluetooth/a2mp.c deleted +0 -1054
1,054 lines not on this page · raw message
net/bluetooth/a2mp.h deleted +0 -154
154 lines not on this page · raw message
net/bluetooth/amp.c deleted +0 -590
590 lines not on this page · raw message
net/bluetooth/amp.h deleted +0 -61
61 lines not on this page · raw message
net/bluetooth/hci_conn.c +0 -4
@@ -35,7 +35,6 @@#include "hci_request.h" #include "smp.h" -#include "a2mp.h" #include "eir.h" struct sco_param {
@@ -1162,9 +1161,6 @@ int hci_conn_del(struct hci_conn *conn) } } -if (conn->amp_mgr) -amp_mgr_put (conn->amp_mgr); -skb_queue_purge (&conn->data_q);/* Remove the connection from the list and cleanup its remaining
net/bluetooth/hci_event.c +0 -2
@@ -35,8 +35,6 @@#include "hci_request.h" #include "hci_debugfs.h" #include "hci_codec.h" -#include "a2mp.h" -#include "amp.h" #include "smp.h" #include "msft.h" #include "eir.h"
net/bluetooth/l2cap_core.c +18 -1051
999 lines not on this page · raw message
net/bluetooth/l2cap_sock.c +1 -17
Lines of this file are not shown. View the raw message.
net/bluetooth/mgmt.c +1 -72
73 lines not on this page · raw message
The rest of this diff is not shown. View the raw message.
Part of the diff is not shown here. View the raw message.
Greg Kroah-Hartman
maintainerPATCH 986/1067 Bluetooth: L2CAP: Fix slab-use-after-free in l2cap_connect()
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sungwoo Kim <[email protected]>
[ Upstream commit 4d7b41c0e43995b0e992b9f8903109275744b658 ]
Extend a critical section to prevent chan from early freeing. Also make the l2cap_connect() return type void. Nothing is using the returned value but it is ugly to return a potentially freed pointer. Making it void will help with backports because earlier kernels did use the return value. Now the compile will break for kernels where this patch is not a complete fix.
Call stack summary:
[use]l2cap_bredr_sig_cmd
l2cap_connect
┌ mutex_lock(&conn->chan_lock);
│ chan = pchan->ops->new_connection(pchan); <- alloc chan
│ __l2cap_chan_add(conn, chan);
│ l2cap_chan_hold(chan);
│ list_add(&chan->list, &conn->chan_l); ... (1)
└ mutex_unlock(&conn->chan_lock);
chan->conf_state ... (4) <- use after free[free]l2cap_conn_del
┌ mutex_lock(&conn->chan_lock); │ foreach chan in conn->chan_l: ... (2) │ l2cap_chan_put(chan); │ l2cap_chan_destroy │ kfree(chan) ... (3) <- chan freed └ mutex_unlock(&conn->chan_lock);
==================================================================
BUG: KASAN: slab-use-after-free in instrument_atomic_read include/linux/instrumented.h:68 [inline] BUG: KASAN: slab-use-after-free in _test_bit include/asm-generic/bitops/instrumented-non-atomic.h:141 [inline] BUG: KASAN: slab-use-after-free in l2cap_connect+0xa67/0x11a0 net/bluetooth/l2cap_core.c:4260 Read of size 8 at addr ffff88810bf040a0 by task kworker/u3:1/311
Stable-dep-of: 6fef032af009 ("Bluetooth: L2CAP: Fix use-after-free in l2cap_sock_new_connection_cb()")
--- net/bluetooth/l2cap_core.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-)
net/bluetooth/l2cap_core.c +10 -11
@@ -3985,13 +3985,12 @@ static inline int l2cap_command_rej(strureturn 0 ; } -static struct l2cap_chan *l2cap_connect (struct l2cap_conn *conn, -struct l2cap_cmd_hdr *cmd, -u8 *data,u8 rsp_code,u8 amp_id) +static void l2cap_connect (struct l2cap_conn *conn,struct l2cap_cmd_hdr *cmd, +u8 *data,u8 rsp_code,u8 amp_id) {struct l2cap_conn_req *req = (struct l2cap_conn_req *) data;struct l2cap_conn_rsp rsp; -struct l2cap_chan *chan =NULL , *pchan; +struct l2cap_chan *chan =NULL , *pchan =NULL ;int result, status =L2CAP_CS_NO_INFO ;u16 dcid =0 , scid =__le16_to_cpu (req->scid);
@@ -4004,7 +4003,7 @@ static struct l2cap_chan *l2cap_connect( &conn->hcon->dst, ACL_LINK);if (!pchan) { result =L2CAP_CR_BAD_PSM ; - goto sendresp; + goto response; }mutex_lock (&conn->chan_lock);
@@ -4092,17 +4091,15 @@ static struct l2cap_chan *l2cap_connect( } response: -l2cap_chan_unlock (pchan); -mutex_unlock (&conn->chan_lock); -l2cap_chan_put (pchan); - -sendresp: rsp.scid =cpu_to_le16 (scid); rsp.dcid =cpu_to_le16 (dcid); rsp.result =cpu_to_le16 (result); rsp.status =cpu_to_le16 (status);l2cap_send_cmd (conn, cmd->ident, rsp_code,sizeof (rsp), &rsp); +if (!pchan) +return ; +if (result ==L2CAP_CR_PEND && status ==L2CAP_CS_NO_INFO ) {struct l2cap_info_req info; info.type =cpu_to_le16 (L2CAP_IT_FEAT_MASK );
@@ -4125,7 +4122,9 @@ sendresp: chan->num_conf_req++; } -return chan; +l2cap_chan_unlock (pchan); +mutex_unlock (&conn->chan_lock); +l2cap_chan_put (pchan); } static int l2cap_connect_req(struct l2cap_conn *conn,
Greg Kroah-Hartman
maintainerPATCH 987/1067 Bluetooth: 6lowpan: fix cyclic locking warning on netdev unregister
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pauli Virtanen <[email protected]>
[ Upstream commit 9707a015fe8f3ba8ec7c270f3b2b8efb38823d6b ]
6lowpan.c has theoretically conflicting lock orderings, which lockdep complains about:
a) rtnl_lock > hdev->workqueue
from 6lowpan.c:delete_netdev -> rtnl_lock -> device_del
-> put_device(parent) -> hci_release_dev -> destroy_workqueue
b) hdev->workqueue > l2cap_conn->lock > chan->lock > rtnl_lock
from hci_rx_work -> 6lowpan.c:chan_ready_cb
-> lowpan_register_netdev, ifup -> rtnl_lockActual deadlock appears not possible, as hci_rx_work is disabled and l2cap_conn flushed already on hdev unregister. Hence, do minimal thing to make lockdep happy by breaking chain a) by holding hdev refcount until after netdev put in 6lowpan.c.
Fixes the lockdep complaint:
WARNING: possible circular locking dependency detected.
kworker/0:1/11 is trying to acquire lock:ffff8880023b3940 ((wq_completion)hci0#2){+.+.}-{0:0}, at: touch_wq_lockdep_map+0x8b/0x130 but task is already holding lock:ffffffff95e4f9c0 (rtnl_mutex){+.+.}-{4:4}, at: lowpan_unregister_netdev+0xd/0x30
Workqueue: events delete_netdev
Stable-dep-of: 6fef032af009 ("Bluetooth: L2CAP: Fix use-after-free in l2cap_sock_new_connection_cb()")
--- net/bluetooth/6lowpan.c | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-)
net/bluetooth/6lowpan.c +23 -2
@@ -759,13 +759,33 @@ static inline struct l2cap_chan *chan_nereturn chan; } +static void unregister_dev (struct lowpan_btle_dev *dev) +{ +struct hci_dev *hdev =READ_ONCE (dev->hdev); + +/* If netdev holds last reference to hci_dev (its parent device), this +* leads to theoretical cyclic locking on lowpan_unregister_netdev: +* +* rtnl_lock -> put_device(parent) -> hci_release_dev -> +* destroy_workqueue -> hci_rx_work -> l2cap_recv_acldata -> +* chan_ready_cb -> ifup -> rtnl_lock +* +* However, hci_rx_work is disabled in hci_unregister_dev, so this +* should not occur. Make lockdep happy by postponing hdev release after +* netdev put. +*/ +hci_dev_hold (hdev); +lowpan_unregister_netdev (dev->netdev); +hci_dev_put (hdev); +} +static void delete_netdev (struct work_struct *work) {struct lowpan_btle_dev *entry =container_of (work, struct lowpan_btle_dev, delete_netdev); -lowpan_unregister_netdev (entry->netdev); +unregister_dev (entry);/* The entry pointer is deleted by the netdev destructor. */ }
@@ -1245,6 +1265,7 @@ static void disconnect_devices(void)break ; new_dev->netdev = entry->netdev; + new_dev->hdev = entry->hdev;INIT_LIST_HEAD (&new_dev->list);list_add_rcu (&new_dev->list, &devices);
@@ -1256,7 +1277,7 @@ static void disconnect_devices(void)ifdown (entry->netdev);BT_DBG ("Unregistering netdev %s %p" , entry->netdev->name, entry->netdev); -lowpan_unregister_netdev (entry->netdev); +unregister_dev (entry);kfree (entry); } }
Greg Kroah-Hartman
maintainerPATCH 988/1067 Bluetooth: L2CAP: Fix use-after-free in l2cap_sock_new_connection_cb()
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Siwei Zhang <[email protected]>
[ Upstream commit 6fef032af0092ed5ccb767239a9ac1bc38c08a40 ]
l2cap_sock_new_connection_cb() returned l2cap_pi(sk)->chan after release_sock(parent). Once the parent lock is dropped the newly enqueued child socket sk is reachable via the accept queue, so another task can accept and free it before the callback dereferences sk, resulting in a use-after-free.
Rework the ->new_connection() op so the core, rather than the callback, owns the child channel's lifetime. The op now receives a pre-allocated new_chan and returns an errno instead of allocating and returning a channel. l2cap_new_connection() allocates the child channel and links it into the conn list via __l2cap_chan_add() before invoking the callback, so the conn-list reference keeps the channel alive once release_sock(parent) exposes the socket to other tasks.
Channel configuration that was duplicated in l2cap_sock_init() and the various new_connection callbacks is consolidated into l2cap_chan_set_defaults(), which now inherits from the parent channel when one is supplied.
--- include/net/bluetooth/l2cap.h | 10 ++-- net/bluetooth/6lowpan.c | 18 ------- net/bluetooth/l2cap_core.c | 78 ++++++++++++++++++++++++++----- net/bluetooth/l2cap_sock.c | 103 +++++++++++++++++++----------------------- net/bluetooth/smp.c | 27 ++--------- 5 files changed, 127 insertions(+), 109 deletions(-)
include/net/bluetooth/l2cap.h +6 -4
@@ -614,7 +614,8 @@ struct l2cap_chan {struct l2cap_ops {char *name; -struct l2cap_chan *(*new_connection) (struct l2cap_chan *chan); +int (*new_connection)(struct l2cap_chan *chan, +struct l2cap_chan *new_chan);int (*recv) (struct l2cap_chan * chan,struct sk_buff *skb);void (*teardown) (struct l2cap_chan *chan,int err);
@@ -878,9 +879,10 @@ static inline __u16 __next_seq(struct l2return (seq +1 ) % (chan->tx_win_max +1 ); } -static inline struct l2cap_chan *l2cap_chan_no_new_connection (struct l2cap_chan *chan) +static inline int l2cap_chan_no_new_connection (struct l2cap_chan *chan, +struct l2cap_chan *new_chan) { -return NULL ; +return -EOPNOTSUPP ; }static inline int l2cap_chan_no_recv (struct l2cap_chan *chan,struct sk_buff *skb)
@@ -956,7 +958,7 @@ int l2cap_chan_send(struct l2cap_chan *cvoid l2cap_chan_busy (struct l2cap_chan *chan,int busy);void l2cap_chan_rx_avail (struct l2cap_chan *chan,ssize_t rx_avail);int l2cap_chan_check_security (struct l2cap_chan *chan,bool initiator); -void l2cap_chan_set_defaults (struct l2cap_chan *chan); +void l2cap_chan_set_defaults (struct l2cap_chan *chan,struct l2cap_chan *pchan);int l2cap_ertm_init (struct l2cap_chan *chan);void l2cap_chan_add (struct l2cap_conn *conn,struct l2cap_chan *chan);void __l2cap_chan_add (struct l2cap_conn *conn,struct l2cap_chan *chan);
net/bluetooth/6lowpan.c +1 -17
@@ -631,7 +631,7 @@ static struct l2cap_chan *chan_create(voif (!chan)return NULL ; -l2cap_chan_set_defaults (chan); +l2cap_chan_set_defaults (chan,NULL ); chan->chan_type =L2CAP_CHAN_CONN_ORIENTED ; chan->mode =L2CAP_MODE_LE_FLOWCTL ;
@@ -744,21 +744,6 @@ static inline void chan_ready_cb(structifup (dev->netdev); } -static inline struct l2cap_chan *chan_new_conn_cb (struct l2cap_chan *pchan) -{ -struct l2cap_chan *chan; - - chan =chan_create (); -if (!chan) -return NULL ; - - chan->ops = pchan->ops; - -BT_DBG ("chan %p pchan %p" , chan, pchan); - -return chan; -} -static void unregister_dev (struct lowpan_btle_dev *dev) {struct hci_dev *hdev =READ_ONCE (dev->hdev);
@@ -900,7 +885,6 @@ static long chan_get_sndtimeo_cb(structstatic const struct l2cap_ops bt_6lowpan_chan_ops = { .name ="L2CAP 6LoWPAN channel" , - .new_connection = chan_new_conn_cb, .recv = chan_recv_cb, .close = chan_close_cb, .state_change = chan_state_change_cb,
net/bluetooth/l2cap_core.c +65 -13
143 lines not on this page · raw message
net/bluetooth/l2cap_sock.c +49 -54
201 lines not on this page · raw message
net/bluetooth/smp.c +6 -21
@@ -3228,34 +3228,19 @@ static const struct l2cap_ops smp_chan_o .get_sndtimeo = l2cap_chan_no_get_sndtimeo, }; -static inline struct l2cap_chan *smp_new_conn_cb (struct l2cap_chan *pchan) +static inline int smp_new_conn_cb (struct l2cap_chan *chan, +struct l2cap_chan *new_chan) { -struct l2cap_chan *chan; - -BT_DBG ("pchan %p" , pchan); - - chan =l2cap_chan_create (); -if (!chan) -return NULL ; - - chan->chan_type = pchan->chan_type; - chan->ops = &smp_chan_ops; - chan->scid = pchan->scid; - chan->dcid = chan->scid; - chan->imtu = pchan->imtu; - chan->omtu = pchan->omtu; - chan->mode = pchan->mode; + new_chan->ops = &smp_chan_ops;/* Other L2CAP channels may request SMP routines in order to * change the security level. This means that the SMP channel * lock must be considered in its own category to avoid lockdep * warnings. */ -atomic_set (&chan->nesting,L2CAP_NESTING_SMP ); - -BT_DBG ("created chan %p" , chan); +atomic_set (&new_chan->nesting,L2CAP_NESTING_SMP ); -return chan; +return 0 ; }static const struct l2cap_ops smp_root_chan_ops = {
@@ -3326,7 +3311,7 @@ create_chan:l2cap_add_scid (chan, cid); -l2cap_chan_set_defaults (chan); +l2cap_chan_set_defaults (chan,NULL );if (cid ==L2CAP_CID_SMP ) {u8 bdaddr_type;
Greg Kroah-Hartman
maintainerPATCH 989/1067 Bluetooth: L2CAP: Fix not tracking outstanding TX ident
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Luiz Augusto von Dentz <[email protected]>
[ Upstream commit 6c3ea155e5ee3e56606233acde8309afda66d483 ]
This attempts to proper track outstanding request by using struct ida and allocating from it in l2cap_get_ident using ida_alloc_range which would reuse ids as they are free, then upon completion release the id using ida_free.
This fixes the qualification test case L2CAP/COS/CED/BI-29-C which attempts to check if the host stack is able to work after 256 attempts to connect which requires Ident field to use the full range of possible values in order to pass the test.
Stable-dep-of: 2641a9e0a1dd ("Bluetooth: L2CAP: cancel pending_rx_work before taking conn->lock")
--- include/net/bluetooth/l2cap.h | 3 -- net/bluetooth/l2cap_core.c | 53 ++++++++++++++++++++++++++++++------------ 2 files changed, 40 insertions(+), 16 deletions(-)
include/net/bluetooth/l2cap.h +1 -2
@@ -653,8 +653,7 @@ struct l2cap_conn {struct sk_buff *rx_skb;__u32 rx_len; -__u8 tx_ident; -struct mutex ident_lock; +struct ida tx_ida;struct sk_buff_head pending_rx;struct work_struct pending_rx_work;
net/bluetooth/l2cap_core.c +39 -14
102 lines not on this page · raw message
Greg Kroah-Hartman
maintainerPATCH 990/1067 Bluetooth: L2CAP: Fix deadlock in l2cap_conn_del()
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Hyunwoo Kim <[email protected]>
[ Upstream commit 00fdebbbc557a2fc21321ff2eaa22fd70c078608 ]
l2cap_conn_del() calls cancel_delayed_work_sync() for both info_timer and id_addr_timer while holding conn->lock. However, the work functions l2cap_info_timeout() and l2cap_conn_update_id_addr() both acquire conn->lock, creating a potential AB-BA deadlock if the work is already executing when l2cap_conn_del() takes the lock.
Move the work cancellations before acquiring conn->lock and use disable_delayed_work_sync() to additionally prevent the works from being rearmed after cancellation, consistent with the pattern used in hci_conn_del().
Stable-dep-of: 2641a9e0a1dd ("Bluetooth: L2CAP: cancel pending_rx_work before taking conn->lock")
--- net/bluetooth/l2cap_core.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-)
net/bluetooth/l2cap_core.c +3 -6
@@ -1782,6 +1782,9 @@ static void l2cap_conn_del(struct hci_coBT_DBG ("hcon %p conn %p, err %d" , hcon, conn, err); +cancel_delayed_work_sync (&conn->info_timer); +cancel_work_sync (&conn->id_addr_update_work); +kfree_skb (conn->rx_skb);skb_queue_purge (&conn->pending_rx);
@@ -1795,9 +1798,6 @@ static void l2cap_conn_del(struct hci_coida_destroy (&conn->tx_ida); -if (work_pending (&conn->id_addr_update_work)) -cancel_work_sync (&conn->id_addr_update_work); -l2cap_unregister_all_users (conn);/* Force the connection to be immediately dropped */
@@ -1822,9 +1822,6 @@ static void l2cap_conn_del(struct hci_cohci_chan_del (conn->hchan); -if (conn->info_state &L2CAP_INFO_FEAT_MASK_REQ_SENT ) -cancel_delayed_work_sync (&conn->info_timer); - hcon->l2cap_data =NULL ; conn->hchan =NULL ;l2cap_conn_put (conn);
Greg Kroah-Hartman
maintainerPATCH 991/1067 Bluetooth: L2CAP: cancel pending_rx_work before taking conn->lock
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Runyu Xiao <[email protected]>
[ Upstream commit 2641a9e0a1dd4af2e21995470a21d55dd35e5203 ]
l2cap_conn_del() takes conn->lock and then calls cancel_work_sync() for pending_rx_work. process_pending_rx() takes the same mutex, so teardown can deadlock against the worker it is flushing.
This issue was found by our static analysis tool and then manually reviewed against the current tree.
The grounded PoC kept the l2cap_conn_ready() -> queue_work(..., &conn->pending_rx_work) submit path, the l2cap_conn_del() -> cancel_work_sync(&conn->pending_rx_work) teardown path, and the process_pending_rx() -> mutex_lock(&conn->lock) worker edge. Lockdep reported:
WARNING: possible circular locking dependency detected
process_pending_rx+0x21/0x2a [vuln_msv] l2cap_conn_del.constprop.0+0x3f/0x4e [vuln_msv] *** DEADLOCK ***
Cancel pending_rx_work before taking conn->lock, matching the existing lock-before-drain ordering used for the two delayed works in the same teardown path. The pending_rx queue is still purged after the work has been cancelled and conn->lock has been acquired.
--- net/bluetooth/l2cap_core.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-)
net/bluetooth/l2cap_core.c +2 -8
@@ -1785,17 +1785,11 @@ static void l2cap_conn_del(struct hci_cocancel_delayed_work_sync (&conn->info_timer);cancel_work_sync (&conn->id_addr_update_work); +cancel_work_sync (&conn->pending_rx_work); +kfree_skb (conn->rx_skb);skb_queue_purge (&conn->pending_rx); - -/* We can not call flush_work(&conn->pending_rx_work) here since we -* might block if we are running on a worker from the same workqueue -* pending_rx_work is waiting on. -*/ -if (work_pending (&conn->pending_rx_work)) -cancel_work_sync (&conn->pending_rx_work); -ida_destroy (&conn->tx_ida);l2cap_unregister_all_users (conn);
Greg Kroah-Hartman
maintainerPATCH 992/1067 Bluetooth: hci_core: Enable buffer flow control for SCO/eSCO
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Luiz Augusto von Dentz <[email protected]>
[ Upstream commit 13218453521d75916dfed55efb8e809bfc03cb4b ]
This enables buffer flow control for SCO/eSCO
(see: Bluetooth Core 6.0 spec: 6.22. Synchronous Flow Control Enable), recently this has caused the following problem and is actually a nice addition for the likes of Socket TX complete:
< HCI Command: Read Buffer Size (0x04|0x0005) plen 0
Quoted 1 line
HCI Event: Command Complete (
0x0e) plen 11
Read Buffer Size (0x04|0x0005) ncmd 1
Status: Success (0x00)
ACL MTU: 1021 ACL max packet: 5
SCO MTU: 240 SCO max packet: 8
...
< SCO Data TX: Handle 257 flags 0x00 dlen 120
< SCO Data TX: Handle 257 flags 0x00 dlen 120
< SCO Data TX: Handle 257 flags 0x00 dlen 120
< SCO Data TX: Handle 257 flags 0x00 dlen 120
< SCO Data TX: Handle 257 flags 0x00 dlen 120
< SCO Data TX: Handle 257 flags 0x00 dlen 120
< SCO Data TX: Handle 257 flags 0x00 dlen 120
< SCO Data TX: Handle 257 flags 0x00 dlen 120
< SCO Data TX: Handle 257 flags 0x00 dlen 120
Quoted 1 line
HCI Event: Hardware Error (
0x10) plen 1
Code: 0x0a
To fix the code will now attempt to enable buffer flow control when HCI_QUIRK_SYNC_FLOWCTL_SUPPORTED is set by the driver:
< HCI Command: Write Sync Fl.. (0x03|0x002f) plen 1
Flow control: Enabled (0x01)
Quoted 1 line
HCI Event: Command Complete (
0x0e) plen 4
Write Sync Flow Control Enable (0x03|0x002f) ncmd 1
Status: Success (0x00)On success then HCI_SCO_FLOWCTL would be set which indicates sco_cnt shall be used for flow contro.
Stable-dep-of: 12917f591cea ("Bluetooth: hci_conn: Fix null ptr deref in hci_abort_conn()")
--- include/net/bluetooth/hci.h | 13 ++++++++ include/net/bluetooth/hci_core.h | 1 net/bluetooth/hci_core.c | 62 +++++++++++++++++---------------------- net/bluetooth/hci_event.c | 2 + net/bluetooth/hci_sync.c | 24 +++++++++++++++ 5 files changed, 68 insertions(+), 34 deletions(-)
include/net/bluetooth/hci.h +13 -0
@@ -228,6 +228,13 @@ enum {*/ HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED , +/* When this quirk is set consider Sync Flow Control as supported by +* the driver. +* +* This quirk must be set before hci_register_dev is called. +*/ + HCI_QUIRK_SYNC_FLOWCTL_SUPPORTED, +/* When this quirk is set, the controller has validated that * LE states reported through the HCI_LE_READ_SUPPORTED_STATES are * valid. This mechanism is necessary as many controllers have
@@ -396,6 +403,7 @@ enum {HCI_WIDEBAND_SPEECH_ENABLED ,HCI_EVENT_FILTER_CONFIGURED ,HCI_PA_SYNC , +HCI_SCO_FLOWCTL ,HCI_DUT_MODE , HCI_VENDOR_DIAG,
@@ -1538,6 +1546,11 @@ struct hci_rp_read_tx_power {__s8 tx_power; } __packed; +#define HCI_OP_WRITE_SYNC_FLOWCTL 0x0c2f +struct hci_cp_write_sync_flowctl { +__u8 enable; +} __packed; +#define HCI_OP_READ_PAGE_SCAN_TYPE 0x0c46struct hci_rp_read_page_scan_type {__u8 status;
include/net/bluetooth/hci_core.h +1 -0
@@ -1659,6 +1659,7 @@ void hci_conn_del_sysfs(struct hci_conn#define lmp_hold_capable (dev) ((dev)->features[0][0] & LMP_HOLD)#define lmp_sniff_capable (dev) ((dev)->features[0][0] & LMP_SNIFF)#define lmp_park_capable (dev) ((dev)->features[0][1] & LMP_PARK) +#define lmp_sco_capable (dev) ((dev)->features[0][1] & LMP_SCO)#define lmp_inq_rssi_capable (dev) ((dev)->features[0][3] & LMP_RSSI_INQ)#define lmp_esco_capable (dev) ((dev)->features[0][3] & LMP_ESCO)#define lmp_bredr_capable (dev) (!((dev)->features[0][4] & LMP_NO_BREDR))
net/bluetooth/hci_core.c +28 -34
102 lines not on this page · raw message
net/bluetooth/hci_event.c +2 -0
@@ -4463,9 +4463,11 @@ static void hci_num_comp_pkts_evt(structbreak ;case SCO_LINK : +case ESCO_LINK : hdev->sco_cnt += count;if (hdev->sco_cnt > hdev->sco_pkts) hdev->sco_cnt = hdev->sco_pkts; +break ;case ISO_LINK :
net/bluetooth/hci_sync.c +24 -0
@@ -3854,6 +3854,28 @@ static int hci_write_ca_timeout_sync(str sizeof(param), ¶m, HCI_CMD_TIMEOUT); } +/* Enable SCO flow control if supported */ +static int hci_write_sync_flowctl_sync (struct hci_dev *hdev) +{ +struct hci_cp_write_sync_flowctl cp; +int err; + +/* Check if the controller supports SCO and HCI_OP_WRITE_SYNC_FLOWCTL */ +if (!lmp_sco_capable (hdev) || !(hdev->commands[10 ] &BIT (4 )) || + !test_bit (HCI_QUIRK_SYNC_FLOWCTL_SUPPORTED , &hdev->quirks)) +return 0 ; + +memset (&cp,0 ,sizeof (cp)); + cp.enable =0x01 ; + + err =__hci_cmd_sync_status (hdev,HCI_OP_WRITE_SYNC_FLOWCTL , +sizeof (cp), &cp,HCI_CMD_TIMEOUT ); +if (!err) +hci_dev_set_flag (hdev,HCI_SCO_FLOWCTL ); + +return err; +} +/* BR Controller init stage 2 command sequence */ static const struct hci_init_stage br_init2[] = {/* HCI_OP_READ_BUFFER_SIZE */
@@ -3872,6 +3894,8 @@ static const struct hci_init_stage br_inHCI_INIT (hci_clear_event_filter_sync),/* HCI_OP_WRITE_CA_TIMEOUT */ HCI_INIT (hci_write_ca_timeout_sync), +/* HCI_OP_WRITE_SYNC_FLOWCTL */ + HCI_INIT(hci_write_sync_flowctl_sync), {} };
Greg Kroah-Hartman
maintainerPATCH 993/1067 cifs: handle when server stops supporting multichannel
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Shyam Prasad N <[email protected]>
[ Upstream commit ee1d21794e55ab76505745d24101331552182002 ]
When a server stops supporting multichannel, we will keep attempting reconnects to the secondary channels today. Avoid this by freeing extra channels when negotiate returns no multichannel support.
Stable-dep-of: ec457f9afe5a ("smb: client: resolve SWN tcon from live registrations")
--- fs/smb/client/cifsglob.h | 1 fs/smb/client/cifsproto.h | 2 + fs/smb/client/connect.c | 10 ++++++ fs/smb/client/sess.c | 65 +++++++++++++++++++++++++++++++++------ fs/smb/client/smb2pdu.c | 76 +++++++++++++++++++++++++++++++++++++++++++++- fs/smb/client/transport.c | 2 - 6 files changed, 145 insertions(+), 11 deletions(-)
fs/smb/client/cifsglob.h +1 -0
@@ -639,6 +639,7 @@ struct TCP_Server_Info {bool noautotune;/* do not autotune send buf sizes */ bool nosharesock;bool tcp_nodelay; +bool terminate;unsigned int credits;/* send no more requests at once */ unsigned int max_credits;/* can override large 32000 default at mnt */ unsigned int in_flight;/* number of requests on the wire to server */
fs/smb/client/cifsproto.h +2 -0
@@ -662,6 +662,8 @@ cifs_chan_needs_reconnect(struct cifs_sebool cifs_chan_is_iface_active (struct cifs_ses *ses,struct TCP_Server_Info *server); +void +cifs_disable_secondary_channels (struct cifs_ses *ses);int cifs_chan_update_iface (struct cifs_ses *ses,struct TCP_Server_Info *server);int
fs/smb/client/connect.c +10 -0
@@ -259,6 +259,14 @@ cifs_mark_tcp_ses_conns_for_reconnect(stspin_lock (&cifs_tcp_ses_lock);list_for_each_entry_safe (ses, nses, &pserver->smb_ses_list, smb_ses_list) { +/* +* if channel has been marked for termination, nothing to do +* for the channel. in fact, we cannot find the channel for the +* server. So safe to exit here +*/ +if (server->terminate) +break ; +spin_lock (&ses->ses_lock);if (ses->ses_status ==SES_EXITING ) {spin_unlock (&ses->ses_lock);
@@ -302,6 +310,8 @@ cifs_mark_tcp_ses_conns_for_reconnect(stspin_lock (&tcon->tc_lock); tcon->status =TID_NEED_RECON ;spin_unlock (&tcon->tc_lock); + +cancel_delayed_work (&tcon->query_interfaces); }if (ses->tcon_ipc) { ses->tcon_ipc->need_reconnect = true;
fs/smb/client/sess.c +56 -9
85 lines not on this page · raw message
fs/smb/client/smb2pdu.c +75 -1
110 lines not on this page · raw message
fs/smb/client/transport.c +1 -1
@@ -1063,7 +1063,7 @@ struct TCP_Server_Info *cifs_pick_channespin_lock (&ses->chan_lock);for (i =0 ; i < ses->chan_count; i++) { server = ses->chans[i].server; -if (!server) +if (!server || server->terminate)continue ;/*
Greg Kroah-Hartman
maintainerPATCH 994/1067 cifs: after disabling multichannel, mark tcon for reconnect
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Shyam Prasad N <[email protected]>
[ Upstream commit 27e1fd343f80168ff456785c2443136b6b7ca3cc ]
Once the server disables multichannel for an active multichannel session, on the following reconnect, the client would reduce the number of channels to 1. However, it could be the case that the tree connect was active on one of these disabled channels. This results in an unrecoverable state.
This change fixes that by making sure that whenever a channel is being terminated, the session and tcon are marked for reconnect too. This could mean a few redundant tree connect calls to the server, but considering that this is not a frequent event, we should be okay.
Stable-dep-of: ec457f9afe5a ("smb: client: resolve SWN tcon from live registrations")
--- fs/smb/client/connect.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-)
fs/smb/client/connect.c +12 -8
@@ -256,17 +256,21 @@ cifs_mark_tcp_ses_conns_for_reconnect(st/* If server is a channel, select the primary channel */ pserver =CIFS_SERVER_IS_CHAN (server) ? server->primary_server : server; +/* +* if the server has been marked for termination, there is a +* chance that the remaining channels all need reconnect. To be +* on the safer side, mark the session and trees for reconnect +* for this scenario. This might cause a few redundant session +* setup and tree connect requests, but it is better than not doing +* a tree connect when needed, and all following requests failing +*/ +if (server->terminate) { + mark_smb_session = true; + server = pserver; + }spin_lock (&cifs_tcp_ses_lock);list_for_each_entry_safe (ses, nses, &pserver->smb_ses_list, smb_ses_list) { -/* -* if channel has been marked for termination, nothing to do -* for the channel. in fact, we cannot find the channel for the -* server. So safe to exit here -*/ -if (server->terminate) -break ; -spin_lock (&ses->ses_lock);if (ses->ses_status ==SES_EXITING ) {spin_unlock (&ses->ses_lock);
Greg Kroah-Hartman
maintainerPATCH 995/1067 cifs: Fix reacquisition of volume cookie on still-live connection
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Howells <[email protected]>
[ Upstream commit dad80c6bff770d25f67ec25fe011730e4a463008 ]
During mount, cifs_mount_get_tcon() gets a tcon resource connection record and then attaches an fscache volume cookie to it. However, it does this irrespective of whether or not the tcon returned from cifs_get_tcon() is a new record or one that's already in use. This leads to a warning about a volume cookie collision and a leaked volume cookie because tcon->fscache gets reset.
Fix this be adding a mutex and a "we've already tried this" flag and only doing it once for the lifetime of the tcon.
[!] Note: Looking at cifs_mount_get_tcon(), a more general solution may actually be required. Reacquiring the volume cookie isn't the only thing that function does: it also partially reinitialises the tcon record without any locking - which may cause live filesystem ops already using the tcon through a previous mount to malfunction.
This can be reproduced simply by something like:
mount //example.com/test /xfstest.test -o user=shares,pass=xxx,fsc mount //example.com/test /mnt -o user=shares,pass=xxx,fsc
Stable-dep-of: ec457f9afe5a ("smb: client: resolve SWN tcon from live registrations")
--- fs/smb/client/cifsglob.h | 2 ++ fs/smb/client/fscache.c | 13 +++++++++++++ fs/smb/client/misc.c | 3 +++ 3 files changed, 18 insertions(+)
fs/smb/client/cifsglob.h +2 -0
@@ -1205,7 +1205,9 @@ struct cifs_tcon {__u32 max_bytes_copy;#ifdef CONFIG_CIFS_FSCACHE u64 resource_id;/* server resource id */ +bool fscache_acquired;/* T if we've tried acquiring a cookie */ struct fscache_volume *fscache;/* cookie for share */ +struct mutex fscache_lock;/* Prevent regetting a cookie */ #endif struct list_head pending_opens;/* list of incomplete opens */ struct cached_fids *cfids;
fs/smb/client/fscache.c +13 -0
@@ -43,12 +43,23 @@ int cifs_fscache_get_super_cookie(structchar *key;int ret = -ENOMEM ; +if (tcon->fscache_acquired) +return 0 ; + +mutex_lock (&tcon->fscache_lock); +if (tcon->fscache_acquired) { +mutex_unlock (&tcon->fscache_lock); +return 0 ; + } + tcon->fscache_acquired = true; + tcon->fscache =NULL ;switch (sa->sa_family) {case AF_INET :case AF_INET6 :break ;default : +mutex_unlock (&tcon->fscache_lock);cifs_dbg (VFS ,"Unknown network family '%d'\n" , sa->sa_family);return -EINVAL ; }
@@ -57,6 +68,7 @@ int cifs_fscache_get_super_cookie(struct sharename =extract_sharename (tcon->tree_name);if (IS_ERR (sharename)) { +mutex_unlock (&tcon->fscache_lock);cifs_dbg (FYI ,"%s: couldn't extract sharename\n" , __func__);return PTR_ERR (sharename); }
@@ -90,6 +102,7 @@ out_2:kfree (key); out:kfree (sharename); +mutex_unlock (&tcon->fscache_lock);return ret; }
fs/smb/client/misc.c +3 -0
@@ -139,6 +139,9 @@ tconInfoAlloc(void)spin_lock_init (&ret_buf->stat_lock);atomic_set (&ret_buf->num_local_opens,0 );atomic_set (&ret_buf->num_remote_opens,0 ); +#ifdef CONFIG_CIFS_FSCACHE +mutex_init (&ret_buf->fscache_lock); +#endif return ret_buf; }
Greg Kroah-Hartman
maintainerPATCH 996/1067 cifs: Add tracing for the cifs_tcon struct refcounting
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Howells <[email protected]>
[ Upstream commit afc23febd51c7e24361e3a9c09f3e892eb0a41ea ]
Add tracing for the refcounting/lifecycle of the cifs_tcon struct, marking different events with different labels and giving each tcon its own debug ID so that the tracelines corresponding to individual tcons can be distinguished. This can be enabled with:
echo 1 >/sys/kernel/debug/tracing/events/cifs/smb3_tcon_ref/enable
Stable-dep-of: ec457f9afe5a ("smb: client: resolve SWN tcon from live registrations")
--- fs/smb/client/cifs_swn.c | 13 ++--- fs/smb/client/cifsfs.c | 2 fs/smb/client/cifsglob.h | 1 fs/smb/client/cifsproto.h | 8 +-- fs/smb/client/connect.c | 21 +++++---- fs/smb/client/dfs_cache.c | 2 fs/smb/client/fscache.c | 7 +++ fs/smb/client/misc.c | 12 +++-- fs/smb/client/smb2misc.c | 10 +++- fs/smb/client/smb2ops.c | 7 ++- fs/smb/client/smb2pdu.c | 8 ++- fs/smb/client/smb2transport.c | 2 fs/smb/client/trace.h | 93 +++++++++++++++++++++++++++++++++++++++++- 13 files changed, 151 insertions(+), 35 deletions(-)
fs/smb/client/cifs_swn.c +6 -7
@@ -313,17 +313,15 @@ static struct cifs_swn_reg *cifs_get_swn reg =cifs_find_swn_reg (tcon);if (!IS_ERR (reg)) {kref_get (®->ref_count); -mutex_unlock (&cifs_swnreg_idr_mutex); -return reg; + goto unlock; }else if (PTR_ERR (reg) != -EEXIST ) { -mutex_unlock (&cifs_swnreg_idr_mutex); -return reg; + goto unlock; } reg =kmalloc (sizeof (struct cifs_swn_reg ),GFP_ATOMIC );if (reg ==NULL ) { -mutex_unlock (&cifs_swnreg_idr_mutex); -return ERR_PTR (-ENOMEM ); + ret = -ENOMEM ; + goto fail_unlock; }kref_init (®->ref_count);
@@ -354,7 +352,7 @@ static struct cifs_swn_reg *cifs_get_swn reg->ip_notify = (tcon->capabilities &SMB2_SHARE_CAP_SCALEOUT ); reg->tcon = tcon; - +unlock:mutex_unlock (&cifs_swnreg_idr_mutex);return reg;
@@ -365,6 +363,7 @@ fail_idr:idr_remove (&cifs_swnreg_idr, reg->id); fail:kfree (reg); +fail_unlock:mutex_unlock (&cifs_swnreg_idr_mutex);return ERR_PTR (ret); }
fs/smb/client/cifsfs.c +2 -0
@@ -747,6 +747,8 @@ static void cifs_umount_begin(struct supspin_lock (&cifs_tcp_ses_lock);spin_lock (&tcon->tc_lock); +trace_smb3_tcon_ref (tcon->debug_id, tcon->tc_count, + netfs_trace_tcon_ref_see_umount);if ((tcon->tc_count >1 ) || (tcon->status ==TID_EXITING )) {/* we have other mounts to same share or we have already tried to umount this and woken up
fs/smb/client/cifsglob.h +1 -0
@@ -1121,6 +1121,7 @@ struct cifs_fattr {*/ struct cifs_tcon {struct list_head tcon_list; +int debug_id;/* Debugging for tracing */ int tc_count;struct list_head rlist;/* reconnect list */ spinlock_t tc_lock;/* protect anything here that is not protected */
fs/smb/client/cifsproto.h +3 -5
@@ -305,7 +305,7 @@ cifs_get_tcp_session(struct smb3_fs_cont struct TCP_Server_Info *primary_server);extern void cifs_put_tcp_session (struct TCP_Server_Info *server,int from_reconnect); -extern void cifs_put_tcon (struct cifs_tcon *tcon); +extern void cifs_put_tcon (struct cifs_tcon *tcon,enum smb3_tcon_ref_trace trace);#if IS_ENABLED (CONFIG_CIFS_DFS_UPCALL )extern void cifs_dfs_release_automount_timer (void );
@@ -525,8 +525,8 @@ extern int CIFSSMBLogoff(const unsignedextern struct cifs_ses *sesInfoAlloc (void );extern void sesInfoFree (struct cifs_ses *); -extern struct cifs_tcon *tconInfoAlloc (void ); -extern void tconInfoFree (struct cifs_tcon *); +extern struct cifs_tcon *tconInfoAlloc (enum smb3_tcon_ref_trace trace); +extern void tconInfoFree (struct cifs_tcon *tcon,enum smb3_tcon_ref_trace trace);extern int cifs_sign_rqst (struct smb_rqst *rqst,struct TCP_Server_Info *server,__u32 *pexpected_response_sequence_number);
@@ -719,8 +719,6 @@ static inline int cifs_create_options(streturn options; } -struct super_block *cifs_get_tcon_super (struct cifs_tcon *tcon); -void cifs_put_tcon_super (struct super_block *sb);int cifs_wait_for_server_reconnect (struct TCP_Server_Info *server,bool retry);static inline void release_mid (struct mid_q_entry *mid)
fs/smb/client/connect.c +12 -9
@@ -1985,7 +1985,7 @@ cifs_setup_ipc(struct cifs_ses *ses, str } } - tcon =tconInfoAlloc (); + tcon =tconInfoAlloc (netfs_trace_tcon_ref_new_ipc);if (tcon ==NULL )return -ENOMEM ;
@@ -2002,7 +2002,7 @@ cifs_setup_ipc(struct cifs_ses *ses, strif (rc) {cifs_server_dbg (VFS ,"failed to connect to IPC (rc=%d)\n" , rc); -tconInfoFree (tcon); +tconInfoFree (tcon, netfs_trace_tcon_ref_free_ipc_fail); goto out; }
@@ -2085,7 +2085,7 @@ void cifs_put_smb_ses(struct cifs_ses *s* files on session close, as specified in MS-SMB2 3.3.5.6 Receiving an * SMB2 LOGOFF Request. */ -tconInfoFree (tcon); +tconInfoFree (tcon, netfs_trace_tcon_ref_free_ipc);if (do_logoff) { xid =get_xid (); rc = server->ops->logoff (xid, ses);
@@ -2459,6 +2459,8 @@ cifs_find_tcon(struct cifs_ses *ses, strcontinue ; } ++tcon->tc_count; +trace_smb3_tcon_ref (tcon->debug_id, tcon->tc_count, + netfs_trace_tcon_ref_get_find);spin_unlock (&tcon->tc_lock);spin_unlock (&cifs_tcp_ses_lock);return tcon;
@@ -2468,7 +2470,7 @@ cifs_find_tcon(struct cifs_ses *ses, str }void -cifs_put_tcon (struct cifs_tcon *tcon) +cifs_put_tcon (struct cifs_tcon *tcon,enum smb3_tcon_ref_trace trace) {unsigned int xid;struct cifs_ses *ses;
@@ -2484,6 +2486,7 @@ cifs_put_tcon(struct cifs_tcon *tcon)cifs_dbg (FYI ,"%s: tc_count=%d\n" , __func__, tcon->tc_count);spin_lock (&cifs_tcp_ses_lock);spin_lock (&tcon->tc_lock); +trace_smb3_tcon_ref (tcon->debug_id, tcon->tc_count -1 , trace);if (--tcon->tc_count >0 ) {spin_unlock (&tcon->tc_lock);spin_unlock (&cifs_tcp_ses_lock);
@@ -2517,7 +2520,7 @@ cifs_put_tcon(struct cifs_tcon *tcon)_free_xid (xid);cifs_fscache_release_super_cookie (tcon); -tconInfoFree (tcon); +tconInfoFree (tcon, netfs_trace_tcon_ref_free);cifs_put_smb_ses (ses); }
@@ -2565,7 +2568,7 @@ cifs_get_tcon(struct cifs_ses *ses, stru goto out_fail; } - tcon =tconInfoAlloc (); + tcon =tconInfoAlloc (netfs_trace_tcon_ref_new);if (tcon ==NULL ) { rc = -ENOMEM ; goto out_fail;
@@ -2762,7 +2765,7 @@ cifs_get_tcon(struct cifs_ses *ses, strureturn tcon; out_fail: -tconInfoFree (tcon); +tconInfoFree (tcon, netfs_trace_tcon_ref_free_fail);return ERR_PTR (rc); }
@@ -2779,7 +2782,7 @@ cifs_put_tlink(struct tcon_link *tlink) }if (!IS_ERR (tlink_tcon (tlink))) -cifs_put_tcon (tlink_tcon (tlink)); +cifs_put_tcon (tlink_tcon (tlink), netfs_trace_tcon_ref_put_tlink);kfree (tlink);return ; }
@@ -3338,7 +3341,7 @@ static inline void mount_put_conns(strucint rc =0 ;if (mnt_ctx->tcon) -cifs_put_tcon (mnt_ctx->tcon); +cifs_put_tcon (mnt_ctx->tcon, netfs_trace_tcon_ref_put_mnt_ctx);else if (mnt_ctx->ses)cifs_put_smb_ses (mnt_ctx->ses);else if (mnt_ctx->server)
fs/smb/client/dfs_cache.c +1 -1
@@ -1564,7 +1564,7 @@ static void refresh_mounts(struct cifs_s }mutex_unlock (&server->refpath_lock); -cifs_put_tcon (tcon); +cifs_put_tcon (tcon, netfs_trace_tcon_ref_put_dfs_refer); } }
fs/smb/client/fscache.c +7 -0
@@ -94,6 +94,11 @@ int cifs_fscache_get_super_cookie(struct }pr_err ("Cache volume key already in use (%s)\n" , key); vcookie =NULL ; +trace_smb3_tcon_ref (tcon->debug_id, tcon->tc_count, + netfs_trace_tcon_ref_see_fscache_collision); + }else { +trace_smb3_tcon_ref (tcon->debug_id, tcon->tc_count, + netfs_trace_tcon_ref_see_fscache_okay); } tcon->fscache = vcookie;
@@ -115,6 +120,8 @@ void cifs_fscache_release_super_cookie(scifs_fscache_fill_volume_coherency (tcon, &cd);fscache_relinquish_volume (tcon->fscache, &cd, false); tcon->fscache =NULL ; +trace_smb3_tcon_ref (tcon->debug_id, tcon->tc_count, + netfs_trace_tcon_ref_see_fscache_relinq); }void cifs_fscache_get_inode_cookie (struct inode *inode)
fs/smb/client/misc.c +8 -4
@@ -116,9 +116,10 @@ sesInfoFree(struct cifs_ses *buf_to_free }struct cifs_tcon * -tconInfoAlloc (void ) +tconInfoAlloc (enum smb3_tcon_ref_trace trace) {struct cifs_tcon *ret_buf; +static atomic_t tcon_debug_id; ret_buf =kzalloc (sizeof (*ret_buf),GFP_KERNEL );if (!ret_buf)
@@ -131,7 +132,8 @@ tconInfoAlloc(void)atomic_inc (&tconInfoAllocCount); ret_buf->status =TID_NEW ; - ++ret_buf->tc_count; + ret_buf->debug_id =atomic_inc_return (&tcon_debug_id); + ret_buf->tc_count =1 ;spin_lock_init (&ret_buf->tc_lock);INIT_LIST_HEAD (&ret_buf->openFileList);INIT_LIST_HEAD (&ret_buf->tcon_list);
@@ -142,17 +144,19 @@ tconInfoAlloc(void)#ifdef CONFIG_CIFS_FSCACHE mutex_init (&ret_buf->fscache_lock);#endif +trace_smb3_tcon_ref (ret_buf->debug_id, ret_buf->tc_count, trace);return ret_buf; }void -tconInfoFree (struct cifs_tcon *tcon) +tconInfoFree (struct cifs_tcon *tcon,enum smb3_tcon_ref_trace trace) {if (tcon ==NULL ) {cifs_dbg (FYI ,"Null buffer passed to tconInfoFree\n" );return ; } +trace_smb3_tcon_ref (tcon->debug_id, tcon->tc_count, trace);free_cached_dirs (tcon->cfids);atomic_dec (&tconInfoAllocCount);kfree (tcon->nativeFileSystem);
@@ -848,7 +852,7 @@ void cifs_close_all_deferred_files_sb(stlist_for_each_entry_safe (tmp_list, q, &tcon_head, entry) {cifs_close_all_deferred_files (tmp_list->tcon);list_del (&tmp_list->entry); -cifs_put_tcon (tmp_list->tcon); +cifs_put_tcon (tmp_list->tcon, netfs_trace_tcon_ref_put_tlink);kfree (tmp_list); } }
fs/smb/client/smb2misc.c +7 -3
@@ -804,7 +804,7 @@ smb2_cancelled_close_fid(struct work_strif (rc)cifs_tcon_dbg (VFS ,"Close cancelled mid failed rc:%d\n" , rc); -cifs_put_tcon (tcon); +cifs_put_tcon (tcon, netfs_trace_tcon_ref_put_cancelled_close_fid);kfree (cancelled); }
@@ -848,6 +848,8 @@ smb2_handle_cancelled_close(struct cifs_if (tcon->tc_count <=0 ) {struct TCP_Server_Info *server =NULL ; +trace_smb3_tcon_ref (tcon->debug_id, tcon->tc_count, + netfs_trace_tcon_ref_see_cancelled_close);WARN_ONCE (tcon->tc_count <0 ,"tcon refcount is negative" );spin_unlock (&cifs_tcp_ses_lock);
@@ -861,12 +863,14 @@ smb2_handle_cancelled_close(struct cifs_return 0 ; } tcon->tc_count++; +trace_smb3_tcon_ref (tcon->debug_id, tcon->tc_count, + netfs_trace_tcon_ref_get_cancelled_close);spin_unlock (&cifs_tcp_ses_lock); rc =__smb2_handle_cancelled_cmd (tcon,SMB2_CLOSE_HE ,0 , persistent_fid, volatile_fid);if (rc) -cifs_put_tcon (tcon); +cifs_put_tcon (tcon, netfs_trace_tcon_ref_put_cancelled_close);return rc; }
@@ -894,7 +898,7 @@ smb2_handle_cancelled_mid(struct mid_q_e rsp->PersistentFileId, rsp->VolatileFileId);if (rc) -cifs_put_tcon (tcon); +cifs_put_tcon (tcon, netfs_trace_tcon_ref_put_cancelled_mid);return rc; }
fs/smb/client/smb2ops.c +6 -1
@@ -2848,8 +2848,11 @@ smb2_get_dfs_refer(const unsigned int xi tcon =list_first_entry_or_null (&ses->tcon_list, struct cifs_tcon, tcon_list); -if (tcon) +if (tcon) { tcon->tc_count++; +trace_smb3_tcon_ref (tcon->debug_id, tcon->tc_count, + netfs_trace_tcon_ref_get_dfs_refer); + }spin_unlock (&cifs_tcp_ses_lock); }
@@ -2913,6 +2916,8 @@ smb2_get_dfs_refer(const unsigned int xi/* ipc tcons are not refcounted */ spin_lock (&cifs_tcp_ses_lock); tcon->tc_count--; +trace_smb3_tcon_ref (tcon->debug_id, tcon->tc_count, + netfs_trace_tcon_ref_dec_dfs_refer);/* tc_count can never go negative */ WARN_ON (tcon->tc_count <0 );spin_unlock (&cifs_tcp_ses_lock);
fs/smb/client/smb2pdu.c +5 -3
@@ -3995,6 +3995,8 @@ void smb2_reconnect_server(struct work_slist_for_each_entry (tcon, &ses->tcon_list, tcon_list) {if (tcon->need_reconnect || tcon->need_reopen_files) { tcon->tc_count++; +trace_smb3_tcon_ref (tcon->debug_id, tcon->tc_count, + netfs_trace_tcon_ref_get_reconnect_server);list_add_tail (&tcon->rlist, &tmp_list); tcon_selected = tcon_exist = true; }
@@ -4040,14 +4042,14 @@ void smb2_reconnect_server(struct work_sif (tcon->ipc)cifs_put_smb_ses (tcon->ses);else -cifs_put_tcon (tcon); +cifs_put_tcon (tcon, netfs_trace_tcon_ref_put_reconnect_server); }if (!ses_exist) goto done;/* allocate a dummy tcon struct used for reconnect */ - tcon =tconInfoAlloc (); + tcon =tconInfoAlloc (netfs_trace_tcon_ref_new_reconnect_server);if (!tcon) { resched = true;list_for_each_entry_safe (ses, ses2, &tmp_ses_list, rlist) {
@@ -4070,7 +4072,7 @@ void smb2_reconnect_server(struct work_slist_del_init (&ses->rlist);cifs_put_smb_ses (ses); } -tconInfoFree (tcon); +tconInfoFree (tcon, netfs_trace_tcon_ref_free_reconnect_server); done:cifs_dbg (FYI ,"Reconnecting tcons and channels finished\n" );
fs/smb/client/smb2transport.c +2 -0
@@ -189,6 +189,8 @@ smb2_find_smb_sess_tcon_unlocked(structif (tcon->tid != tid)continue ; ++tcon->tc_count; +trace_smb3_tcon_ref (tcon->debug_id, tcon->tc_count, + netfs_trace_tcon_ref_get_find_sess_tcon);return tcon; }
fs/smb/client/trace.h +91 -2
112 lines not on this page · raw message
Greg Kroah-Hartman
maintainerPATCH 997/1067 smb: client: resolve SWN tcon from live registrations
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Michael Bommarito <[email protected]>
[ Upstream commit ec457f9afe5ae9538bdcd58fd4cb442b9787e183 ]
cifs_swn_notify() looks up a witness registration by id under cifs_swnreg_idr_mutex, drops the mutex, and then uses the registration's cached tcon pointer. That pointer is not a lifetime reference, and it is not a stable representative once cifs_get_swn_reg() lets multiple tcons for the same net/share name share one registration id.
A same-share second mount can keep the cifs_swn_reg alive after the first tcon unregisters and is freed. The registration then still points at the freed first tcon, so taking tc_lock or incrementing tc_count through swnreg->tcon only moves the use-after-free earlier. Taking tc_lock while holding cifs_swnreg_idr_mutex also violates the documented CIFS lock order.
Fix this by making the registration store only the stable witness
identity: id, net name, share name, and notify flags. When a notify arrives, copy that identity under cifs_swnreg_idr_mutex, drop the mutex, then find and pin a live witness tcon that currently matches the net/share pair under the normal cifs_tcp_ses_lock -> tc_lock order. The notification path uses that pinned tcon directly and drops the reference when done.
Registration and unregister messages now use the live tcon passed by the caller instead of a cached tcon in the registration. The final unregister send is folded into cifs_swn_unregister() while the registration is still protected by cifs_swnreg_idr_mutex. This removes the previous find/drop/reacquire raw-pointer window. The release path only removes the idr entry and frees the stable identity strings.
This preserves the intended one-registration/many-tcon behavior: a registration id represents a net/share pair, and notify handling acts on a live representative selected at use time. It also preserves CLIENT_MOVE ordering for the representative tcon because the old-IP unregister is sent before cifs_swn_register() sends the new-IP register.
--- fs/smb/client/cifs_swn.c | 314 ++++++++++++++++++++++++++++++++++++++--------- fs/smb/client/trace.h | 2 2 files changed, 262 insertions(+), 54 deletions(-)
fs/smb/client/cifs_swn.c +260 -54
@@ -28,10 +28,54 @@ struct cifs_swn_reg {bool net_name_notify;bool share_name_notify;bool ip_notify; +}; -struct cifs_tcon *tcon; +struct cifs_swn_reg_info { +int id; +unsigned int ref_count; +const char *net_name; +const char *share_name; +bool net_name_notify; +bool share_name_notify; +bool ip_notify; }; +static void cifs_swn_snapshot_reg (struct cifs_swn_reg *swnreg, +struct cifs_swn_reg_info *info) +{ + info->id = swnreg->id; + info->ref_count =kref_read (&swnreg->ref_count); + info->net_name = swnreg->net_name; + info->share_name = swnreg->share_name; + info->net_name_notify = swnreg->net_name_notify; + info->share_name_notify = swnreg->share_name_notify; + info->ip_notify = swnreg->ip_notify; +} + +static int cifs_swn_dup_reg (struct cifs_swn_reg *swnreg, +struct cifs_swn_reg_info *info) +{ +cifs_swn_snapshot_reg (swnreg, info); + + info->net_name =kstrdup (swnreg->net_name,GFP_KERNEL ); +if (!info->net_name) +return -ENOMEM ; + + info->share_name =kstrdup (swnreg->share_name,GFP_KERNEL ); +if (!info->share_name) { +kfree (info->net_name); +return -ENOMEM ; + } + +return 0 ; +} + +static void cifs_swn_free_reg_info (struct cifs_swn_reg_info *info) +{ +kfree (info->net_name); +kfree (info->share_name); +} +static int cifs_swn_auth_info_krb (struct cifs_tcon *tcon,struct sk_buff *skb) {int ret;
@@ -73,7 +117,8 @@ static int cifs_swn_auth_info_ntlm(struc* The authentication information to connect to the witness service is bundled * into the message. */ -static int cifs_swn_send_register_message (struct cifs_swn_reg *swnreg) +static int cifs_swn_send_register_message (struct cifs_swn_reg_info *swnreg, +struct cifs_tcon *tcon) {struct sk_buff *skb;struct genlmsghdr *hdr;
@@ -111,10 +156,10 @@ static int cifs_swn_send_register_messag* told to switch to it (client move message). In these cases we unregister from the * server address and register to the new address when we receive the notification. */ -if (swnreg->tcon->ses->server->use_swn_dstaddr) - addr = &swnreg->tcon->ses->server->swn_dstaddr; +if (tcon->ses->server->use_swn_dstaddr) + addr = &tcon->ses->server->swn_dstaddr;else - addr = &swnreg->tcon->ses->server->dstaddr; + addr = &tcon->ses->server->dstaddr; ret =nla_put (skb,CIFS_GENL_ATTR_SWN_IP ,sizeof (struct sockaddr_storage ), addr);if (ret <0 )
@@ -138,10 +183,10 @@ static int cifs_swn_send_register_messag goto nlmsg_fail; } - authtype =cifs_select_sectype (swnreg->tcon->ses->server, swnreg->tcon->ses->sectype); + authtype =cifs_select_sectype (tcon->ses->server, tcon->ses->sectype);switch (authtype) {case Kerberos: - ret =cifs_swn_auth_info_krb (swnreg->tcon, skb); + ret =cifs_swn_auth_info_krb (tcon, skb);if (ret <0 ) {cifs_dbg (VFS ,"%s: Failed to get kerberos auth info: %d\n" , __func__, ret); goto nlmsg_fail;
@@ -149,7 +194,7 @@ static int cifs_swn_send_register_messagbreak ;case NTLMv2:case RawNTLMSSP: - ret =cifs_swn_auth_info_ntlm (swnreg->tcon, skb); + ret =cifs_swn_auth_info_ntlm (tcon, skb);if (ret <0 ) {cifs_dbg (VFS ,"%s: Failed to get NTLM auth info: %d\n" , __func__, ret); goto nlmsg_fail;
@@ -179,7 +224,8 @@ fail:/* * Sends an uregister message to the userspace daemon based on the registration */ -static int cifs_swn_send_unregister_message (struct cifs_swn_reg *swnreg) +static int cifs_swn_send_unregister_message (struct cifs_swn_reg_info *swnreg, +struct cifs_tcon *tcon) {struct sk_buff *skb;struct genlmsghdr *hdr;
@@ -208,7 +254,7 @@ static int cifs_swn_send_unregister_mess goto nlmsg_fail; ret =nla_put (skb,CIFS_GENL_ATTR_SWN_IP ,sizeof (struct sockaddr_storage ), - &swnreg->tcon->ses->server->dstaddr); + &tcon->ses->server->dstaddr);if (ret <0 ) goto nlmsg_fail;
@@ -245,6 +291,88 @@ nlmsg_fail: }/* +* Allocation-free mirror of extract_hostname() + extract_sharename() from +* fs/smb/client/unc.c. Those helpers kmalloc(GFP_KERNEL); this runs under +* cifs_tcp_ses_lock and tcon->tc_lock, both spinlocks, so we mirror their +* parsing in place against the caller's stable net_name/share_name strings. +* Keep in sync with unc.c. +*/ +static bool cifs_swn_tcon_matches (struct cifs_tcon *tcon, +const char *net_name, +const char *share_name) +{ +const char *unc = tcon->tree_name; +const char *host, *share, *delim; +size_t host_len, share_len; + +if (!tcon->use_witness) +return false; + +/* extract_hostname: require strlen(unc) >= 3 */ +if (strnlen (unc,3 ) <3 ) +return false; +/* extract_hostname: skip all leading '\' characters */ +for (host = unc; *host =='\\' ; host++) + ; +if (!*host) +return false; + delim =strchr (host,'\\' ); +if (!delim) +return false; + host_len = delim - host; +if (strlen (net_name) != host_len || +strncasecmp (host, net_name, host_len)) +return false; + +/* extract_sharename: start at unc + 2, then first '\' onward */ + share = unc +2 ; + delim =strchr (share,'\\' ); +if (!delim) +return false; + share = delim +1 ; + share_len =strlen (share); + +return strlen (share_name) == share_len && + !strncasecmp (share, share_name, share_len); +} + +/* +* One SWN registration id represents one net/share name pair. Multiple +* mounted tcons can therefore share the id. Pick a live representative at +* use time instead of caching the first tcon pointer in the registration. +*/ +static struct cifs_tcon *cifs_swn_get_tcon (struct cifs_swn_reg_info *swnreg) +{ +struct TCP_Server_Info *server; +struct cifs_ses *ses; +struct cifs_tcon *tcon; + +spin_lock (&cifs_tcp_ses_lock); +list_for_each_entry (server, &cifs_tcp_ses_list, tcp_ses_list) { +list_for_each_entry (ses, &server->smb_ses_list, smb_ses_list) { +list_for_each_entry (tcon, &ses->tcon_list, tcon_list) { +spin_lock (&tcon->tc_lock); +if (tcon->status ==TID_EXITING || + !cifs_swn_tcon_matches (tcon, swnreg->net_name, + swnreg->share_name)) { +spin_unlock (&tcon->tc_lock); +continue ; + } + ++tcon->tc_count; +trace_smb3_tcon_ref (tcon->debug_id, + tcon->tc_count, + netfs_trace_tcon_ref_get_swn_notify); +spin_unlock (&tcon->tc_lock); +spin_unlock (&cifs_tcp_ses_lock); +return tcon; + } + } + } +spin_unlock (&cifs_tcp_ses_lock); +return NULL ; +} + +/* * Try to find a matching registration for the tcon's server name and share name. * Calls to this function must be protected by cifs_swnreg_idr_mutex. * TODO Try to avoid memory allocations
@@ -350,8 +478,6 @@ static struct cifs_swn_reg *cifs_get_swn reg->net_name_notify = true; reg->share_name_notify = true; reg->ip_notify = (tcon->capabilities &SMB2_SHARE_CAP_SCALEOUT ); - - reg->tcon = tcon; unlock:mutex_unlock (&cifs_swnreg_idr_mutex);
@@ -371,11 +497,6 @@ fail_unlock:static void cifs_swn_reg_release (struct kref *ref) { struct cifs_swn_reg *swnreg = container_of(ref, struct cifs_swn_reg, ref_count); -int ret; - - ret =cifs_swn_send_unregister_message (swnreg); -if (ret <0 ) -cifs_dbg (VFS ,"%s: Failed to send unregister message: %d\n" , __func__, ret);idr_remove (&cifs_swnreg_idr, swnreg->id);kfree (swnreg->net_name);
@@ -383,23 +504,33 @@ static void cifs_swn_reg_release(structkfree (swnreg); } -static void cifs_put_swn_reg (struct cifs_swn_reg *swnreg) +static void cifs_put_swn_reg_locked (struct cifs_swn_reg *swnreg, +struct cifs_tcon *tcon) { -mutex_lock (&cifs_swnreg_idr_mutex); +if (kref_read (&swnreg->ref_count) ==1 ) { +struct cifs_swn_reg_info swnreg_info; +int ret; + +cifs_swn_snapshot_reg (swnreg, &swnreg_info); + ret =cifs_swn_send_unregister_message (&swnreg_info, tcon); +if (ret <0 ) +cifs_dbg (VFS ,"%s: Failed to send unregister message: %d\n" , + __func__, ret); + } +kref_put (&swnreg->ref_count, cifs_swn_reg_release); -mutex_unlock (&cifs_swnreg_idr_mutex); } -static int cifs_swn_resource_state_changed (struct cifs_swn_reg *swnreg,const char *name,int state) +static int cifs_swn_resource_state_changed (struct cifs_tcon *tcon,const char *name,int state) {switch (state) {case CIFS_SWN_RESOURCE_STATE_UNAVAILABLE :cifs_dbg (FYI ,"%s: resource name '%s' become unavailable\n" , __func__, name); -cifs_signal_cifsd_for_reconnect (swnreg->tcon->ses->server, true); +cifs_signal_cifsd_for_reconnect (tcon->ses->server, true);break ;case CIFS_SWN_RESOURCE_STATE_AVAILABLE :cifs_dbg (FYI ,"%s: resource name '%s' become available\n" , __func__, name); -cifs_signal_cifsd_for_reconnect (swnreg->tcon->ses->server, true); +cifs_signal_cifsd_for_reconnect (tcon->ses->server, true);break ;case CIFS_SWN_RESOURCE_STATE_UNKNOWN :cifs_dbg (FYI ,"%s: resource name '%s' changed to unknown state\n" , __func__, name);
@@ -505,7 +636,7 @@ unlock:return ret; } -static int cifs_swn_client_move (struct cifs_swn_reg *swnreg,struct sockaddr_storage *addr) +static int cifs_swn_client_move (struct cifs_tcon *tcon,struct sockaddr_storage *addr) {struct sockaddr_in *ipv4 = (struct sockaddr_in *)addr;struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)addr;
@@ -515,14 +646,17 @@ static int cifs_swn_client_move(struct celse if (addr->ss_family ==AF_INET6 )cifs_dbg (FYI ,"%s: move to %pI6\n" , __func__, &ipv6->sin6_addr); -return cifs_swn_reconnect (swnreg->tcon, addr); +return cifs_swn_reconnect (tcon, addr); }int cifs_swn_notify (struct sk_buff *skb,struct genl_info *info) {struct cifs_swn_reg *swnreg; +struct cifs_swn_reg_info swnreg_info; +struct cifs_tcon *tcon;char name[256 ];int type; +int ret =0 ;if (info->attrs[CIFS_GENL_ATTR_SWN_REGISTRATION_ID ]) {int swnreg_id;
@@ -530,21 +664,34 @@ int cifs_swn_notify(struct sk_buff *skb, swnreg_id =nla_get_u32 (info->attrs[CIFS_GENL_ATTR_SWN_REGISTRATION_ID ]);mutex_lock (&cifs_swnreg_idr_mutex); swnreg =idr_find (&cifs_swnreg_idr, swnreg_id); -mutex_unlock (&cifs_swnreg_idr_mutex);if (swnreg ==NULL ) { +mutex_unlock (&cifs_swnreg_idr_mutex);cifs_dbg (FYI ,"%s: registration id %d not found\n" , __func__, swnreg_id);return -EINVAL ; } + ret =cifs_swn_dup_reg (swnreg, &swnreg_info); +mutex_unlock (&cifs_swnreg_idr_mutex); +if (ret) +return ret; }else {cifs_dbg (FYI ,"%s: missing registration id attribute\n" , __func__);return -EINVAL ; } + tcon =cifs_swn_get_tcon (&swnreg_info); +if (!tcon) { +cifs_dbg (FYI ,"%s: registration id %d has no live tcon\n" , + __func__, swnreg_info.id); + ret = -ENODEV ; + goto free_info; + } +if (info->attrs[CIFS_GENL_ATTR_SWN_NOTIFICATION_TYPE ]) { type =nla_get_u32 (info->attrs[CIFS_GENL_ATTR_SWN_NOTIFICATION_TYPE ]); }else {cifs_dbg (FYI ,"%s: missing notification type attribute\n" , __func__); -return -EINVAL ; + ret = -EINVAL ; + goto out; }switch (type) {
@@ -556,15 +703,18 @@ int cifs_swn_notify(struct sk_buff *skb, sizeof(name)); }else {cifs_dbg (FYI ,"%s: missing resource name attribute\n" , __func__); -return -EINVAL ; + ret = -EINVAL ; + goto out; }if (info->attrs[CIFS_GENL_ATTR_SWN_RESOURCE_STATE ]) { state =nla_get_u32 (info->attrs[CIFS_GENL_ATTR_SWN_RESOURCE_STATE ]); }else {cifs_dbg (FYI ,"%s: missing resource state attribute\n" , __func__); -return -EINVAL ; + ret = -EINVAL ; + goto out; } -return cifs_swn_resource_state_changed (swnreg, name, state); + ret =cifs_swn_resource_state_changed (tcon, name, state); +break ; }case CIFS_SWN_NOTIFICATION_CLIENT_MOVE : {struct sockaddr_storage addr;
@@ -573,28 +723,36 @@ int cifs_swn_notify(struct sk_buff *skb,nla_memcpy (&addr, info->attrs[CIFS_GENL_ATTR_SWN_IP ],sizeof (addr)); }else {cifs_dbg (FYI ,"%s: missing IP address attribute\n" , __func__); -return -EINVAL ; + ret = -EINVAL ; + goto out; } -return cifs_swn_client_move (swnreg, &addr); + ret =cifs_swn_client_move (tcon, &addr); +break ; }default :cifs_dbg (FYI ,"%s: unknown notification type %d\n" , __func__, type);break ; } -return 0 ; +out: +cifs_put_tcon (tcon, netfs_trace_tcon_ref_put_swn_notify); +free_info: +cifs_swn_free_reg_info (&swnreg_info); +return ret; }int cifs_swn_register (struct cifs_tcon *tcon) {struct cifs_swn_reg *swnreg; +struct cifs_swn_reg_info swnreg_info;int ret; swnreg =cifs_get_swn_reg (tcon);if (IS_ERR (swnreg))return PTR_ERR (swnreg); - ret =cifs_swn_send_register_message (swnreg); +cifs_swn_snapshot_reg (swnreg, &swnreg_info); + ret =cifs_swn_send_register_message (&swnreg_info, tcon);if (ret <0 ) {cifs_dbg (VFS ,"%s: Failed to send swn register message: %d\n" , __func__, ret);/* Do not put the swnreg or return error, the echo task will retry */
@@ -615,35 +773,68 @@ int cifs_swn_unregister(struct cifs_tconreturn PTR_ERR (swnreg); } +cifs_put_swn_reg_locked (swnreg, tcon);mutex_unlock (&cifs_swnreg_idr_mutex); -cifs_put_swn_reg (swnreg); -return 0 ; } -void cifs_swn_dump (struct seq_file *m) +/* +* Snapshot one registration under cifs_swnreg_idr_mutex and return. Callers +* intentionally do the per-registration network/genlmsg work without the +* mutex held, both to keep the critical section short and to avoid nesting +* cifs_swnreg_idr_mutex inside the higher tc_lock when a live tcon is then +* pinned for the send. +*/ +static int cifs_swn_get_next_reg_info (int *id,struct cifs_swn_reg_info *info) {struct cifs_swn_reg *swnreg; +int ret =0 ; + +mutex_lock (&cifs_swnreg_idr_mutex); + swnreg =idr_get_next (&cifs_swnreg_idr, id); +if (swnreg) { + ret =cifs_swn_dup_reg (swnreg, info); +if (!ret) { + *id = swnreg->id +1 ; + ret =1 ; + } + } +mutex_unlock (&cifs_swnreg_idr_mutex); + +return ret; +} + +void cifs_swn_dump (struct seq_file *m) +{ +struct cifs_swn_reg_info swnreg_info; +struct cifs_tcon *tcon;struct sockaddr_in *sa;struct sockaddr_in6 *sa6; -int id; +int id =0 ; +int ret;seq_puts (m,"Witness registrations:" ); -mutex_lock (&cifs_swnreg_idr_mutex); -idr_for_each_entry (&cifs_swnreg_idr, swnreg, id) { +while ((ret =cifs_swn_get_next_reg_info (&id, &swnreg_info)) >0 ) {seq_printf (m,"\nId: %u Refs: %u Network name: '%s'%s Share name: '%s'%s Ip address: " , - id,kref_read (&swnreg->ref_count), - swnreg->net_name, swnreg->net_name_notify ?"(y)" :"(n)" , - swnreg->share_name, swnreg->share_name_notify ?"(y)" :"(n)" ); -switch (swnreg->tcon->ses->server->dstaddr.ss_family) { + swnreg_info.id, swnreg_info.ref_count, + swnreg_info.net_name, swnreg_info.net_name_notify ?"(y)" :"(n)" , + swnreg_info.share_name, swnreg_info.share_name_notify ?"(y)" :"(n)" ); + + tcon =cifs_swn_get_tcon (&swnreg_info); +if (!tcon) { +seq_puts (m,"(no live tcon)" ); + goto next; + } + +switch (tcon->ses->server->dstaddr.ss_family) {case AF_INET : - sa = (struct sockaddr_in *) &swnreg->tcon->ses->server->dstaddr; + sa = (struct sockaddr_in *)&tcon->ses->server->dstaddr;seq_printf (m,"%pI4" , &sa->sin_addr.s_addr);break ;case AF_INET6 : - sa6 = (struct sockaddr_in6 *) &swnreg->tcon->ses->server->dstaddr; + sa6 = (struct sockaddr_in6 *)&tcon->ses->server->dstaddr;seq_printf (m,"%pI6" , &sa6->sin6_addr.s6_addr);if (sa6->sin6_scope_id)seq_printf (m,"%%%u" , sa6->sin6_scope_id);
@@ -651,23 +842,38 @@ void cifs_swn_dump(struct seq_file *m)default :seq_puts (m,"(unknown)" ); } -seq_printf (m,"%s" , swnreg->ip_notify ?"(y)" :"(n)" ); +cifs_put_tcon (tcon, netfs_trace_tcon_ref_put_swn_notify); +next: +seq_printf (m,"%s" , swnreg_info.ip_notify ?"(y)" :"(n)" ); +cifs_swn_free_reg_info (&swnreg_info); } -mutex_unlock (&cifs_swnreg_idr_mutex); +if (ret <0 ) +seq_printf (m,"\nFailed to snapshot witness registration: %d" , ret);seq_puts (m,"\n" ); }void cifs_swn_check (void ) { -struct cifs_swn_reg *swnreg; -int id; +struct cifs_swn_reg_info swnreg_info; +struct cifs_tcon *tcon; +int id =0 ;int ret; -mutex_lock (&cifs_swnreg_idr_mutex); -idr_for_each_entry (&cifs_swnreg_idr, swnreg, id) { - ret =cifs_swn_send_register_message (swnreg); +while ((ret =cifs_swn_get_next_reg_info (&id, &swnreg_info)) >0 ) { + tcon =cifs_swn_get_tcon (&swnreg_info); +if (!tcon) { +cifs_dbg (FYI ,"%s: registration id %d has no live tcon\n" , + __func__, swnreg_info.id); + goto free_info; + } + + ret =cifs_swn_send_register_message (&swnreg_info, tcon);if (ret <0 )cifs_dbg (FYI ,"%s: Failed to send register message: %d\n" , __func__, ret); +cifs_put_tcon (tcon, netfs_trace_tcon_ref_put_swn_notify); +free_info: +cifs_swn_free_reg_info (&swnreg_info); } -mutex_unlock (&cifs_swnreg_idr_mutex); +if (ret <0 ) +cifs_dbg (FYI ,"%s: Failed to snapshot registration: %d\n" , __func__, ret); }
fs/smb/client/trace.h +2 -0
@@ -32,6 +32,7 @@ EM(netfs_trace_tcon_ref_get_find, "GET Find ") \ EM(netfs_trace_tcon_ref_get_find_sess_tcon, "GET FndSes") \ EM(netfs_trace_tcon_ref_get_reconnect_server, "GET Reconn") \ + EM(netfs_trace_tcon_ref_get_swn_notify, "GET SwnNot") \ EM(netfs_trace_tcon_ref_new, "NEW ") \ EM(netfs_trace_tcon_ref_new_ipc, "NEW Ipc ") \EM (netfs_trace_tcon_ref_new_reconnect_server,"NEW Reconn" ) \
@@ -41,6 +42,7 @@ EM(netfs_trace_tcon_ref_put_mnt_ctx, "PUT MntCtx") \ EM(netfs_trace_tcon_ref_put_dfs_refer, "PUT DfsRfr") \ EM(netfs_trace_tcon_ref_put_reconnect_server, "PUT Reconn") \ + EM(netfs_trace_tcon_ref_put_swn_notify, "PUT SwnNot") \ EM(netfs_trace_tcon_ref_put_tlink, "PUT Tlink ") \ EM(netfs_trace_tcon_ref_see_cancelled_close, "SEE Cn-Cls") \EM (netfs_trace_tcon_ref_see_fscache_collision,"SEE FV-CO!" ) \
Greg Kroah-Hartman
maintainerPATCH 998/1067 ksmbd: use opener credentials for FSCTL mutations
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Namjae Jeon <[email protected]>
[ Upstream commit c6394bcaf254c5baf9aff43376020be5db6d3316 ]
SET_SPARSE, SET_ZERO_DATA and SET_COMPRESSION operate on an open SMB handle but call VFS xattr, fallocate or fileattr helpers with the current ksmbd worker credentials. Those helpers can revalidate inode permissions, ownership and LSM policy independently of the SMB handle access mask.
Run each operation with the credentials captured in the target file when the handle was opened. Keep credential handling local to these single-file FSCTLs rather than applying session credentials to the complete IOCTL handler, which also contains handle-less and multi-handle operations.
--- fs/smb/server/smb2pdu.c | 3 +++ fs/smb/server/vfs.c | 20 +++++++++++++------- 2 files changed, 16 insertions(+), 7 deletions(-)
fs/smb/server/smb2pdu.c +3 -0
@@ -7739,6 +7739,7 @@ static inline int fsctl_set_sparse(strucif (fp->f_ci->m_fattr != old_fattr &&test_share_config_flag (work->tcon->share_conf,KSMBD_SHARE_FLAG_STORE_DOS_ATTRS )) { +const struct cred *saved_cred;struct xattr_dos_attrib da; ret = ksmbd_vfs_get_dos_attrib_xattr(user_ns,
@@ -7747,9 +7748,11 @@ static inline int fsctl_set_sparse(struc goto out; da.attr =le32_to_cpu (fp->f_ci->m_fattr); + saved_cred =override_creds (fp->filp->f_cred); ret =ksmbd_vfs_set_dos_attrib_xattr (user_ns, &fp->filp->f_path, &da, true); +revert_creds (saved_cred);if (ret) fp->f_ci->m_fattr = old_fattr; }
fs/smb/server/vfs.c +13 -7
@@ -991,15 +991,21 @@ void ksmbd_vfs_set_fadvise(struct file *int ksmbd_vfs_zero_data (struct ksmbd_work *work,struct ksmbd_file *fp,loff_t off,loff_t len) { +const struct cred *saved_cred; +int err; +smb_break_all_levII_oplock (work, fp,1 ); + saved_cred =override_creds (fp->filp->f_cred);if (fp->f_ci->m_fattr &FILE_ATTRIBUTE_SPARSE_FILE_LE ) -return vfs_fallocate (fp->filp, -FALLOC_FL_PUNCH_HOLE |FALLOC_FL_KEEP_SIZE , - off, len); - -return vfs_fallocate (fp->filp, -FALLOC_FL_ZERO_RANGE |FALLOC_FL_KEEP_SIZE , - off, len); + err =vfs_fallocate (fp->filp, +FALLOC_FL_PUNCH_HOLE |FALLOC_FL_KEEP_SIZE , + off, len); +else + err =vfs_fallocate (fp->filp, +FALLOC_FL_ZERO_RANGE |FALLOC_FL_KEEP_SIZE , + off, len); +revert_creds (saved_cred); +return err; } int ksmbd_vfs_fqar_lseek(struct ksmbd_file *fp, loff_t start, loff_t length,
Greg Kroah-Hartman
maintainerPATCH 999/1067 smb: move some duplicate definitions to common/cifsglob.h
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: ZhangGuoDong <[email protected]>
[ Upstream commit d877470b59910b5c50383d634dda3782386bba51 ]
In order to maintain the code more easily, move duplicate definitions to new common header file.
Stable-dep-of: c1016dd1d8b2 ("ksmbd: track the connection owning a byte-range lock")
--- fs/smb/client/cifsglob.h | 19 +------------------ fs/smb/common/cifsglob.h | 30 ++++++++++++++++++++++++++++++ fs/smb/server/smb_common.h | 14 +------------- 3 files changed, 32 insertions(+), 31 deletions(-) create mode 100644 fs/smb/common/cifsglob.h
fs/smb/client/cifsglob.h +1 -18
@@ -26,6 +26,7 @@#include <crypto/internal/hash.h> #include <linux/scatterlist.h> #include <uapi/linux/cifs/cifs_mount.h> +#include "../common/cifsglob.h" #include "../common/smb2pdu.h" #include "smb2pdu.h"
@@ -606,12 +607,6 @@ get_rfc1002_length(void *buf)return be32_to_cpu (*((__be32 *)buf)) &0xffffff ; } -static inline void -inc_rfc1001_len (void *buf,int count) -{ -be32_add_cpu ((__be32 *)buf, count); -} -struct TCP_Server_Info {struct list_head tcp_ses_list;struct list_head smb_ses_list;
@@ -913,8 +908,6 @@ compare_mid(__u16 mid, const struct smb_#define CIFS_MAX_RFC1002_WSIZE ((1<<17) - 1 - sizeof(WRITE_REQ) + 4)#define CIFS_MAX_RFC1002_RSIZE ((1<<17) - 1 - sizeof(READ_RSP) + 4) -#define CIFS_DEFAULT_IOSIZE (1024 * 1024) -/* * Windows only supports a max of 60kb reads and 65535 byte writes. Default to * those values when posix extensions aren't in force. In actuality here, we
@@ -2022,30 +2015,20 @@ extern __u32 cifs_lock_secret;extern mempool_t *cifs_mid_poolp;/* Operations for different SMB versions */ -#define SMB1_VERSION_STRING "1.0" -#define SMB20_VERSION_STRING "2.0"#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY extern struct smb_version_operations smb1_operations;extern struct smb_version_values smb1_values;extern struct smb_version_operations smb20_operations;extern struct smb_version_values smb20_values;#endif /* CIFS_ALLOW_INSECURE_LEGACY */ -#define SMB21_VERSION_STRING "2.1"extern struct smb_version_operations smb21_operations;extern struct smb_version_values smb21_values; -#define SMBDEFAULT_VERSION_STRING "default"extern struct smb_version_values smbdefault_values; -#define SMB3ANY_VERSION_STRING "3"extern struct smb_version_values smb3any_values; -#define SMB30_VERSION_STRING "3.0"extern struct smb_version_operations smb30_operations;extern struct smb_version_values smb30_values; -#define SMB302_VERSION_STRING "3.02" -#define ALT_SMB302_VERSION_STRING "3.0.2"/*extern struct smb_version_operations smb302_operations;*/ /* not needed yet */ extern struct smb_version_values smb302_values; -#define SMB311_VERSION_STRING "3.1.1" -#define ALT_SMB311_VERSION_STRING "3.11"extern struct smb_version_operations smb311_operations;extern struct smb_version_values smb311_values;
fs/smb/common/cifsglob.h new file +30 -0
@@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: LGPL-2.1 */ +/* +* +* Copyright (C) International Business Machines Corp., 2002,2008 +* Author(s): Steve French ([email protected]) +* Jeremy Allison ([email protected]) +* +*/ +#ifndef _COMMON_CIFS_GLOB_H +#define _COMMON_CIFS_GLOB_H + +static inline void inc_rfc1001_len (void *buf,int count) +{ +be32_add_cpu ((__be32 *)buf, count); +} + +#define SMB1_VERSION_STRING "1.0" +#define SMB20_VERSION_STRING "2.0" +#define SMB21_VERSION_STRING "2.1" +#define SMBDEFAULT_VERSION_STRING "default" +#define SMB3ANY_VERSION_STRING "3" +#define SMB30_VERSION_STRING "3.0" +#define SMB302_VERSION_STRING "3.02" +#define ALT_SMB302_VERSION_STRING "3.0.2" +#define SMB311_VERSION_STRING "3.1.1" +#define ALT_SMB311_VERSION_STRING "3.11" + +#define CIFS_DEFAULT_IOSIZE (1024 * 1024) + +#endif /* _COMMON_CIFS_GLOB_H */
fs/smb/server/smb_common.h +1 -13
@@ -10,6 +10,7 @@#include "glob.h" #include "nterr.h" +#include "../common/cifsglob.h" #include "../common/smb2pdu.h" #include "smb2pdu.h"
@@ -26,16 +27,8 @@#define SMB311_PROT 6#define BAD_PROT 0xFFFF -#define SMB1_VERSION_STRING "1.0" -#define SMB20_VERSION_STRING "2.0" -#define SMB21_VERSION_STRING "2.1" -#define SMB30_VERSION_STRING "3.0" -#define SMB302_VERSION_STRING "3.02" -#define SMB311_VERSION_STRING "3.1.1" -#define SMB_ECHO_INTERVAL (60 * HZ) -#define CIFS_DEFAULT_IOSIZE (64 * 1024)#define MAX_CIFS_SMALL_BUFFER_SIZE 448/* big enough for most */ #define MAX_STREAM_PROT_LEN 0x00FFFFFF
@@ -462,9 +455,4 @@ static inline unsigned int get_rfc1002_l {return be32_to_cpu (*((__be32 *)buf)) &0xffffff ; } - -static inline void inc_rfc1001_len (void *buf,int count) -{ -be32_add_cpu ((__be32 *)buf, count); -}#endif /* __SMB_COMMON_H__ */
Greg Kroah-Hartman
maintainerPATCH 1000/1067 smb: move smb_version_values to common/smbglob.h
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: ZhangGuoDong <[email protected]>
[ Upstream commit 34cf191bb6a349dc88ec2c4f6355fe006ac669e0 ]
Merge the struct members of the server and the client:
- req_capabilities: from client
- header_preamble_size: from client
- cap_unicode: from client
- capabilities: from server, rename to req_capabilities
- max_read_size: from server
- max_write_size: from server
- max_trans_size: from server
- max_credits: from server
- create_durable_size: from server
- create_durable_v2_size: from server
- create_mxac_size: from server
- create_disk_id_size: from server
- create_posix_size: from server
Then move duplicate definitions to common header file.
Stable-dep-of: c1016dd1d8b2 ("ksmbd: track the connection owning a byte-range lock")
--- fs/smb/client/cifsglob.h | 21 --------------------- fs/smb/common/cifsglob.h | 31 +++++++++++++++++++++++++++++++ fs/smb/server/smb2misc.c | 2 +- fs/smb/server/smb2ops.c | 28 ++++++++++++++-------------- fs/smb/server/smb2pdu.c | 10 +++++----- fs/smb/server/smb_common.h | 29 ----------------------------- 6 files changed, 51 insertions(+), 70 deletions(-)
fs/smb/client/cifsglob.h +0 -21
@@ -543,27 +543,6 @@ struct smb_version_operations {void (*is_network_name_deleted)(char *buf,struct TCP_Server_Info *srv); }; -struct smb_version_values { -char *version_string; -__u16 protocol_id; -__u32 req_capabilities; -__u32 large_lock_type; -__u32 exclusive_lock_type; -__u32 shared_lock_type; -__u32 unlock_lock_type; -size_t header_preamble_size; -size_t header_size; -size_t max_header_size; -size_t read_rsp_size; -__le16 lock_cmd; -unsigned int cap_unix; -unsigned int cap_nt_find; -unsigned int cap_large_files; -__u16 signing_enabled; -__u16 signing_required; -size_t create_lease_size; -}; -#define HEADER_SIZE (server) (server->vals->header_size)#define MAX_HEADER_SIZE (server) (server->vals->max_header_size)#define HEADER_PREAMBLE_SIZE (server) (server->vals->header_preamble_size)
fs/smb/common/cifsglob.h +31 -0
@@ -9,6 +9,37 @@#ifndef _COMMON_CIFS_GLOB_H#define _COMMON_CIFS_GLOB_H +struct smb_version_values { +char *version_string; +__u16 protocol_id; +__le16 lock_cmd; +__u32 req_capabilities; +__u32 max_read_size; +__u32 max_write_size; +__u32 max_trans_size; +__u32 max_credits; +__u32 large_lock_type; +__u32 exclusive_lock_type; +__u32 shared_lock_type; +__u32 unlock_lock_type; +size_t header_preamble_size; +size_t header_size; +size_t max_header_size; +size_t read_rsp_size; +unsigned int cap_unix; +unsigned int cap_nt_find; +unsigned int cap_large_files; +unsigned int cap_unicode; +__u16 signing_enabled; +__u16 signing_required; +size_t create_lease_size; +size_t create_durable_size; +size_t create_durable_v2_size; +size_t create_mxac_size; +size_t create_disk_id_size; +size_t create_posix_size; +}; +static inline void inc_rfc1001_len (void *buf,int count) {be32_add_cpu ((__be32 *)buf, count);
fs/smb/server/smb2misc.c +1 -1
@@ -460,7 +460,7 @@ int ksmbd_smb2_check_message(struct ksmb } validate_credit: -if ((work->conn->vals->capabilities &SMB2_GLOBAL_CAP_LARGE_MTU ) && +if ((work->conn->vals->req_capabilities &SMB2_GLOBAL_CAP_LARGE_MTU ) &&smb2_validate_credit_charge (work->conn, hdr))return 1 ;
fs/smb/server/smb2ops.c +14 -14
@@ -15,7 +15,7 @@static struct smb_version_values smb21_server_values = { .version_string =SMB21_VERSION_STRING , .protocol_id =SMB21_PROT_ID , - .capabilities =SMB2_GLOBAL_CAP_LARGE_MTU , + .req_capabilities =SMB2_GLOBAL_CAP_LARGE_MTU , .max_read_size =SMB21_DEFAULT_IOSIZE , .max_write_size =SMB21_DEFAULT_IOSIZE , .max_trans_size =SMB21_DEFAULT_IOSIZE ,
@@ -41,7 +41,7 @@ static struct smb_version_values smb21_sstatic struct smb_version_values smb30_server_values = { .version_string =SMB30_VERSION_STRING , .protocol_id =SMB30_PROT_ID , - .capabilities =SMB2_GLOBAL_CAP_LARGE_MTU , + .req_capabilities =SMB2_GLOBAL_CAP_LARGE_MTU , .max_read_size =SMB3_DEFAULT_IOSIZE , .max_write_size =SMB3_DEFAULT_IOSIZE , .max_trans_size =SMB3_DEFAULT_TRANS_SIZE ,
@@ -68,7 +68,7 @@ static struct smb_version_values smb30_sstatic struct smb_version_values smb302_server_values = { .version_string =SMB302_VERSION_STRING , .protocol_id =SMB302_PROT_ID , - .capabilities =SMB2_GLOBAL_CAP_LARGE_MTU , + .req_capabilities =SMB2_GLOBAL_CAP_LARGE_MTU , .max_read_size =SMB3_DEFAULT_IOSIZE , .max_write_size =SMB3_DEFAULT_IOSIZE , .max_trans_size =SMB3_DEFAULT_TRANS_SIZE ,
@@ -95,7 +95,7 @@ static struct smb_version_values smb302_static struct smb_version_values smb311_server_values = { .version_string =SMB311_VERSION_STRING , .protocol_id =SMB311_PROT_ID , - .capabilities =SMB2_GLOBAL_CAP_LARGE_MTU , + .req_capabilities =SMB2_GLOBAL_CAP_LARGE_MTU , .max_read_size =SMB3_DEFAULT_IOSIZE , .max_write_size =SMB3_DEFAULT_IOSIZE , .max_trans_size =SMB3_DEFAULT_TRANS_SIZE ,
@@ -204,7 +204,7 @@ void init_smb2_1_server(struct ksmbd_con conn->signing_algorithm =SIGNING_ALG_HMAC_SHA256_LE ;if (server_conf.flags &KSMBD_GLOBAL_FLAG_SMB2_LEASES ) - conn->vals->capabilities |=SMB2_GLOBAL_CAP_LEASING ; + conn->vals->req_capabilities |=SMB2_GLOBAL_CAP_LEASING ; }/**
@@ -221,20 +221,20 @@ void init_smb3_0_server(struct ksmbd_con conn->signing_algorithm =SIGNING_ALG_AES_CMAC_LE ;if (server_conf.flags &KSMBD_GLOBAL_FLAG_SMB2_LEASES ) - conn->vals->capabilities |=SMB2_GLOBAL_CAP_LEASING | + conn->vals->req_capabilities |=SMB2_GLOBAL_CAP_LEASING |SMB2_GLOBAL_CAP_DIRECTORY_LEASING ;if (server_conf.flags &KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION && conn->cli_cap &SMB2_GLOBAL_CAP_ENCRYPTION ) - conn->vals->capabilities |=SMB2_GLOBAL_CAP_ENCRYPTION ; + conn->vals->req_capabilities |=SMB2_GLOBAL_CAP_ENCRYPTION ;if (server_conf.flags &KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION || (!(server_conf.flags &KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION_OFF ) && conn->cli_cap &SMB2_GLOBAL_CAP_ENCRYPTION )) - conn->vals->capabilities |=SMB2_GLOBAL_CAP_ENCRYPTION ; + conn->vals->req_capabilities |=SMB2_GLOBAL_CAP_ENCRYPTION ;if (server_conf.flags &KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL ) - conn->vals->capabilities |=SMB2_GLOBAL_CAP_MULTI_CHANNEL ; + conn->vals->req_capabilities |=SMB2_GLOBAL_CAP_MULTI_CHANNEL ; }/**
@@ -251,16 +251,16 @@ void init_smb3_02_server(struct ksmbd_co conn->signing_algorithm =SIGNING_ALG_AES_CMAC_LE ;if (server_conf.flags &KSMBD_GLOBAL_FLAG_SMB2_LEASES ) - conn->vals->capabilities |=SMB2_GLOBAL_CAP_LEASING | + conn->vals->req_capabilities |=SMB2_GLOBAL_CAP_LEASING |SMB2_GLOBAL_CAP_DIRECTORY_LEASING ;if (server_conf.flags &KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION || (!(server_conf.flags &KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION_OFF ) && conn->cli_cap &SMB2_GLOBAL_CAP_ENCRYPTION )) - conn->vals->capabilities |=SMB2_GLOBAL_CAP_ENCRYPTION ; + conn->vals->req_capabilities |=SMB2_GLOBAL_CAP_ENCRYPTION ;if (server_conf.flags &KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL ) - conn->vals->capabilities |=SMB2_GLOBAL_CAP_MULTI_CHANNEL ; + conn->vals->req_capabilities |=SMB2_GLOBAL_CAP_MULTI_CHANNEL ; }/**
@@ -277,11 +277,11 @@ int init_smb3_11_server(struct ksmbd_con conn->signing_algorithm =SIGNING_ALG_AES_CMAC_LE ;if (server_conf.flags &KSMBD_GLOBAL_FLAG_SMB2_LEASES ) - conn->vals->capabilities |=SMB2_GLOBAL_CAP_LEASING | + conn->vals->req_capabilities |=SMB2_GLOBAL_CAP_LEASING |SMB2_GLOBAL_CAP_DIRECTORY_LEASING ;if (server_conf.flags &KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL ) - conn->vals->capabilities |=SMB2_GLOBAL_CAP_MULTI_CHANNEL ; + conn->vals->req_capabilities |=SMB2_GLOBAL_CAP_MULTI_CHANNEL ;INIT_LIST_HEAD (&conn->preauth_sess_table);return 0 ;
fs/smb/server/smb2pdu.c +5 -5
@@ -283,7 +283,7 @@ int init_smb2_neg_rsp(struct ksmbd_work/* Not setting conn guid rsp->ServerGUID, as it * not used by client for identifying connection */ - rsp->Capabilities =cpu_to_le32 (conn->vals->capabilities); + rsp->Capabilities =cpu_to_le32 (conn->vals->req_capabilities);/* Default Max Message Size till SMB2.0, 64K*/ rsp->MaxTransactSize =cpu_to_le32 (conn->vals->max_trans_size); rsp->MaxReadSize =cpu_to_le32 (conn->vals->max_read_size);
@@ -975,7 +975,7 @@ bool smb3_encryption_negotiated(struct k* SMB 3.0 and 3.0.2 dialects use the SMB2_GLOBAL_CAP_ENCRYPTION flag. * SMB 3.1.1 uses the cipher_type field. */ -return (conn->vals->capabilities &SMB2_GLOBAL_CAP_ENCRYPTION ) || +return (conn->vals->req_capabilities &SMB2_GLOBAL_CAP_ENCRYPTION ) || conn->cipher_type; }
@@ -1228,7 +1228,7 @@ int smb2_handle_negotiate(struct ksmbd_w rc = -EINVAL ; goto err_out; } - rsp->Capabilities =cpu_to_le32 (conn->vals->capabilities); + rsp->Capabilities =cpu_to_le32 (conn->vals->req_capabilities);/* For stats */ conn->connection_type = conn->dialect;
@@ -3254,7 +3254,7 @@ int smb2_open(struct ksmbd_work *work) share_ret =ksmbd_smb_check_shared_mode (fp->filp, fp);if (!test_share_config_flag (work->tcon->share_conf,KSMBD_SHARE_FLAG_OPLOCKS ) || (req_op_level ==SMB2_OPLOCK_LEVEL_LEASE && - !(conn->vals->capabilities &SMB2_GLOBAL_CAP_LEASING ))) { + !(conn->vals->req_capabilities &SMB2_GLOBAL_CAP_LEASING ))) {if (share_ret <0 && !S_ISDIR (file_inode (fp->filp)->i_mode)) { rc = share_ret; goto err_out1;
@@ -7632,7 +7632,7 @@ static int fsctl_validate_negotiate_info goto err_out; } - neg_rsp->Capabilities =cpu_to_le32 (conn->vals->capabilities); + neg_rsp->Capabilities =cpu_to_le32 (conn->vals->req_capabilities);memset (neg_rsp->Guid,0 ,SMB2_CLIENT_GUID_SIZE ); neg_rsp->SecurityMode =cpu_to_le16 (conn->srv_sec_mode); neg_rsp->Dialect =cpu_to_le16 (conn->dialect);
fs/smb/server/smb_common.h +0 -29
@@ -336,35 +336,6 @@ struct file_id_full_dir_info {char FileName[]; } __packed;/* level 0x105 FF rsp data */ -struct smb_version_values { -char *version_string; -__u16 protocol_id; -__le16 lock_cmd; -__u32 capabilities; -__u32 max_read_size; -__u32 max_write_size; -__u32 max_trans_size; -__u32 max_credits; -__u32 large_lock_type; -__u32 exclusive_lock_type; -__u32 shared_lock_type; -__u32 unlock_lock_type; -size_t header_size; -size_t max_header_size; -size_t read_rsp_size; -unsigned int cap_unix; -unsigned int cap_nt_find; -unsigned int cap_large_files; -__u16 signing_enabled; -__u16 signing_required; -size_t create_lease_size; -size_t create_durable_size; -size_t create_durable_v2_size; -size_t create_mxac_size; -size_t create_disk_id_size; -size_t create_posix_size; -}; -struct filesystem_posix_info {/* For undefined recommended transfer size return -1 in that field */ __le32 OptimalTransferSize;/* bsize on some os, iosize on other os */
Greg Kroah-Hartman
maintainerPATCH 1001/1067 ksmbd: fix use-after-free in __ksmbd_close_fd() via durable scavenger
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Namjae Jeon <[email protected]>
[ Upstream commit 235e32320a470fcd3998fb3774f2290a0eb302a1 ]
When a durable file handle survives session disconnect (TCP close without SMB2_LOGOFF), session_fd_check() sets fp->conn = NULL to preserve the handle for later reconnection. However, it did not clean up the byte-range locks on fp->lock_list.
Later, when the durable scavenger thread times out and calls __ksmbd_close_fd(NULL, fp), the lock cleanup loop did:
spin_lock(&fp->conn->llist_lock);
This caused a slab use-after-free because fp->conn was NULL and the original connection object had already been freed by ksmbd_tcp_disconnect().
The root cause is asymmetric cleanup: lock entries (smb_lock->clist) were left dangling on the freed conn->lock_list while fp->conn was nulled out.
To fix this issue properly, we need to handle the lifetime of smb_lock->clist across three paths:
- Safely skip clist deletion when list is empty and fp->conn is NULL.
- Remove the lock from the old connection's lock_list in session_fd_check()
- Re-add the lock to the new connection's lock_list in ksmbd_reopen_durable_fd().
Stable-dep-of: c1016dd1d8b2 ("ksmbd: track the connection owning a byte-range lock")
--- fs/smb/server/vfs_cache.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-)
fs/smb/server/vfs_cache.c +5 -3
@@ -317,9 +317,11 @@ static void __ksmbd_close_fd(struct ksmb* there are not accesses to fp->lock_list. */ list_for_each_entry_safe (smb_lock, tmp_lock, &fp->lock_list, flist) { -spin_lock (&fp->conn->llist_lock); -list_del (&smb_lock->clist); -spin_unlock (&fp->conn->llist_lock); +if (!list_empty (&smb_lock->clist) && fp->conn) { +spin_lock (&fp->conn->llist_lock); +list_del (&smb_lock->clist); +spin_unlock (&fp->conn->llist_lock); + }list_del (&smb_lock->flist);locks_free_lock (smb_lock->fl);
Greg Kroah-Hartman
maintainerPATCH 1002/1067 ksmbd: fix mechToken leak when SPNEGO decode fails after token alloc
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Greg Kroah-Hartman <[email protected]>
[ Upstream commit ad0057fb91218914d6c98268718ceb9d59b388e1 ]
The kernel ASN.1 BER decoder calls action callbacks incrementally as it walks the input. When ksmbd_decode_negTokenInit() reaches the mechToken
[2] OCTET STRING element, ksmbd_neg_token_alloc() allocates conn->mechToken immediately via kmemdup_nul(). If a later element in the same blob is malformed, then the decoder will return nonzero after the allocation is already live. This could happen if mechListMIC [3] overrunse the enclosing SEQUENCE.
decode_negotiation_token() then sets conn->use_spnego = false because both the negTokenInit and negTokenTarg grammars failed. The cleanup at the bottom of smb2_sess_setup() is gated on use_spnego:
if (conn->use_spnego && conn->mechToken) {
kfree(conn->mechToken);
conn->mechToken = NULL;
}so the kfree is skipped, causing the mechToken to never be freed.
This codepath is reachable pre-authentication, so untrusted clients can cause slow memory leaks on a server without even being properly authenticated.
Fix this up by not checking check for use_spnego, as it's not required, so the memory will always be properly freed. At the same time, always free the memory in ksmbd_conn_free() incase some other failure path forgot to free it.
Stable-dep-of: c1016dd1d8b2 ("ksmbd: track the connection owning a byte-range lock")
--- fs/smb/server/connection.c | 1 + fs/smb/server/smb2pdu.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-)
fs/smb/server/connection.c +1 -0
@@ -39,6 +39,7 @@ void ksmbd_conn_free(struct ksmbd_conn *xa_destroy (&conn->sessions);kvfree (conn->request_buf);kfree (conn->preauth_info); +kfree (conn->mechToken);kfree (conn); }
fs/smb/server/smb2pdu.c +1 -1
@@ -1909,7 +1909,7 @@ out_err:else if (rc) rsp->hdr.Status =STATUS_LOGON_FAILURE ; -if (conn->use_spnego && conn->mechToken) { +if (conn->mechToken) {kfree (conn->mechToken); conn->mechToken =NULL ; }
Greg Kroah-Hartman
maintainerPATCH 1003/1067 ksmbd: destroy async_ida in ksmbd_conn_free()
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: DaeMyung Kang <[email protected]>
[ Upstream commit b32c8db48212a34998c36d0bbc05b29d5c407ef5 ]
When per-connection async_ida was converted from a dynamically allocated ksmbd_ida to an embedded struct ida, ksmbd_ida_free() was removed from the connection teardown path but no matching ida_destroy() was added. The connection is therefore freed with the IDA's backing xarray still intact.
The kernel IDA API expects ida_init() and ida_destroy() to be paired over an object's lifetime, so add the missing cleanup before the connection is freed.
No leak has been observed in testing; this is a pairing fix to match the IDA lifetime rules, not a response to a reproduced regression.
Stable-dep-of: c1016dd1d8b2 ("ksmbd: track the connection owning a byte-range lock")
--- fs/smb/server/connection.c | 1 + 1 file changed, 1 insertion(+)
fs/smb/server/connection.c +1 -0
@@ -40,6 +40,7 @@ void ksmbd_conn_free(struct ksmbd_conn *kvfree (conn->request_buf);kfree (conn->preauth_info);kfree (conn->mechToken); +ida_destroy (&conn->async_ida);kfree (conn); }
Greg Kroah-Hartman
maintainerPATCH 1004/1067 ksmbd: centralize ksmbd_conn final release to plug transport leak
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: DaeMyung Kang <[email protected]>
[ Upstream commit b1f1e80620deb49daf63c2e677046599b693dc1f ]
ksmbd_conn_free() is one of four sites that can observe the last refcount drop of a struct ksmbd_conn. The other three
fs/smb/server/connection.c ksmbd_conn_r_count_dec()
fs/smb/server/oplock.c __free_opinfo()
fs/smb/server/vfs_cache.c session_fd_check()end the conn with a bare kfree(), skipping ida_destroy(&conn->async_ida) and conn->transport->ops->free_transport(conn->transport). Whenever one of them is the last putter, the embedded async_ida and the entire transport struct leak -- for TCP, that is also the struct socket and the kvec iov.
__free_opinfo() being a final putter is not theoretical. opinfo_put() queues the callback via call_rcu(&opinfo->rcu, free_opinfo_rcu), so ksmbd_server_terminate_conn() can deposit N opinfo releases in RCU and have ksmbd_conn_free() run in the handler thread before any of them fire. ksmbd_conn_free() then observes refcnt > 0 and short-circuits; the last RCU-delivered __free_opinfo() falls onto its bare kfree(conn) branch and the transport is lost.
A/B validation in a QEMU/virtme guest, mounting //127.0.0.1/testshare:
each iteration holds 8 files open via sleep processes, force-closes TCP with "ss -K sport = :445", kills the holders, lazy-umounts; repeated 10 times, then ksmbd shutdown and kmemleak scan.
state conn_alloc conn_free tcp_free opi_rcu kmemleak
---------- ---------- --------- -------- ------- --------
pre-patch 20 20 10 160 7
with patch 20 20 20 160 0Pre-patch conn_free=20 with tcp_free=10 directly demonstrates the bare-kfree paths skipping transport cleanup; kmemleak backtraces point into struct tcp_transport / iov. With this patch tcp_free matches conn_free at 20/20 and kmemleak is clean.
Move the per-struct final release into __ksmbd_conn_release_work() and route the three bare-kfree final-put sites through a new ksmbd_conn_put(). Those sites now pair ida_destroy() and free_transport() with kfree(conn) regardless of which holder happens to release the last reference. stop_sessions() only triggers the transport shutdown and does not itself drop the last conn reference, so it is unaffected.
The centralized release reaches sock_release() -> tcp_close() -> lock_sock_nested() (might_sleep) from every final putter, including __free_opinfo() invoked from an RCU softirq callback, which trips CONFIG_DEBUG_ATOMIC_SLEEP. Defer the release to a dedicated ksmbd_conn_wq workqueue so ksmbd_conn_put() is safe from any non-sleeping context.
Make ksmbd_file own a strong connection reference while fp->conn is non-NULL so durable-preserve and final-close paths cannot dereference a stale connection. ksmbd_open_fd() and ksmbd_reopen_durable_fd() take the reference via ksmbd_conn_get() (the latter also reorders the fp->conn / fp->tcon assignments before __open_id() so the published fp is never observed with fp->conn == NULL); session_fd_check() and __ksmbd_close_fd() drop it via ksmbd_conn_put(). With that invariant, session_fd_check() can take a local conn pointer once and use it across the m_op_list and lock_list iterations even though op->conn puts may otherwise drop the last reference.
At module exit the workqueue is flushed and destroyed after rcu_barrier(), so any release queued by a trailing RCU callback is drained before the inode hash and module text go away.
Stable-dep-of: c1016dd1d8b2 ("ksmbd: track the connection owning a byte-range lock")
--- fs/smb/server/connection.c | 49 +++++++++++++++++++++++++++++++++++++++++++-- fs/smb/server/connection.h | 3 ++ fs/smb/server/oplock.c | 4 +-- fs/smb/server/vfs_cache.c | 18 +++++++++++++++- 4 files changed, 69 insertions(+), 5 deletions(-)
fs/smb/server/connection.c +47 -2
@@ -23,6 +23,43 @@ LIST_HEAD(conn_list);DECLARE_RWSEM (conn_list_lock);/** +* ksmbd_conn_get() - take a reference on @conn and return it. +* +* Returns @conn unchanged so callers can write +* "fp->conn = ksmbd_conn_get(work->conn);" in one expression. Returns NULL +* if @conn is NULL. +*/ +struct ksmbd_conn *ksmbd_conn_get (struct ksmbd_conn *conn) +{ +if (!conn) +return NULL ; + +atomic_inc (&conn->refcnt); +return conn; +} + +/** +* ksmbd_conn_put() - drop a reference and, if it was the last, perform the +* final, once-per-struct release of a ksmbd_conn. +* +* The transport is not released here: free_transport() releases it after +* calling ksmbd_conn_free(). The final release therefore only pairs +* ida_destroy() with kfree(), reaches no sleeping teardown and is safe +* from any context, including the RCU callback that frees an opinfo +* holding a connection reference. +*/ +void ksmbd_conn_put (struct ksmbd_conn *conn) +{ +if (!conn) +return ; + +if (atomic_dec_and_test (&conn->refcnt)) { +ida_destroy (&conn->async_ida); +kfree (conn); + } +} + +/** * ksmbd_conn_free() - free resources of the connection instance * * @conn: connection instance to be cleand up
@@ -36,12 +73,19 @@ void ksmbd_conn_free(struct ksmbd_conn *list_del (&conn->conns_list);up_write (&conn_list_lock); +/* +* request_buf / preauth_info / mechToken are only ever accessed by the +* connection handler thread that owns @conn. ksmbd_conn_free() is +* called from the transport free_transport() path when that thread is +* exiting, so it is safe to release them unconditionally even when +* ksmbd_conn_put() below is not the final putter (oplock / ksmbd_file +* holders only retain the conn pointer, not these per-thread buffers). +*/ xa_destroy (&conn->sessions);kvfree (conn->request_buf);kfree (conn->preauth_info);kfree (conn->mechToken); -ida_destroy (&conn->async_ida); -kfree (conn); +ksmbd_conn_put (conn); }/**
@@ -70,6 +114,7 @@ struct ksmbd_conn *ksmbd_conn_alloc(void conn->um =NULL ;atomic_set (&conn->req_running,0 );atomic_set (&conn->r_count,0 ); +atomic_set (&conn->refcnt,1 ); conn->total_credits =1 ; conn->outstanding_credits =0 ;
fs/smb/server/connection.h +3 -0
@@ -112,6 +112,7 @@ struct ksmbd_conn {bool signing_negotiated;__le16 signing_algorithm;bool binding; +atomic_t refcnt; };struct ksmbd_conn_ops {
@@ -154,6 +155,8 @@ bool ksmbd_conn_alive(struct ksmbd_connvoid ksmbd_conn_wait_idle (struct ksmbd_conn *conn,u64 sess_id);struct ksmbd_conn *ksmbd_conn_alloc (void );void ksmbd_conn_free (struct ksmbd_conn *conn); +struct ksmbd_conn *ksmbd_conn_get (struct ksmbd_conn *conn); +void ksmbd_conn_put (struct ksmbd_conn *conn);bool ksmbd_conn_lookup_dialect (struct ksmbd_conn *c);int ksmbd_conn_write (struct ksmbd_work *work); int ksmbd_conn_rdma_read(struct ksmbd_conn *conn,
fs/smb/server/oplock.c +2 -2
@@ -30,7 +30,6 @@ static DEFINE_RWLOCK(lease_list_lock);static struct oplock_info *alloc_opinfo (struct ksmbd_work *work,u64 id,__u16 Tid) { -struct ksmbd_conn *conn = work->conn;struct ksmbd_session *sess = work->sess;struct oplock_info *opinfo;
@@ -39,7 +38,7 @@ static struct oplock_info *alloc_opinfo(return NULL ; opinfo->sess = sess; - opinfo->conn = conn; + opinfo->conn =ksmbd_conn_get (work->conn); opinfo->level =SMB2_OPLOCK_LEVEL_NONE ; opinfo->op_state =OPLOCK_STATE_NONE ; opinfo->pending_break =0 ;
@@ -124,6 +123,7 @@ static void free_opinfo(struct oplock_in {if (opinfo->is_lease)free_lease (opinfo); +ksmbd_conn_put (opinfo->conn);kfree (opinfo); }
fs/smb/server/vfs_cache.c +17 -1
@@ -328,6 +328,14 @@ static void __ksmbd_close_fd(struct ksmbkfree (smb_lock); } +/* +* Drop fp's strong reference on conn (taken in ksmbd_open_fd()). +*/ +if (fp->conn) { +ksmbd_conn_put (fp->conn); + fp->conn =NULL ; + } +if (ksmbd_stream_fd (fp))kfree (fp->stream.name);kmem_cache_free (filp_cache, fp);
@@ -580,7 +588,13 @@ struct ksmbd_file *ksmbd_open_fd(structatomic_set (&fp->refcount,1 ); fp->filp = filp; - fp->conn = work->conn; +/* +* fp owns a strong reference on fp->conn for as long as fp->conn is +* non-NULL, so __ksmbd_close_fd() never dereferences a dangling +* pointer. Paired with ksmbd_conn_put() in __ksmbd_close_fd() +* (final close) and on the error paths below. +*/ + fp->conn =ksmbd_conn_get (work->conn); fp->tcon = work->tcon; fp->volatile_id =KSMBD_NO_FID ; fp->persistent_id =KSMBD_NO_FID ;
@@ -602,6 +616,8 @@ struct ksmbd_file *ksmbd_open_fd(structreturn fp; err_out: +/* fp->conn was set and refcounted before every branch here. */ +ksmbd_conn_put (fp->conn);kmem_cache_free (filp_cache, fp);return ERR_PTR (ret); }
Greg Kroah-Hartman
maintainerPATCH 1005/1067 ksmbd: track the connection owning a byte-range lock
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Namjae Jeon <[email protected]>
[ Upstream commit c1016dd1d8b2bcd1158bbaabe94a31bb7e7431fb ]
SMB2_LOCK adds each granted byte-range lock to both the file lock list and the lock list of the connection which handled the request. The final close and durable handle paths, however, remove the connection list entry while holding fp->conn->llist_lock.
With SMB3 multichannel, the connection handling the LOCK request can be different from the connection which opened the file. The entry can therefore be removed under a different spinlock from the one protecting the list it belongs to. A concurrent traversal can then access freed struct ksmbd_lock and struct file_lock objects.
Record the connection owning each lock's clist entry and hold a reference to it while the entry is linked. Use that connection and its llist_lock for unlock, rollback, close, and durable preserve. Durable reconnect assigns the new connection as the owner when publishing the locks again.
--- fs/smb/server/smb2pdu.c | 10 ++++++++-- fs/smb/server/vfs_cache.c | 12 ++++++++---- fs/smb/server/vfs_cache.h | 1 + 3 files changed, 17 insertions(+), 6 deletions(-)
fs/smb/server/smb2pdu.c +8 -2
@@ -7136,9 +7136,11 @@ int smb2_lock(struct ksmbd_work *work) nolock =0 ;list_del (&cmp_lock->flist);list_del (&cmp_lock->clist); + cmp_lock->conn =NULL ;spin_unlock (&conn->llist_lock);up_read (&conn_list_lock); +ksmbd_conn_put (conn);locks_free_lock (cmp_lock->fl);kfree (cmp_lock); goto out_check_cl;
@@ -7273,6 +7275,7 @@ skip: goto out2; }else if (!rc) {list_add (&smb_lock->llist, &rollback_list); + smb_lock->conn =ksmbd_conn_get (work->conn);spin_lock (&work->conn->llist_lock);list_add_tail (&smb_lock->clist, &work->conn->lock_list);
@@ -7327,11 +7330,14 @@ out: }list_del (&smb_lock->llist); -spin_lock (&work->conn->llist_lock); + conn = smb_lock->conn; +spin_lock (&conn->llist_lock);if (!list_empty (&smb_lock->flist))list_del (&smb_lock->flist);list_del (&smb_lock->clist); -spin_unlock (&work->conn->llist_lock); + smb_lock->conn =NULL ; +spin_unlock (&conn->llist_lock); +ksmbd_conn_put (conn);locks_free_lock (smb_lock->fl);if (rlock)
fs/smb/server/vfs_cache.c +8 -4
@@ -317,10 +317,14 @@ static void __ksmbd_close_fd(struct ksmb* there are not accesses to fp->lock_list. */ list_for_each_entry_safe (smb_lock, tmp_lock, &fp->lock_list, flist) { -if (!list_empty (&smb_lock->clist) && fp->conn) { -spin_lock (&fp->conn->llist_lock); -list_del (&smb_lock->clist); -spin_unlock (&fp->conn->llist_lock); +struct ksmbd_conn *conn = smb_lock->conn; + +if (conn) { +spin_lock (&conn->llist_lock); +list_del_init (&smb_lock->clist); + smb_lock->conn =NULL ; +spin_unlock (&conn->llist_lock); +ksmbd_conn_put (conn); }list_del (&smb_lock->flist);
fs/smb/server/vfs_cache.h +1 -0
@@ -31,6 +31,7 @@ struct ksmbd_session;struct ksmbd_lock {struct file_lock *fl; +struct ksmbd_conn *conn;struct list_head clist;struct list_head flist;struct list_head llist;
Greg Kroah-Hartman
maintainerPATCH 1006/1067 writeback: Avoid contention on wb->list_lock when switching inodes
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jan Kara <[email protected]>
[ Upstream commit e1b849cfa6b61f1c866a908c9e8dd9b5aaab820b ]
There can be multiple inode switch works that are trying to switch inodes to / from the same wb. This can happen in particular if some cgroup exits which owns many (thousands) inodes and we need to switch them all. In this case several inode_switch_wbs_work_fn() instances will be just spinning on the same wb->list_lock while only one of them makes forward progress. This wastes CPU cycles and quickly leads to softlockup reports and unusable system.
Instead of running several inode_switch_wbs_work_fn() instances in parallel switching to the same wb and contending on wb->list_lock, run just one work item per wb and manage a queue of isw items switching to this wb.
Stable-dep-of: cba38ec4cbd3 ("writeback: fix race between cgroup_writeback_umount() and inode_switch_wbs()")
--- fs/fs-writeback.c | 99 ++++++++++++++++++++++++--------------- include/linux/backing-dev-defs.h | 4 + include/linux/writeback.h | 2 mm/backing-dev.c | 5 + 4 files changed, 74 insertions(+), 36 deletions(-)
fs/fs-writeback.c +63 -36
@@ -343,7 +343,8 @@ static struct bdi_writeback *inode_to_wb }struct inode_switch_wbs_context { -struct rcu_work work; +/* List of queued switching contexts for the wb */ +struct llist_node list;/* * Multiple inodes can be switched at once. The switching procedure
@@ -353,7 +354,6 @@ struct inode_switch_wbs_context {* array embedded into struct inode_switch_wbs_context. Otherwise * an inode could be left in a non-consistent state. */ -struct bdi_writeback *new_wb;struct inode *inodes[]; };
@@ -462,13 +462,11 @@ skip_switch:return switched; } -static void inode_switch_wbs_work_fn (struct work_struct *work) +static void process_inode_switch_wbs (struct bdi_writeback *new_wb, +struct inode_switch_wbs_context *isw) { -struct inode_switch_wbs_context *isw = - container_of(to_rcu_work(work), struct inode_switch_wbs_context, work);struct backing_dev_info *bdi =inode_to_bdi (isw->inodes[0 ]);struct bdi_writeback *old_wb = isw->inodes[0 ]->i_wb; -struct bdi_writeback *new_wb = isw->new_wb;unsigned long nr_switched =0 ;struct inode **inodep;
@@ -528,6 +526,38 @@ relock:atomic_dec (&isw_nr_in_flight); } +void inode_switch_wbs_work_fn (struct work_struct *work) +{ + struct bdi_writeback *new_wb = container_of(work, struct bdi_writeback, + switch_work); +struct inode_switch_wbs_context *isw, *next_isw; +struct llist_node *list; + +/* +* Grab out reference to wb so that it cannot get freed under us +* after we process all the isw items. +*/ +wb_get (new_wb); +while (1 ) { + list =llist_del_all (&new_wb->switch_wbs_ctxs); +/* Nothing to do? */ +if (!list) +break ; +/* +* In addition to synchronizing among switchers, I_WB_SWITCH +* tells the RCU protected stat update paths to grab the i_page +* lock so that stat transfer can synchronize against them. +* Let's continue after I_WB_SWITCH is guaranteed to be +* visible. +*/ +synchronize_rcu (); + +llist_for_each_entry_safe (isw, next_isw, list, list) +process_inode_switch_wbs (new_wb, isw); + } +wb_put (new_wb); +} +static bool inode_prepare_wbs_switch (struct inode *inode,struct bdi_writeback *new_wb) {
@@ -557,6 +587,13 @@ static bool inode_prepare_wbs_switch(strreturn true; } +static void wb_queue_isw (struct bdi_writeback *wb, +struct inode_switch_wbs_context *isw) +{ +if (llist_add (&isw->list, &wb->switch_wbs_ctxs)) +queue_work (isw_wq, &wb->switch_work); +} +/** * inode_switch_wbs - change the wb association of an inode * @inode: target inode
@@ -570,6 +607,7 @@ static void inode_switch_wbs(struct inodstruct backing_dev_info *bdi =inode_to_bdi (inode);struct cgroup_subsys_state *memcg_css;struct inode_switch_wbs_context *isw; +struct bdi_writeback *new_wb =NULL ;/* noop if seems to be already in progress */ if (inode->i_state &I_WB_SWITCH )
@@ -594,40 +632,34 @@ static void inode_switch_wbs(struct inodif (!memcg_css) goto out_free; - isw->new_wb =wb_get_create (bdi, memcg_css,GFP_ATOMIC ); + new_wb =wb_get_create (bdi, memcg_css,GFP_ATOMIC );css_put (memcg_css); -if (!isw->new_wb) +if (!new_wb) goto out_free; -if (!inode_prepare_wbs_switch (inode, isw->new_wb)) +if (!inode_prepare_wbs_switch (inode, new_wb)) goto out_free; isw->inodes[0 ] = inode; -/* -* In addition to synchronizing among switchers, I_WB_SWITCH tells -* the RCU protected stat update paths to grab the i_page -* lock so that stat transfer can synchronize against them. -* Let's continue after I_WB_SWITCH is guaranteed to be visible. -*/ -INIT_RCU_WORK (&isw->work, inode_switch_wbs_work_fn); -queue_rcu_work (isw_wq, &isw->work); +wb_queue_isw (new_wb, isw);return ; out_free:atomic_dec (&isw_nr_in_flight); -if (isw->new_wb) -wb_put (isw->new_wb); +if (new_wb) +wb_put (new_wb);kfree (isw); } -static bool isw_prepare_wbs_switch (struct inode_switch_wbs_context *isw, +static bool isw_prepare_wbs_switch (struct bdi_writeback *new_wb, +struct inode_switch_wbs_context *isw,struct list_head *list,int *nr) {struct inode *inode;list_for_each_entry (inode, list, i_io_list) { -if (!inode_prepare_wbs_switch (inode, isw->new_wb)) +if (!inode_prepare_wbs_switch (inode, new_wb))continue ; isw->inodes[*nr] = inode;
@@ -651,6 +683,7 @@ bool cleanup_offline_cgwb(struct bdi_wri {struct cgroup_subsys_state *memcg_css;struct inode_switch_wbs_context *isw; +struct bdi_writeback *new_wb;int nr;bool restart = false;
@@ -663,12 +696,12 @@ bool cleanup_offline_cgwb(struct bdi_wrifor (memcg_css = wb->memcg_css->parent; memcg_css; memcg_css = memcg_css->parent) { - isw->new_wb =wb_get_create (wb->bdi, memcg_css,GFP_KERNEL ); -if (isw->new_wb) + new_wb =wb_get_create (wb->bdi, memcg_css,GFP_KERNEL ); +if (new_wb)break ; } -if (unlikely (!isw->new_wb)) - isw->new_wb = &wb->bdi->wb;/* wb_get() is noop for bdi's wb */ +if (unlikely (!new_wb)) + new_wb = &wb->bdi->wb;/* wb_get() is noop for bdi's wb */ nr =0 ;spin_lock (&wb->list_lock);
@@ -680,27 +713,21 @@ bool cleanup_offline_cgwb(struct bdi_wri* bandwidth restrictions, as writeback of inode metadata is not * accounted for. */ - restart =isw_prepare_wbs_switch (isw, &wb->b_attached, &nr); + restart =isw_prepare_wbs_switch (new_wb, isw, &wb->b_attached, &nr);if (!restart) - restart =isw_prepare_wbs_switch (isw, &wb->b_dirty_time, &nr); + restart =isw_prepare_wbs_switch (new_wb, isw, &wb->b_dirty_time, + &nr);spin_unlock (&wb->list_lock);/* no attached inodes? bail out */ if (nr ==0 ) {atomic_dec (&isw_nr_in_flight); -wb_put (isw->new_wb); +wb_put (new_wb);kfree (isw);return restart; } -/* -* In addition to synchronizing among switchers, I_WB_SWITCH tells -* the RCU protected stat update paths to grab the i_page -* lock so that stat transfer can synchronize against them. -* Let's continue after I_WB_SWITCH is guaranteed to be visible. -*/ -INIT_RCU_WORK (&isw->work, inode_switch_wbs_work_fn); -queue_rcu_work (isw_wq, &isw->work); +wb_queue_isw (new_wb, isw);return restart; }
include/linux/backing-dev-defs.h +4 -0
@@ -154,6 +154,10 @@ struct bdi_writeback {struct list_head blkcg_node;/* anchored at blkcg->cgwb_list */ struct list_head b_attached;/* attached inodes, protected by list_lock */ struct list_head offline_node;/* anchored at offline_cgwbs */ +struct work_struct switch_work;/* work used to perform inode switching +* to this wb */ +struct llist_head switch_wbs_ctxs;/* queued contexts for +* writeback switching */ union {struct work_struct release_work;
include/linux/writeback.h +2 -0
@@ -288,6 +288,8 @@ static inline void wbc_init_bio(struct w bio_associate_blkg_from_css(bio, wbc->wb->blkcg_css); } +void inode_switch_wbs_work_fn (struct work_struct *work); +#else /* CONFIG_CGROUP_WRITEBACK */ static inline void inode_attach_wb (struct inode *inode,struct page *page)
mm/backing-dev.c +5 -0
@@ -415,6 +415,7 @@ static void cgwb_release_workfn(struct wwb_exit (wb);bdi_put (bdi);WARN_ON_ONCE (!list_empty (&wb->b_attached)); +WARN_ON_ONCE (work_pending (&wb->switch_work));call_rcu (&wb->rcu, cgwb_free_rcu); }
@@ -491,6 +492,8 @@ static int cgwb_create(struct backing_de wb->memcg_css = memcg_css; wb->blkcg_css = blkcg_css;INIT_LIST_HEAD (&wb->b_attached); +INIT_WORK (&wb->switch_work, inode_switch_wbs_work_fn); +init_llist_head (&wb->switch_wbs_ctxs);INIT_WORK (&wb->release_work, cgwb_release_workfn);set_bit (WB_registered, &wb->state);bdi_get (bdi);
@@ -624,6 +627,8 @@ static int cgwb_bdi_init(struct backing_if (!ret) { bdi->wb.memcg_css = &root_mem_cgroup->css; bdi->wb.blkcg_css = blkcg_root_css; +INIT_WORK (&bdi->wb.switch_work, inode_switch_wbs_work_fn); +init_llist_head (&bdi->wb.switch_wbs_ctxs); }return ret; }
Greg Kroah-Hartman
maintainerPATCH 1007/1067 writeback: fix race between cgroup_writeback_umount() and inode_switch_wbs()
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Baokun Li <[email protected]>
[ Upstream commit cba38ec4cbd3a7b8b942a8d52531a05be8a9ff0d ]
When a container exits, the following BUG_ON() is occasionally triggered:
==================================================================
VFS: Busy inodes after unmount of sdb (ext4)
------------[ cut here ]------------ kernel BUG at fs/super.c:695! CPU: 3 PID: 6 Comm: containerd-shim Tainted: G OE K 6.6 #1 pstate: 63400009 (nZCv daif +PAN -UAO +TCO +DIT -SSBS BTYPE=--) pc : generic_shutdown_super+0xf0/0x100 lr : generic_shutdown_super+0xf0/0x100 Call trace: generic_shutdown_super+0xf0/0x100 kill_block_super+0x20/0x48 ext4_kill_sb+0x28/0x60 deactivate_locked_super+0x54/0x130 deactivate_super+0x84/0xa0 cleanup_mnt+0xa4/0x140 __cleanup_mnt+0x18/0x28 task_work_run+0x78/0xe0 do_notify_resume+0x204/0x240 ==================================================================
The root cause is a race between cgroup_writeback_umount() and inode_switch_wbs()/cleanup_offline_cgwb(). There is a window between inode_prepare_wbs_switch() returning true and the subsequent wb_queue_isw() call. Following is the process that triggers the issue:
CPU A (umount) | CPU B (writeback)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
inode_switch_wbs/cleanup_offline_cgwb
atomic_inc(&isw_nr_in_flight)
inode_prepare_wbs_switch
-> passes SB_ACTIVE check
__iget(inode)
generic_shutdown_super
sb->s_flags &= ~SB_ACTIVE
cgroup_writeback_umount(sb)
smp_mb()
atomic_read(&isw_nr_in_flight)
rcu_barrier()
-> no pending RCU callbacks
flush_workqueue(isw_wq)
-> nothing queued, returns
evict_inodes(sb)
-> Inode skipped as isw still holds a ref.
sop->put_super(sb)
/* destroys percpu counters */
-> VFS: Busy inodes after unmount!
wb_queue_isw()
queue_work(isw_wq, ...)
/* later in work function */
inode_switch_wbs_work_fn
process_inode_switch_wbs
iput() -> evict
percpu_counter_dec() // UAF!Fix this by extending the RCU read-side critical section in inode_switch_wbs() and cleanup_offline_cgwb() to cover from inode_prepare_wbs_switch() through wb_queue_isw(). Since there is no sleep in this window, rcu_read_lock() can be used. Then add a synchronize_rcu() in cgroup_writeback_umount() before the existing rcu_barrier(), so that all in-flight switchers that have passed the SB_ACTIVE check have completed queue_work() before flush_workqueue() is called.
The existing rcu_barrier() is intentionally retained so this fix can be backported unchanged to stable kernels (5.10.y, 6.6.y, ...) that still queue switches via queue_rcu_work(). It is a no-op on current mainline (since commit e1b849cfa6b6 ("writeback: Avoid contention on wb->list_lock when switching inodes")) and is removed in a follow-up patch.
--- fs/fs-writeback.c | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-)
fs/fs-writeback.c +29 -2
@@ -623,12 +623,19 @@ static void inode_switch_wbs(struct inodatomic_inc (&isw_nr_in_flight); -/* find and pin the new wb */ +/* +* Paired with synchronize_rcu() in cgroup_writeback_umount(): +* holding rcu_read_lock across inode_prepare_wbs_switch() +* (covering the SB_ACTIVE check and the inode grab) and +* wb_queue_isw() ensures synchronize_rcu() cannot return until +* the work is queued, so the subsequent flush_workqueue() will +* wait for the switch. +*/ rcu_read_lock (); +/* find and pin the new wb */ memcg_css =css_from_id (new_wb_id, &memory_cgrp_subsys);if (memcg_css && !css_tryget (memcg_css)) memcg_css =NULL ; -rcu_read_unlock ();if (!memcg_css) goto out_free;
@@ -643,9 +650,11 @@ static void inode_switch_wbs(struct inod isw->inodes[0 ] = inode;wb_queue_isw (new_wb, isw); +rcu_read_unlock ();return ; out_free: +rcu_read_unlock ();atomic_dec (&isw_nr_in_flight);if (new_wb)wb_put (new_wb);
@@ -704,6 +713,14 @@ bool cleanup_offline_cgwb(struct bdi_wri new_wb = &wb->bdi->wb;/* wb_get() is noop for bdi's wb */ nr =0 ; +/* +* Paired with synchronize_rcu() in cgroup_writeback_umount(). +* Holding rcu_read_lock across the SB_ACTIVE check, the inode grab +* and wb_queue_isw() ensures synchronize_rcu() cannot return until +* the work is queued, so the subsequent flush_workqueue() will wait +* for the switch. +*/ +rcu_read_lock ();spin_lock (&wb->list_lock);/* * In addition to the inodes that have completed writeback, also switch
@@ -721,6 +738,7 @@ bool cleanup_offline_cgwb(struct bdi_wri/* no attached inodes? bail out */ if (nr ==0 ) { +rcu_read_unlock ();atomic_dec (&isw_nr_in_flight);wb_put (new_wb);kfree (isw);
@@ -728,6 +746,7 @@ bool cleanup_offline_cgwb(struct bdi_wri }wb_queue_isw (new_wb, isw); +rcu_read_unlock ();return restart; }
@@ -1159,6 +1178,14 @@ void cgroup_writeback_umount(void)if (atomic_read (&isw_nr_in_flight)) {/* +* Paired with rcu_read_lock() in inode_switch_wbs() and +* cleanup_offline_cgwb(). synchronize_rcu() waits for any +* in-flight switcher that already passed the SB_ACTIVE check +* to finish queueing its work, so flush_workqueue() below +* will then drain it. +*/ +synchronize_rcu (); +/* * Use rcu_barrier() to wait for all pending callbacks to * ensure that all in-flight wb switches are in the workqueue. */
Greg Kroah-Hartman
maintainerPATCH 1008/1067 perf/x86/intel/uncore: Defer ADL global PMON enable to enable_box()
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zide Chen <[email protected]>
[ Upstream commit 9a0bb848a37150aeccc10088e141339917d995dc ]
On some Raptor Cove CPUs, enabling uncore PMON globally at driver init may increase power consumption even when no perf events are in use.
Drop adl_uncore_msr_init_box() and defer programming the global control register to enable_box(), so it is only set when a box is actually used.
IMC and IMC freerunning counters use a separate control path and are unaffected.
--- arch/x86/events/intel/uncore_snb.c | 7 ------- 1 file changed, 7 deletions(-)
arch/x86/events/intel/uncore_snb.c +0 -7
@@ -537,12 +537,6 @@ void tgl_uncore_cpu_init(void) skl_uncore_msr_ops.init_box = rkl_uncore_msr_init_box; } -static void adl_uncore_msr_init_box (struct intel_uncore_box *box) -{ -if (box->pmu->pmu_idx ==0 ) -wrmsrl (ADL_UNC_PERF_GLOBAL_CTL ,SNB_UNC_GLOBAL_CTL_EN ); -} -static void adl_uncore_msr_enable_box (struct intel_uncore_box *box) {wrmsrl (ADL_UNC_PERF_GLOBAL_CTL ,SNB_UNC_GLOBAL_CTL_EN );
@@ -561,7 +555,6 @@ static void adl_uncore_msr_exit_box(stru }static struct intel_uncore_ops adl_uncore_msr_ops = { - .init_box = adl_uncore_msr_init_box, .enable_box = adl_uncore_msr_enable_box, .disable_box = adl_uncore_msr_disable_box, .exit_box = adl_uncore_msr_exit_box,
Greg Kroah-Hartman
maintainerPATCH 1009/1067 proc: use generic setattr() for /proc/$PID/net
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Thomas Weißschuh <[email protected]>
[ Upstream commit 18e66ae67673f2c8df6f02428798b1355691f2a9 ]
All other files in /proc/$PID/ use proc_setattr().
Not using it allows the usage of chmod() on /proc/$PID/net, even on other processes owned by the same user.
The same would probably also be true for other attributes to be changed.
As this technically represents an ABI change it is not marked for stable so any unlikely regressions are caught during a full release cycle.
Message-Id: <[email protected]>
Stable-dep-of: 6255da28d4bb ("proc: protect ptrace_may_access() with exec_update_lock (FD links)")
--- fs/proc/proc_net.c | 1 + 1 file changed, 1 insertion(+)
fs/proc/proc_net.c +1 -0
@@ -321,6 +321,7 @@ static int proc_tgid_net_getattr(structconst struct inode_operations proc_net_inode_operations = { .lookup = proc_tgid_net_lookup, .getattr = proc_tgid_net_getattr, + .setattr = proc_setattr, };static int proc_tgid_net_readdir (struct file *file,struct dir_context *ctx)
Greg Kroah-Hartman
maintainerPATCH 1010/1067 proc: Move fdinfo PTRACE_MODE_READ check into the inode .permission operation
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: "Tyler Hicks (Microsoft)" <[email protected]>
[ Upstream commit 0a960ba49869ebe8ff859d000351504dd6b93b68 ]
The following commits loosened the permissions of /proc/<PID>/fdinfo/ directory, as well as the files within it, from 0500 to 0555 while also introducing a PTRACE_MODE_READ check between the current task and <PID>'s task:
- commit 7bc3fa0172a4 ("procfs: allow reading fdinfo with PTRACE_MODE_READ")
- commit 1927e498aee1 ("procfs: prevent unprivileged processes accessing fdinfo dir")
Before those changes, inode based system calls like inotify_add_watch(2) would fail when the current task didn't have sufficient read permissions:
[...]
lstat("/proc/1/task/1/fdinfo", {st_mode=S_IFDIR|0500, st_size=0, ...}) = 0 inotify_add_watch(64, "/proc/1/task/1/fdinfo",
IN_MODIFY|IN_ATTRIB|IN_MOVED_FROM|IN_MOVED_TO|IN_CREATE|IN_DELETE| IN_ONLYDIR|IN_DONT_FOLLOW|IN_EXCL_UNLINK) = -1 EACCES (Permission denied)
[...]
This matches the documented behavior in the inotify_add_watch(2) man page:
ERRORS
EACCES Read access to the given file is not permitted.
After those changes, inotify_add_watch(2) started succeeding despite the current task not having PTRACE_MODE_READ privileges on the target task:
[...]
lstat("/proc/1/task/1/fdinfo", {st_mode=S_IFDIR|0555, st_size=0, ...}) = 0 inotify_add_watch(64, "/proc/1/task/1/fdinfo",
IN_MODIFY|IN_ATTRIB|IN_MOVED_FROM|IN_MOVED_TO|IN_CREATE|IN_DELETE| IN_ONLYDIR|IN_DONT_FOLLOW|IN_EXCL_UNLINK) = 1757
openat(AT_FDCWD, "/proc/1/task/1/fdinfo",
O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_DIRECTORY) = -1 EACCES (Permission denied)
[...]
This change in behavior broke .NET prior to v7. See the github link below for the v7 commit that inadvertently/quietly (?) fixed .NET after the kernel changes mentioned above.
Return to the old behavior by moving the PTRACE_MODE_READ check out of the file .open operation and into the inode .permission operation:
[...]
lstat("/proc/1/task/1/fdinfo", {st_mode=S_IFDIR|0555, st_size=0, ...}) = 0 inotify_add_watch(64, "/proc/1/task/1/fdinfo",
IN_MODIFY|IN_ATTRIB|IN_MOVED_FROM|IN_MOVED_TO|IN_CREATE|IN_DELETE| IN_ONLYDIR|IN_DONT_FOLLOW|IN_EXCL_UNLINK) = -1 EACCES (Permission denied)
[...]
Stable-dep-of: 6255da28d4bb ("proc: protect ptrace_may_access() with exec_update_lock (FD links)")
--- fs/proc/fd.c | 42 ++++++++++++++++++++---------------------- 1 file changed, 20 insertions(+), 22 deletions(-)
fs/proc/fd.c +20 -22
@@ -72,7 +72,18 @@ out:return 0 ; } -static int proc_fdinfo_access_allowed (struct inode *inode) +static int seq_fdinfo_open (struct inode *inode,struct file *file) +{ +return single_open (file, seq_show, inode); +} + +/** +* Shared /proc/pid/fdinfo and /proc/pid/fdinfo/fd permission helper to ensure +* that the current task has PTRACE_MODE_READ in addition to the normal +* POSIX-like checks. +*/ +static int proc_fdinfo_permission (struct mnt_idmap *idmap,struct inode *inode, +int mask) {bool allowed = false;struct task_struct *task =get_proc_task (inode);
@@ -86,18 +97,13 @@ static int proc_fdinfo_access_allowed(stif (!allowed)return -EACCES ; -return 0 ; +return generic_permission (idmap, inode, mask); } -static int seq_fdinfo_open (struct inode *inode,struct file *file) -{ -int ret =proc_fdinfo_access_allowed (inode); - -if (ret) -return ret; - -return single_open (file, seq_show, inode); -} +static const struct inode_operations proc_fdinfo_file_inode_operations = { + .permission = proc_fdinfo_permission, + .setattr = proc_setattr, +};static const struct file_operations proc_fdinfo_file_operations = { .open = seq_fdinfo_open,
@@ -339,6 +345,8 @@ static struct dentry *proc_fdinfo_instan ei =PROC_I (inode); ei->fd = data->fd; + inode->i_op = &proc_fdinfo_file_inode_operations; + inode->i_fop = &proc_fdinfo_file_operations;tid_fd_update_inode (task, inode,0 );
@@ -358,23 +366,13 @@ static int proc_readfdinfo(struct file * proc_fdinfo_instantiate); } -static int proc_open_fdinfo (struct inode *inode,struct file *file) -{ -int ret =proc_fdinfo_access_allowed (inode); - -if (ret) -return ret; - -return 0 ; -} -const struct inode_operations proc_fdinfo_inode_operations = { .lookup = proc_lookupfdinfo, + .permission = proc_fdinfo_permission, .setattr = proc_setattr, };const struct file_operations proc_fdinfo_operations = { - .open = proc_open_fdinfo, .read = generic_read_dir, .iterate_shared = proc_readfdinfo, .llseek = generic_file_llseek,
Greg Kroah-Hartman
maintainerPATCH 1011/1067 proc: rename proc_setattr to proc_nochmod_setattr
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christoph Hellwig <[email protected]>
[ Upstream commit 690005b0b1e6b567c88b7790e6d90d4d6c9e09cc ]
What is currently proc_setattr is a special version added after the more general procfs ->seattr in commit 6d76fa58b050 ("Don't allow chmod() on the /proc/<pid>/ files"). Give it a name that reflects that to free the proc_setattr name and better describe what is doing.
Stable-dep-of: 6255da28d4bb ("proc: protect ptrace_may_access() with exec_update_lock (FD links)")
--- fs/proc/base.c | 22 +++++++++++----------- fs/proc/fd.c | 10 +++++----- fs/proc/internal.h | 4 ++-- fs/proc/namespaces.c | 4 ++-- fs/proc/proc_net.c | 2 +- 5 files changed, 21 insertions(+), 21 deletions(-)
fs/proc/base.c +11 -11
@@ -720,7 +720,7 @@ static bool proc_fd_access_allowed(strucreturn allowed; } -int proc_setattr (struct user_namespace *mnt_userns,struct dentry *dentry, +int proc_nochmod_setattr (struct user_namespace *mnt_userns,struct dentry *dentry,struct iattr *attr) {int error;
@@ -794,7 +794,7 @@ static int proc_pid_permission(struct usstatic const struct inode_operations proc_def_inode_operations = { - .setattr = proc_setattr, + .setattr = proc_nochmod_setattr, };static int proc_single_show (struct seq_file *m,void *v)
@@ -1867,7 +1867,7 @@ out:const struct inode_operations proc_pid_link_inode_operations = { .readlink = proc_pid_readlink, .get_link = proc_pid_get_link, - .setattr = proc_setattr, + .setattr = proc_nochmod_setattr, };
@@ -2317,7 +2317,7 @@ proc_map_files_get_link(struct dentry *dstatic const struct inode_operations proc_map_files_link_inode_operations = { .readlink = proc_pid_readlink, .get_link = proc_map_files_get_link, - .setattr = proc_setattr, + .setattr = proc_nochmod_setattr, }; static struct dentry *
@@ -2396,7 +2396,7 @@ out:static const struct inode_operations proc_map_files_inode_operations = { .lookup = proc_map_files_lookup, .permission = proc_fd_permission, - .setattr = proc_setattr, + .setattr = proc_nochmod_setattr, }; static int
@@ -2892,7 +2892,7 @@ static struct dentry *proc_##LSM##_attr_ static const struct inode_operations proc_##LSM##_attr_dir_inode_ops = { \ .lookup = proc_##LSM##_attr_dir_lookup, \ .getattr = pid_getattr, \ - .setattr = proc_setattr, \ + .setattr = proc_nochmod_setattr, \ }#ifdef CONFIG_SECURITY_SMACK
@@ -2951,7 +2951,7 @@ static struct dentry *proc_attr_dir_lookstatic const struct inode_operations proc_attr_dir_inode_operations = { .lookup = proc_attr_dir_lookup, .getattr = pid_getattr, - .setattr = proc_setattr, + .setattr = proc_nochmod_setattr, };#endif
@@ -3441,7 +3441,7 @@ static struct dentry *proc_tgid_base_loostatic const struct inode_operations proc_tgid_base_inode_operations = { .lookup = proc_tgid_base_lookup, .getattr = pid_getattr, - .setattr = proc_setattr, + .setattr = proc_nochmod_setattr, .permission = proc_pid_permission, };
@@ -3641,7 +3641,7 @@ static int proc_tid_comm_permission(stru }static const struct inode_operations proc_tid_comm_inode_operations = { - .setattr = proc_setattr, + .setattr = proc_nochmod_setattr, .permission = proc_tid_comm_permission, };
@@ -3772,7 +3772,7 @@ static const struct file_operations procstatic const struct inode_operations proc_tid_base_inode_operations = { .lookup = proc_tid_base_lookup, .getattr = pid_getattr, - .setattr = proc_setattr, + .setattr = proc_nochmod_setattr, }; static struct dentry *proc_task_instantiate(struct dentry *dentry,
@@ -3971,7 +3971,7 @@ static int proc_task_getattr(struct userstatic const struct inode_operations proc_task_inode_operations = { .lookup = proc_task_lookup, .getattr = proc_task_getattr, - .setattr = proc_setattr, + .setattr = proc_nochmod_setattr, .permission = proc_pid_permission, };
fs/proc/fd.c +5 -5
@@ -82,7 +82,7 @@ static int seq_fdinfo_open(struct inode* that the current task has PTRACE_MODE_READ in addition to the normal * POSIX-like checks. */ -static int proc_fdinfo_permission (struct mnt_idmap *idmap,struct inode *inode, +static int proc_fdinfo_permission (struct user_namespace *mnt_userns,struct inode *inode,int mask) {bool allowed = false;
@@ -97,12 +97,12 @@ static int proc_fdinfo_permission(structif (!allowed)return -EACCES ; -return generic_permission (idmap, inode, mask); +return generic_permission (mnt_userns, inode, mask); }static const struct inode_operations proc_fdinfo_file_inode_operations = { .permission = proc_fdinfo_permission, - .setattr = proc_setattr, + .setattr = proc_nochmod_setattr, };static const struct file_operations proc_fdinfo_file_operations = {
@@ -328,7 +328,7 @@ int proc_fd_permission(struct user_namesconst struct inode_operations proc_fd_inode_operations = { .lookup = proc_lookupfd, .permission = proc_fd_permission, - .setattr = proc_setattr, + .setattr = proc_nochmod_setattr, }; static struct dentry *proc_fdinfo_instantiate(struct dentry *dentry,
@@ -369,7 +369,7 @@ static int proc_readfdinfo(struct file *const struct inode_operations proc_fdinfo_inode_operations = { .lookup = proc_lookupfdinfo, .permission = proc_fdinfo_permission, - .setattr = proc_setattr, + .setattr = proc_nochmod_setattr, };const struct file_operations proc_fdinfo_operations = {
fs/proc/internal.h +2 -2
@@ -183,8 +183,8 @@ extern int proc_pid_statm(struct seq_filextern const struct dentry_operations pid_dentry_operations;extern int pid_getattr (struct user_namespace *,const struct path *,struct kstat *,u32 ,unsigned int ); -extern int proc_setattr (struct user_namespace *,struct dentry *, -struct iattr *); +int proc_nochmod_setattr (struct user_namespace *mnt_userns,struct dentry *dentry, +struct iattr *attr);extern void proc_pid_evict_inode (struct proc_inode *);extern struct inode *proc_pid_make_inode (struct super_block *,struct task_struct *,umode_t );extern void pid_update_inode (struct task_struct *,struct inode *);
fs/proc/namespaces.c +2 -2
@@ -92,7 +92,7 @@ static int proc_ns_readlink(struct dentrstatic const struct inode_operations proc_ns_link_inode_operations = { .readlink = proc_ns_readlink, .get_link = proc_ns_get_link, - .setattr = proc_setattr, + .setattr = proc_nochmod_setattr, }; static struct dentry *proc_ns_instantiate(struct dentry *dentry,
@@ -179,5 +179,5 @@ out_no_task:const struct inode_operations proc_ns_dir_inode_operations = { .lookup = proc_ns_dir_lookup, .getattr = pid_getattr, - .setattr = proc_setattr, + .setattr = proc_nochmod_setattr, };
fs/proc/proc_net.c +1 -1
@@ -321,7 +321,7 @@ static int proc_tgid_net_getattr(structconst struct inode_operations proc_net_inode_operations = { .lookup = proc_tgid_net_lookup, .getattr = proc_tgid_net_getattr, - .setattr = proc_setattr, + .setattr = proc_nochmod_setattr, };static int proc_tgid_net_readdir (struct file *file,struct dir_context *ctx)
Greg Kroah-Hartman
maintainerPATCH 1012/1067 proc: protect ptrace_may_access() with exec_update_lock (FD links)
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jann Horn <[email protected]>
[ Upstream commit 6255da28d4bb5349fe18e84cb043ccd394eba75d ]
proc_pid_get_link() and proc_pid_readlink() currently look up the task from the pid once, then do the ptrace access check on that task, then look up the task from the pid a second time to do the actual access. That's racy in several ways.
To fix it, pass the task to the ->proc_get_link() handler, and instead of proc_fd_access_allowed(), introduce a new helper call_proc_get_link() that looks up and locks the task, does the access check, and calls ->proc_get_link().
--- fs/proc/base.c | 119 ++++++++++++++++++++--------------------------------- fs/proc/fd.c | 25 ++++------- fs/proc/internal.h | 2 3 files changed, 58 insertions(+), 88 deletions(-)
fs/proc/base.c +47 -72
@@ -217,33 +217,24 @@ static int get_task_root(struct task_strreturn result; } -static int proc_cwd_link (struct dentry *dentry,struct path *path) +static int proc_cwd_link (struct dentry *dentry,struct path *path, +struct task_struct *task) { -struct task_struct *task =get_proc_task (d_inode (dentry));int result = -ENOENT ; -if (task) { -task_lock (task); -if (task->fs) { -get_fs_pwd (task->fs, path); - result =0 ; - } -task_unlock (task); -put_task_struct (task); +task_lock (task); +if (task->fs) { +get_fs_pwd (task->fs, path); + result =0 ; } +task_unlock (task);return result; } -static int proc_root_link (struct dentry *dentry,struct path *path) +static int proc_root_link (struct dentry *dentry,struct path *path, +struct task_struct *task) { -struct task_struct *task =get_proc_task (d_inode (dentry)); -int result = -ENOENT ; - -if (task) { - result =get_task_root (task, path); -put_task_struct (task); - } -return result; +return get_task_root (task, path); }/*
@@ -703,23 +694,6 @@ static int proc_pid_syscall(struct seq_f/* Here the fs part begins */ /************************************************************************/ -/* permission checks */ -static bool proc_fd_access_allowed (struct inode *inode) -{ -struct task_struct *task; -bool allowed = false; -/* Allow access to a task's file descriptors if it is us or we -* may use ptrace attach to the process and find out that -* information. -*/ - task =get_proc_task (inode); -if (task) { - allowed =ptrace_may_access (task,PTRACE_MODE_READ_FSCREDS ); -put_task_struct (task); - } -return allowed; -} -int proc_nochmod_setattr (struct user_namespace *mnt_userns,struct dentry *dentry,struct iattr *attr) {
@@ -1778,16 +1752,12 @@ static const struct file_operations proc .release = single_release, }; -static int proc_exe_link (struct dentry *dentry,struct path *exe_path) +static int proc_exe_link (struct dentry *dentry,struct path *exe_path, +struct task_struct *task) { -struct task_struct *task;struct file *exe_file; - task =get_proc_task (d_inode (dentry)); -if (!task) -return -ENOENT ; exe_file =get_task_exe_file (task); -put_task_struct (task);if (exe_file) { *exe_path = exe_file->f_path;path_get (&exe_file->f_path);
@@ -1797,26 +1767,42 @@ static int proc_exe_link(struct dentry *return -ENOENT ; } +static int call_proc_get_link (struct dentry *dentry,struct inode *inode,struct path *path_out) +{ +struct task_struct *task; +int ret; + + task =get_proc_task (inode); +if (!task) +return -ENOENT ; + ret =down_read_killable (&task->signal->exec_update_lock); +if (ret) + goto out_put_task; +if (!ptrace_may_access (task,PTRACE_MODE_READ_FSCREDS )) { + ret = -EACCES ; + goto out; + } + ret =PROC_I (inode)->op.proc_get_link (dentry, path_out, task); + +out: +up_read (&task->signal->exec_update_lock); +out_put_task: +put_task_struct (task); +return ret; +} +static const char *proc_pid_get_link (struct dentry *dentry,struct inode *inode,struct delayed_call *done) {struct path path; -int error = -EACCES ; +int error;if (!dentry)return ERR_PTR (-ECHILD ); - -/* Are we allowed to snoop on the tasks file descriptors? */ -if (!proc_fd_access_allowed (inode)) - goto out; - - error =PROC_I (inode)->op.proc_get_link (dentry, &path); -if (error) - goto out; - - error =nd_jump_link (&path); -out: + error =call_proc_get_link (dentry, inode, &path); +if (!error) + error =nd_jump_link (&path);return ERR_PTR (error); }
@@ -1850,17 +1836,11 @@ static int proc_pid_readlink(struct dentstruct inode *inode =d_inode (dentry);struct path path; -/* Are we allowed to snoop on the tasks file descriptors? */ -if (!proc_fd_access_allowed (inode)) - goto out; - - error =PROC_I (inode)->op.proc_get_link (dentry, &path); -if (error) - goto out; - - error =do_proc_readlink (&path, buffer, buflen); -path_put (&path); -out: + error =call_proc_get_link (dentry, inode, &path); +if (!error) { + error =do_proc_readlink (&path, buffer, buflen); +path_put (&path); + }return error; }
@@ -2248,21 +2228,16 @@ static const struct dentry_operations ti .d_delete = pid_delete_dentry, }; -static int map_files_get_link (struct dentry *dentry,struct path *path) +static int map_files_get_link (struct dentry *dentry,struct path *path, +struct task_struct *task) {unsigned long vm_start, vm_end;struct vm_area_struct *vma; -struct task_struct *task;struct mm_struct *mm;int rc; rc = -ENOENT ; - task =get_proc_task (d_inode (dentry)); -if (!task) - goto out; - mm =get_task_mm (task); -put_task_struct (task);if (!mm) goto out;
fs/proc/fd.c +10 -15
@@ -170,24 +170,19 @@ static const struct dentry_operations ti .d_delete = pid_delete_dentry, }; -static int proc_fd_link (struct dentry *dentry,struct path *path) +static int proc_fd_link (struct dentry *dentry,struct path *path, +struct task_struct *task) { -struct task_struct *task;int ret = -ENOENT ; +unsigned int fd =proc_fd (d_inode (dentry)); +struct file *fd_file; - task =get_proc_task (d_inode (dentry)); -if (task) { -unsigned int fd =proc_fd (d_inode (dentry)); -struct file *fd_file; - - fd_file =fget_task (task, fd); -if (fd_file) { - *path = fd_file->f_path; -path_get (&fd_file->f_path); - ret =0 ; -fput (fd_file); - } -put_task_struct (task); + fd_file =fget_task (task, fd); +if (fd_file) { + *path = fd_file->f_path; +path_get (&fd_file->f_path); + ret =0 ; +fput (fd_file); }return ret;
fs/proc/internal.h +1 -1
@@ -107,7 +107,7 @@ extern struct kmem_cache *proc_dir_entryvoid pde_free (struct proc_dir_entry *pde);union proc_op { -int (*proc_get_link)(struct dentry *,struct path *); +int (*proc_get_link)(struct dentry *,struct path *,struct task_struct *);int (*proc_show)(struct seq_file *m,struct pid_namespace *ns,struct pid *pid,struct task_struct *task);
Greg Kroah-Hartman
maintainerPATCH 1013/1067 HID: add haptics page defines
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Angela Czubak <[email protected]>
[ Upstream commit 5e0ae59159e3a07391a35865bb79ff335473fa79 ]
Introduce haptic usages as defined in HID Usage Tables specification. Add HID units for newton and gram.
Stable-dep-of: 8813b0612275 ("HID: multitouch: fix out-of-bounds bit access on mt_io_flags")
--- include/linux/hid.h | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+)
include/linux/hid.h +29 -0
@@ -153,6 +153,7 @@ struct hid_item {#define HID_UP_TELEPHONY 0x000b0000#define HID_UP_CONSUMER 0x000c0000#define HID_UP_DIGITIZER 0x000d0000 +#define HID_UP_HAPTIC 0x000e0000#define HID_UP_PID 0x000f0000#define HID_UP_BATTERY 0x00850000#define HID_UP_HPVENDOR 0xff7f0000
@@ -311,6 +312,28 @@ struct hid_item {#define HID_DG_TOOLSERIALNUMBER 0x000d005b#define HID_DG_LATENCYMODE 0x000d0060 +#define HID_HP_SIMPLECONTROLLER 0x000e0001 +#define HID_HP_WAVEFORMLIST 0x000e0010 +#define HID_HP_DURATIONLIST 0x000e0011 +#define HID_HP_AUTOTRIGGER 0x000e0020 +#define HID_HP_MANUALTRIGGER 0x000e0021 +#define HID_HP_AUTOTRIGGERASSOCIATEDCONTROL 0x000e0022 +#define HID_HP_INTENSITY 0x000e0023 +#define HID_HP_REPEATCOUNT 0x000e0024 +#define HID_HP_RETRIGGERPERIOD 0x000e0025 +#define HID_HP_WAVEFORMVENDORPAGE 0x000e0026 +#define HID_HP_WAVEFORMVENDORID 0x000e0027 +#define HID_HP_WAVEFORMCUTOFFTIME 0x000e0028 +#define HID_HP_WAVEFORMNONE 0x000e1001 +#define HID_HP_WAVEFORMSTOP 0x000e1002 +#define HID_HP_WAVEFORMCLICK 0x000e1003 +#define HID_HP_WAVEFORMBUZZCONTINUOUS 0x000e1004 +#define HID_HP_WAVEFORMRUMBLECONTINUOUS 0x000e1005 +#define HID_HP_WAVEFORMPRESS 0x000e1006 +#define HID_HP_WAVEFORMRELEASE 0x000e1007 +#define HID_HP_VENDORWAVEFORMMIN 0x000e2001 +#define HID_HP_VENDORWAVEFORMMAX 0x000e2fff +#define HID_BAT_ABSOLUTESTATEOFCHARGE 0x00850065#define HID_BAT_CHARGING 0x00850044
@@ -392,6 +415,12 @@ struct hid_item {#define HID_BOOT_PROTOCOL 0/* +* HID units +*/ +#define HID_UNIT_GRAM 0x0101 +#define HID_UNIT_NEWTON 0xe111 + +/* * This is the global environment of the parser. This information is * persistent for main-items. The global environment can be saved and * restored with PUSH/POP statements.
Greg Kroah-Hartman
maintainerPATCH 1014/1067 HID: multitouch: fix out-of-bounds bit access on mt_io_flags
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Trung Nguyen <[email protected]>
[ Upstream commit 8813b0612275cc61fe9e6603d0ee019247ade6be ]
mt_io_flags is a single unsigned long, but mt_process_slot(), mt_release_pending_palms() and mt_release_contacts() use it as a per-slot bitmap indexed by the slot number. That slot number is only bounded by td->maxcontacts, which is taken from the device's ContactCountMaximum feature report and can be up to 255, not by BITS_PER_LONG.
As a result, a multitouch device that advertises a large contact count makes set_bit()/clear_bit() operate past the mt_io_flags word and corrupt the adjacent members of struct mt_device. The sticky-fingers release timer is the easiest way to reach this. mt_release_contacts() runs
for (i = 0; i < mt->num_slots; i++) clear_bit(i, &td->mt_io_flags);
with num_slots == maxcontacts. For maxcontacts around 250 the loop clears the bits that overlap td->applications.next, zeroing that list head, and the list_for_each_entry() that immediately follows then dereferences NULL. The kernel panics from timer (softirq) context. On a KASAN build this shows up as a general protection fault in mt_release_contacts() with a null-ptr-deref at offset 0x58, which is offsetof(struct mt_application, num_received).
The state is reachable from an untrusted USB or Bluetooth HID multitouch device; no local privileges are required.
Store the per-slot active state in a separately allocated bitmap sized for maxcontacts, the same pattern already used for pending_palm_slots, and keep only MT_IO_FLAGS_RUNNING in mt_io_flags. The two "mt_io_flags & MT_IO_SLOTS_MASK" arming checks become bitmap_empty(td->active_slots, td->maxcontacts).
Move MT_IO_FLAGS_RUNNING back to bit 0. It was bumped to bit 32 by the same commit to leave the low byte for the slot bits; with the slot bits gone it fits in bit 0 again, which also keeps it within the unsigned long on 32-bit.
--- drivers/hid/hid-multitouch.c | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-)
drivers/hid/hid-multitouch.c +20 -12
@@ -31,6 +31,7 @@* [1] https://gitlab.freedesktop.org/libevdev/hid-tools */ +#include <linux/bitmap.h> #include <linux/device.h> #include <linux/hid.h> #include <linux/module.h>
@@ -83,8 +84,7 @@ enum latency_mode { HID_LATENCY_HIGH = 1, }; -#define MT_IO_SLOTS_MASK GENMASK(7, 0)/* reserve first 8 bits for slot tracking */ -#define MT_IO_FLAGS_RUNNING 32 +#define MT_IO_FLAGS_RUNNING 0static const bool mtrue = true;/* default for true */ static const bool mfalse;/* default for false */
@@ -160,10 +160,9 @@ struct mt_device {struct mt_class mtclass;/* our mt device class */ struct timer_list release_timer;/* to release sticky fingers */ struct hid_device *hdev;/* hid_device we're attached to */ -unsigned long mt_io_flags;/* mt flags (MT_IO_FLAGS_RUNNING) -* first 8 bits are reserved for keeping the slot -* states, this is fine because we only support up -* to 250 slots (MT_MAX_MAXCONTACT) +unsigned long mt_io_flags;/* mt flags (MT_IO_FLAGS_RUNNING) */ +unsigned long *active_slots;/* bitmap of slots with an active +* contact, sized for maxcontacts */ __u8 inputmode_value;/* InputMode HID feature value */ __u8 maxcontacts;
@@ -947,7 +946,7 @@ static void mt_release_pending_palms(strfor_each_set_bit (slotnum, app->pending_palm_slots, td->maxcontacts) {clear_bit (slotnum, app->pending_palm_slots); -clear_bit (slotnum, &td->mt_io_flags); +clear_bit (slotnum, td->active_slots);input_mt_slot (input, slotnum);input_mt_report_slot_inactive (input);
@@ -1153,9 +1152,9 @@ static int mt_process_slot(struct mt_devinput_event (input,EV_ABS ,ABS_MT_TOUCH_MAJOR , major);input_event (input,EV_ABS ,ABS_MT_TOUCH_MINOR , minor); -set_bit (slotnum, &td->mt_io_flags); +set_bit (slotnum, td->active_slots); }else { -clear_bit (slotnum, &td->mt_io_flags); +clear_bit (slotnum, td->active_slots); }return 0 ;
@@ -1290,7 +1289,7 @@ static void mt_touch_report(struct hid_d* defect. */ if (app->quirks &MT_QUIRK_STICKY_FINGERS ) { -if (td->mt_io_flags &MT_IO_SLOTS_MASK ) +if (!bitmap_empty (td->active_slots, td->maxcontacts))mod_timer (&td->release_timer, jiffies +msecs_to_jiffies (100 ));else
@@ -1330,6 +1329,15 @@ static int mt_touch_input_configured(strif (td->is_buttonpad)__set_bit (INPUT_PROP_BUTTONPAD , input->propbit); +if (!td->active_slots) { + td->active_slots =devm_kcalloc (&td->hdev->dev, +BITS_TO_LONGS (td->maxcontacts), +sizeof (long ), +GFP_KERNEL ); +if (!td->active_slots) +return -ENOMEM ; + } + app->pending_palm_slots = devm_kcalloc(&hi->input->dev, BITS_TO_LONGS(td->maxcontacts), sizeof(long),
@@ -1738,7 +1746,7 @@ static void mt_release_contacts(struct hfor (i =0 ; i < mt->num_slots; i++) {input_mt_slot (input_dev, i);input_mt_report_slot_inactive (input_dev); -clear_bit (i, &td->mt_io_flags); +clear_bit (i, td->active_slots); }input_mt_sync_frame (input_dev);input_sync (input_dev);
@@ -1761,7 +1769,7 @@ static void mt_expired_timeout(struct ti*/ if (test_and_set_bit_lock (MT_IO_FLAGS_RUNNING , &td->mt_io_flags))return ; -if (td->mt_io_flags &MT_IO_SLOTS_MASK ) +if (!bitmap_empty (td->active_slots, td->maxcontacts))mt_release_contacts (hdev);clear_bit_unlock (MT_IO_FLAGS_RUNNING , &td->mt_io_flags); }
Greg Kroah-Hartman
maintainerPATCH 1015/1067 seqlock: Introduce scoped_seqlock_read()
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Peter Zijlstra <[email protected]>
[ Upstream commit cc39f3872c0865bef992b713338df369554fa9e0 ]
The read_seqbegin/need_seqretry/done_seqretry API is cumbersome and error prone. With the new helper the "typical" code like
int seq, nextseq;
unsigned long flags;
nextseq = 0;
do {
seq = nextseq;
flags = read_seqbegin_or_lock_irqsave(&seqlock, &seq);
// read-side critical section
nextseq = 1;
} while (need_seqretry(&seqlock, seq));
done_seqretry_irqrestore(&seqlock, seq, flags);can be rewritten as
scoped_seqlock_read (&seqlock, ss_lock_irqsave) {
// read-side critical section
}Original idea by Oleg Nesterov; with contributions from Linus.
Stable-dep-of: 6650527444da ("proc: protect ptrace_may_access() with exec_update_lock (part 1)")
--- include/linux/seqlock.h | 111 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+)
include/linux/seqlock.h +111 -0
@@ -1266,4 +1266,115 @@ done_seqretry_irqrestore(seqlock_t *lockif (seq &1 )read_sequnlock_excl_irqrestore (lock, flags); } + +enum ss_state { + ss_done =0 , + ss_lock, + ss_lock_irqsave, + ss_lockless, +}; + +struct ss_tmp { +enum ss_state state; +unsigned long data; +spinlock_t *lock; +spinlock_t *lock_irqsave; +}; + +static inline void __scoped_seqlock_cleanup (struct ss_tmp *sst) +{ +if (sst->lock) +spin_unlock (sst->lock); +if (sst->lock_irqsave) +spin_unlock_irqrestore (sst->lock_irqsave, sst->data); +} + +extern void __scoped_seqlock_invalid_target (void ); + +#if defined(CONFIG_CC_IS_GCC ) &&CONFIG_GCC_VERSION <90000 +/* +* For some reason some GCC-8 architectures (nios2, alpha) have trouble +* determining that the ss_done state is impossible in __scoped_seqlock_next() +* below. +*/ +static inline void __scoped_seqlock_bug (void ) { } +#else +/* +* Canary for compiler optimization -- if the compiler doesn't realize this is +* an impossible state, it very likely generates sub-optimal code here. +*/ +extern void __scoped_seqlock_bug (void ); +#endif + +static inline void +__scoped_seqlock_next (struct ss_tmp *sst,seqlock_t *lock,enum ss_state target) +{ +switch (sst->state) { +case ss_done: +__scoped_seqlock_bug (); +return ; + +case ss_lock: +case ss_lock_irqsave: + sst->state = ss_done; +return ; + +case ss_lockless: +if (!read_seqretry (lock, sst->data)) { + sst->state = ss_done; +return ; + } +break ; + } + +switch (target) { +case ss_done: +__scoped_seqlock_invalid_target (); +return ; + +case ss_lock: + sst->lock = &lock->lock; +spin_lock (sst->lock); + sst->state = ss_lock; +return ; + +case ss_lock_irqsave: + sst->lock_irqsave = &lock->lock; +spin_lock_irqsave (sst->lock_irqsave, sst->data); + sst->state = ss_lock_irqsave; +return ; + +case ss_lockless: + sst->data =read_seqbegin (lock); +return ; + } +} + +#define __scoped_seqlock_read (_seqlock, _target, _s) \ + for (struct ss_tmp _s __cleanup(__scoped_seqlock_cleanup) = \ + { .state = ss_lockless, .data = read_seqbegin(_seqlock) }; \ + _s.state != ss_done; \ + __scoped_seqlock_next(&_s, _seqlock, _target)) + +/** +* scoped_seqlock_read (lock, ss_state) - execute the read side critical +* section without manual sequence +* counter handling or calls to other +* helpers +* @lock: pointer to seqlock_t protecting the data +* @ss_state: one of {ss_lock, ss_lock_irqsave, ss_lockless} indicating +* the type of critical read section +* +* Example: +* +* scoped_seqlock_read (&lock, ss_lock) { +* // read-side critical section +* } +* +* Starts with a lockess pass first. If it fails, restarts the critical +* section with the lock held. +*/ +#define scoped_seqlock_read (_seqlock, _target) \ + __scoped_seqlock_read(_seqlock, _target, __UNIQUE_ID(seqlock)) +#endif /* __LINUX_SEQLOCK_H */
Greg Kroah-Hartman
maintainerPATCH 1016/1067 seqlock: Change do_task_stat() to use scoped_seqlock_read()
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Oleg Nesterov <[email protected]>
[ Upstream commit b76f72bea2c601afec81829ea427fc0d20f83216 ]
To simplify the code and make it more readable.
Stable-dep-of: 6650527444da ("proc: protect ptrace_may_access() with exec_update_lock (part 1)")
--- fs/proc/array.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-)
fs/proc/array.c +2 -7
@@ -473,7 +473,6 @@ static int do_task_stat(struct seq_fileunsigned long flags;int exit_code = task->exit_code;struct signal_struct *sig = task->signal; -unsigned int seq =1 ; state = *get_task_state (task); vsize = eip = esp =0 ;
@@ -530,10 +529,7 @@ static int do_task_stat(struct seq_fileif (permitted && (!whole || num_threads <2 )) wchan = !task_is_running (task); - do { - seq++; /* 2 on the 1st/lockless path, otherwise odd */ - flags = read_seqbegin_or_lock_irqsave(&sig->stats_lock, &seq); - + scoped_seqlock_read (&sig->stats_lock, ss_lock_irqsave) { cmin_flt = sig->cmin_flt; cmaj_flt = sig->cmaj_flt; cutime = sig->cutime;
@@ -555,8 +551,7 @@ static int do_task_stat(struct seq_file }rcu_read_unlock (); } - }while (need_seqretry (&sig->stats_lock, seq)); -done_seqretry_irqrestore (&sig->stats_lock, seq, flags); + }if (whole) {thread_group_cputime_adjusted (task, &utime, &stime);
Greg Kroah-Hartman
maintainerPATCH 1017/1067 proc: protect ptrace_may_access() with exec_update_lock (part 1)
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jann Horn <[email protected]>
[ Upstream commit 6650527444dadc63d84aa939d14ecba4fadb2f69 ]
Fix the easy cases where procfs currently calls ptrace_may_access() without exec_update_lock protection, where the fix is to simply add the extra lock or use mm_access():
- do_task_stat(): grab exec_update_lock
- proc_pid_wchan(): grab exec_update_lock
- proc_map_files_lookup(): use mm_access() instead of get_task_mm()
- proc_map_files_readdir(): use mm_access() instead of get_task_mm()
- proc_ns_get_link(): grab exec_update_lock
- proc_ns_readlink(): grab exec_update_lock
--- fs/proc/array.c | 6 ++++++ fs/proc/base.c | 41 ++++++++++++++++++++++------------------- fs/proc/namespaces.c | 12 ++++++++++++ 3 files changed, 40 insertions(+), 19 deletions(-)
fs/proc/array.c +6 -0
@@ -473,6 +473,11 @@ static int do_task_stat(struct seq_fileunsigned long flags;int exit_code = task->exit_code;struct signal_struct *sig = task->signal; +int ret; + + ret =down_read_killable (&task->signal->exec_update_lock); +if (ret) +return ret; state = *get_task_state (task); vsize = eip = esp =0 ;
@@ -648,6 +653,7 @@ static int do_task_stat(struct seq_fileseq_puts (m," 0" );seq_putc (m,'\n' ); +up_read (&task->signal->exec_update_lock);if (mm)mmput (mm);return 0 ;
fs/proc/base.c +22 -19
101 lines not on this page · raw message
fs/proc/namespaces.c +12 -0
@@ -55,6 +55,10 @@ static const char *proc_ns_get_link(struif (!task)return ERR_PTR (-EACCES ); + error =down_read_killable (&task->signal->exec_update_lock); +if (error) + goto out_put_task; +if (!ptrace_may_access (task,PTRACE_MODE_READ_FSCREDS )) goto out;
@@ -64,6 +68,8 @@ static const char *proc_ns_get_link(stru error =nd_jump_link (&ns_path); out: +up_read (&task->signal->exec_update_lock); +out_put_task:put_task_struct (task);return ERR_PTR (error); }
@@ -80,11 +86,17 @@ static int proc_ns_readlink(struct dentrif (!task)return res; + res =down_read_killable (&task->signal->exec_update_lock); +if (res) + goto out_put_task; +if (ptrace_may_access (task,PTRACE_MODE_READ_FSCREDS )) { res =ns_get_name (name,sizeof (name), task, ns_ops);if (res >=0 ) res =readlink_copy (buffer, buflen, name); } +up_read (&task->signal->exec_update_lock); +out_put_task:put_task_struct (task);return res; }
Greg Kroah-Hartman
maintainerPATCH 1018/1067 treewide: Switch/rename to timer_delete[_sync]()
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Thomas Gleixner <[email protected]>
[ Upstream commit 8fa7292fee5c5240402371ea89ab285ec856c916 ]
timer_delete[_sync]() replaces del_timer[_sync](). Convert the whole tree over and remove the historical wrapper inlines.
Conversion was done with coccinelle plus manual fixups where necessary.
Stable-dep-of: 75fe87e19d8a ("HID: appleir: fix UAF on pending key_up_timer in remove()")
--- drivers/hid/hid-appleir.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/hid/hid-appleir.c +1 -1
@@ -319,7 +319,7 @@ static void appleir_remove(struct hid_de {struct appleir *appleir =hid_get_drvdata (hid);hid_hw_stop (hid); -del_timer_sync (&appleir->key_up_timer); +timer_delete_sync (&appleir->key_up_timer); }static const struct hid_device_id appleir_devices[] = {
Greg Kroah-Hartman
maintainerPATCH 1019/1067 HID: appleir: fix UAF on pending key_up_timer in remove()
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Manish Khadka <[email protected]>
[ Upstream commit 75fe87e19d8aff81eb2c64d15d244ab8da4de945 ]
appleir_remove() runs hid_hw_stop() before timer_delete_sync(). hid_hw_stop() synchronously unregisters the HID input device via hid_disconnect() -> hidinput_disconnect() -> input_unregister_device(), which drops the last reference and frees the underlying input_dev when no userspace handle holds it open.
key_up_tick() reads appleir->input_dev and calls input_report_key() / input_sync() on it. The timer is armed from appleir_raw_event() with a HZ/8 (~125 ms) timeout on every keydown and key-repeat report. If a key was pressed shortly before the device is disconnected, the timer can fire after hid_hw_stop() has freed input_dev but before the teardown drains it.
A simple reorder is not sufficient. Putting the timer drain first still leaves a window where a USB URB completion (raw_event) running during hid_hw_stop() can call mod_timer() and re-arm the timer, which then fires after hidinput_disconnect() has freed input_dev. The same URB-completion window also lets raw_event() reach key_up(), key_down() and battery_flat() directly, all of which dereference appleir->input_dev.
Introduce a 'removing' flag on struct appleir, gated by the existing spinlock. appleir_remove() sets the flag under the lock and then shuts down the timer with timer_shutdown_sync(), which both drains any in-flight callback and permanently disables further mod_timer() calls. appleir_raw_event() and key_up_tick() bail out early if the flag is set, so no path can arm or run the timer, or dereference appleir->input_dev, after remove() has started tearing down.
The keyrepeat and flatbattery branches of appleir_raw_event() previously called into the input layer without holding the spinlock; take it now so the flag check is well-defined. This incidentally closes a pre-existing read-side race on appleir->current_key in the keyrepeat branch.
This bug is structurally a sibling of commit 4db2af929279 ("HID:
appletb-kbd: fix UAF in inactivity-timer cleanup path") and has been present since the driver was introduced.
--- drivers/hid/hid-appleir.c | 45 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 35 insertions(+), 10 deletions(-)
drivers/hid/hid-appleir.c +35 -10
@@ -109,9 +109,10 @@ struct appleir {struct hid_device *hid;unsigned short keymap[ARRAY_SIZE (appleir_key_table)];struct timer_list key_up_timer;/* timer for key up */ -spinlock_t lock;/* protects .current_key */ +spinlock_t lock;/* protects .current_key, .removing */ int current_key;/* the currently pressed key */ int prev_key_idx;/* key index in a 2 packets message */ +bool removing;/* set during teardown; gates input_dev access */ };static int get_key (int data)
@@ -172,7 +173,7 @@ static void key_up_tick(struct timer_lisunsigned long flags;spin_lock_irqsave (&appleir->lock, flags); -if (appleir->current_key) { +if (!appleir->removing && appleir->current_key) {key_up (hid, appleir, appleir->current_key); appleir->current_key =0 ; }
@@ -195,6 +196,10 @@ static int appleir_raw_event(struct hid_int index;spin_lock_irqsave (&appleir->lock, flags); +if (appleir->removing) { +spin_unlock_irqrestore (&appleir->lock, flags); + goto out; + }/* * If we already have a key down, take it up before marking * this one down
@@ -229,17 +234,25 @@ static int appleir_raw_event(struct hid_ appleir->prev_key_idx =0 ;if (!memcmp (data, keyrepeat,sizeof (keyrepeat))) { -key_down (hid, appleir, appleir->current_key); -/* -* Remote doesn't do key up, either pull them up, in the test -* above, or here set a timer which pulls them up after 1/8 s -*/ -mod_timer (&appleir->key_up_timer, jiffies +HZ /8 ); +spin_lock_irqsave (&appleir->lock, flags); +if (!appleir->removing) { +key_down (hid, appleir, appleir->current_key); +/* +* Remote doesn't do key up, either pull them up, in +* the test above, or here set a timer which pulls them +* up after 1/8 s +*/ +mod_timer (&appleir->key_up_timer, jiffies +HZ /8 ); + } +spin_unlock_irqrestore (&appleir->lock, flags); goto out; }if (!memcmp (data, flatbattery,sizeof (flatbattery))) { -battery_flat (appleir); +spin_lock_irqsave (&appleir->lock, flags); +if (!appleir->removing) +battery_flat (appleir); +spin_unlock_irqrestore (&appleir->lock, flags);/* Fall through */ }
@@ -318,8 +331,20 @@ fail:static void appleir_remove (struct hid_device *hid) {struct appleir *appleir =hid_get_drvdata (hid); +unsigned long flags; + +/* +* Mark the driver as tearing down so that any concurrent raw_event +* (e.g. from a USB URB completion that hid_hw_stop() has not yet +* killed) and the key_up_timer softirq stop touching input_dev +* before hid_hw_stop() frees it via hidinput_disconnect(). +*/ +spin_lock_irqsave (&appleir->lock, flags); + appleir->removing = true; +spin_unlock_irqrestore (&appleir->lock, flags); + +timer_shutdown_sync (&appleir->key_up_timer);hid_hw_stop (hid); -timer_delete_sync (&appleir->key_up_timer); }static const struct hid_device_id appleir_devices[] = {
Greg Kroah-Hartman
maintainerPATCH 1020/1067 serial: 8250_mid: Remove 8250_pci usage
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ilpo Järvinen <[email protected]>
[ Upstream commit a136abd7e7abe0f1247f8ffde6cc7c8ab09f985b ]
8250_mid uses FL_*BASE* from linux/8250_pci.h and nothing else. The code can be simplified by directly defining BARs within the driver instead.
Stable-dep-of: b1b4efea05a5 ("serial: 8250_mid: Disable DMA for selected platforms")
--- drivers/tty/serial/8250/8250_mid.c | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-)
drivers/tty/serial/8250/8250_mid.c +7 -11
@@ -12,7 +12,6 @@#include <linux/rational.h> #include <linux/dma/hsu.h> -#include <linux/8250_pci.h> #include "8250.h"
@@ -32,9 +31,9 @@struct mid8250 ;struct mid8250_board { -unsigned int flags;unsigned long freq;unsigned int base_baud; +unsigned int bar;int (*setup)(struct mid8250 *,struct uart_port *p);void (*exit)(struct mid8250 *); };
@@ -169,7 +168,6 @@ static int dnv_setup(struct mid8250 *mid {struct hsu_dma_chip *chip = &mid->dma_chip;struct pci_dev *pdev =to_pci_dev (p->dev); -unsigned int bar =FL_GET_BASE (mid->board->flags);int ret;pci_set_master (pdev);
@@ -183,7 +181,7 @@ static int dnv_setup(struct mid8250 *mid chip->dev = &pdev->dev; chip->irq =pci_irq_vector (pdev,0 ); chip->regs = p->membase; - chip->length =pci_resource_len (pdev, bar); + chip->length =pci_resource_len (pdev, mid->board->bar); chip->offset =DNV_DMA_CHAN_OFFSET ;/* Falling back to PIO mode if DMA probing fails */
@@ -291,7 +289,6 @@ static int mid8250_probe(struct pci_dev {struct uart_8250_port uart;struct mid8250 *mid; -unsigned int bar;int ret; ret =pcim_enable_device (pdev);
@@ -303,7 +300,6 @@ static int mid8250_probe(struct pci_devreturn -ENOMEM ; mid->board = (struct mid8250_board *)id->driver_data; - bar =FL_GET_BASE (mid->board->flags);memset (&uart,0 ,sizeof (struct uart_8250_port ));
@@ -316,8 +312,8 @@ static int mid8250_probe(struct pci_dev uart.port.flags =UPF_SHARE_IRQ |UPF_FIXED_PORT |UPF_FIXED_TYPE ; uart.port.set_termios = mid8250_set_termios; - uart.port.mapbase =pci_resource_start (pdev, bar); - uart.port.membase =pcim_iomap (pdev, bar,0 ); + uart.port.mapbase =pci_resource_start (pdev, mid->board->bar); + uart.port.membase =pcim_iomap (pdev, mid->board->bar,0 );if (!uart.port.membase)return -ENOMEM ;
@@ -353,25 +349,25 @@ static void mid8250_remove(struct pci_de }static const struct mid8250_board pnw_board = { - .flags =FL_BASE0 , .freq =50000000 , .base_baud =115200 , + .bar =0 , .setup = pnw_setup, .exit = pnw_exit, };static const struct mid8250_board tng_board = { - .flags =FL_BASE0 , .freq =38400000 , .base_baud =1843200 , + .bar =0 , .setup = tng_setup, .exit = tng_exit, };static const struct mid8250_board dnv_board = { - .flags =FL_BASE1 , .freq =133333333 , .base_baud =115200 , + .bar =1 , .setup = dnv_setup, .exit = dnv_exit, };
Greg Kroah-Hartman
maintainerPATCH 1021/1067 serial: 8250_mid: Disable DMA for selected platforms
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Andy Shevchenko <[email protected]>
[ Upstream commit b1b4efea05a56c0995e4702a86d6624b4fdff32f ]
In accordance with Errata (specification updates)
HSUART May Stop Functioning when DMA is Active.
- Denverton document #572409, rev 3.4, DNV60
- Ice Lake Xeon D document #714070, ICXD65
- Snowridge document #731931, SNR44
For a quick fix just disable the respective callbacks during the device probe. Depending on the future development we might remove them completely.
--- drivers/tty/serial/8250/8250_mid.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-)
drivers/tty/serial/8250/8250_mid.c +11 -2
@@ -10,6 +10,7 @@#include <linux/module.h> #include <linux/pci.h> #include <linux/rational.h> +#include <linux/util_macros.h> #include <linux/dma/hsu.h>
@@ -368,8 +369,16 @@ static const struct mid8250_board dnv_bo .freq = 133333333, .base_baud = 115200, .bar = 1, - .setup = dnv_setup, - .exit = dnv_exit, +/* +* Errata: +* HSUART May Stop Functioning when DMA is Active. +* +* - Denverton document #572409, rev 3.4, DNV60 +* - Ice Lake Xeon D document #714070, ICXD65 +* - Snowridge document #731931, SNR44 +*/ + .setup = PTR_IF(false, dnv_setup), + .exit = PTR_IF(false, dnv_exit), };static const struct pci_device_id pci_ids[] = {
Greg Kroah-Hartman
maintainerPATCH 1022/1067 hfs/hfsplus: prevent getting negative values of offset/length
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Viacheslav Dubeyko <[email protected]>
[ Upstream commit 00c14a09a70e10ae18eb3707d0059291425c04bd ]
The syzbot reported KASAN out-of-bounds issue inhfs_bnode_move():
[ 45.588165][ T9821] hfs: dst 14, src 65536, len -65536 [ 45.588895][ T9821] ================================================================== [ 45.590114][ T9821] BUG: KASAN: out-of-bounds in hfs_bnode_move+0xfd/0x140 [ 45.591127][ T9821] Read of size 18446744073709486080 at addr ffff888035935400 by task repro/9821 [ 45.592207][ T9821] [ 45.592420][ T9821] CPU: 0 UID: 0 PID: 9821 Comm: repro Not tainted 6.16.0-rc7-dirty #42 PREEMPT(full) [ 45.592428][ T9821] Hardware name: QEMU Ubuntu 24.04 PC (i440FX + PIIX, 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014 [ 45.592431][ T9821] Call Trace: [ 45.592434][ T9821] <TASK> [ 45.592437][ T9821] dump_stack_lvl+0x1c1/0x2a0 [ 45.592446][ T9821] ? __virt_addr_valid+0x1c8/0x5c0 [ 45.592454][ T9821] ? __pfx_dump_stack_lvl+0x10/0x10 [ 45.592461][ T9821] ? rcu_is_watching+0x15/0xb0 [ 45.592469][ T9821] ? lock_release+0x4b/0x3e0 [ 45.592476][ T9821] ? __virt_addr_valid+0x1c8/0x5c0 [ 45.592483][ T9821] ? __virt_addr_valid+0x4a5/0x5c0 [ 45.592491][ T9821] print_report+0x17e/0x7c0 [ 45.592497][ T9821] ? __virt_addr_valid+0x1c8/0x5c0 [ 45.592504][ T9821] ? __virt_addr_valid+0x4a5/0x5c0 [ 45.592511][ T9821] ? __phys_addr+0xd3/0x180 [ 45.592519][ T9821] ? hfs_bnode_move+0xfd/0x140 [ 45.592526][ T9821] kasan_report+0x147/0x180 [ 45.592531][ T9821] ? _printk+0xcf/0x120 [ 45.592537][ T9821] ? hfs_bnode_move+0xfd/0x140 [ 45.592544][ T9821] ? hfs_bnode_move+0xfd/0x140 [ 45.592552][ T9821] kasan_check_range+0x2b0/0x2c0 [ 45.592557][ T9821] ? hfs_bnode_move+0xfd/0x140 [ 45.592565][ T9821] __asan_memmove+0x29/0x70 [ 45.592572][ T9821] hfs_bnode_move+0xfd/0x140 [ 45.592580][ T9821] hfs_brec_remove+0x473/0x560 [ 45.592589][ T9821] hfs_cat_move+0x6fb/0x960 [ 45.592598][ T9821] ? __pfx_hfs_cat_move+0x10/0x10 [ 45.592607][ T9821] ? seqcount_lockdep_reader_access+0x122/0x1c0 [ 45.592614][ T9821] ? lockdep_hardirqs_on+0x9c/0x150 [ 45.592631][ T9821] ? __lock_acquire+0xaec/0xd80 [ 45.592641][ T9821] hfs_rename+0x1dc/0x2d0 [ 45.592649][ T9821] ? __pfx_hfs_rename+0x10/0x10 [ 45.592657][ T9821] vfs_rename+0xac6/0xed0 [ 45.592664][ T9821] ? __pfx_vfs_rename+0x10/0x10 [ 45.592670][ T9821] ? d_alloc+0x144/0x190 [ 45.592677][ T9821] ? bpf_lsm_path_rename+0x9/0x20 [ 45.592683][ T9821] ? security_path_rename+0x17d/0x490 [ 45.592691][ T9821] do_renameat2+0x890/0xc50 [ 45.592699][ T9821] ? __pfx_do_renameat2+0x10/0x10 [ 45.592707][ T9821] ? getname_flags+0x1e5/0x540 [ 45.592714][ T9821] __x64_sys_rename+0x82/0x90 [ 45.592720][ T9821] ? entry_SYSCALL_64_after_hwframe+0x77/0x7f [ 45.592725][ T9821] do_syscall_64+0xf3/0x3a0 [ 45.592741][ T9821] ? exc_page_fault+0x9f/0xf0 [ 45.592748][ T9821] entry_SYSCALL_64_after_hwframe+0x77/0x7f [ 45.592754][ T9821] RIP: 0033:0x7f7f73fe3fc9 [ 45.592760][ T9821] Code: 00 c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 48 [ 45.592765][ T9821] RSP: 002b:00007ffc7e116cf8 EFLAGS: 00000283 ORIG_RAX: 0000000000000052 [ 45.592772][ T9821] RAX: ffffffffffffffda RBX: 0000000000000000 RCX: 00007f7f73fe3fc9 [ 45.592776][ T9821] RDX: 0000200000000871 RSI: 0000200000000780 RDI: 00002000000003c0 [ 45.592781][ T9821] RBP: 00007ffc7e116d00 R08: 0000000000000000 R09: 00007ffc7e116d30 [ 45.592784][ T9821] R10: fffffffffffffff0 R11: 0000000000000283 R12: 00005557e81f8250 [ 45.592788][ T9821] R13: 0000000000000000 R14: 0000000000000000 R15: 0000000000000000 [ 45.592795][ T9821] </TASK> [ 45.592797][ T9821] [ 45.619721][ T9821] The buggy address belongs to the physical page: [ 45.620300][ T9821] page: refcount:1 mapcount:1 mapping:0000000000000000 index:0x559a88174 pfn:0x35935 [ 45.621150][ T9821] memcg:ffff88810a1d5b00 [ 45.621531][ T9821] anon flags: 0xfff60000020838(uptodate|dirty|lru|owner_2|swapbacked|node=0|zone=1|lastcpupid=0x7ff) [ 45.622496][ T9821] raw: 00fff60000020838 ffffea0000d64d88 ffff888021753e10 ffff888029da0771 [ 45.623260][ T9821] raw: 0000000559a88174 0000000000000000 0000000100000000 ffff88810a1d5b00 [ 45.624030][ T9821] page dumped because: kasan: bad access detected [ 45.624602][ T9821] page_owner tracks the page as allocated [ 45.625115][ T9821] page last allocated via order 0, migratetype Movable, gfp_mask 0x140dca(GFP_HIGHUSER_MOVABLE|__GFP_ZERO0 [ 45.626685][ T9821] post_alloc_hook+0x240/0x2a0 [ 45.627127][ T9821] get_page_from_freelist+0x2101/0x21e0 [ 45.627628][ T9821] __alloc_frozen_pages_noprof+0x274/0x380 [ 45.628154][ T9821] alloc_pages_mpol+0x241/0x4b0 [ 45.628593][ T9821] vma_alloc_folio_noprof+0xe4/0x210 [ 45.629066][ T9821] folio_prealloc+0x30/0x180 [ 45.629487][ T9821] __handle_mm_fault+0x34bd/0x5640 [ 45.629957][ T9821] handle_mm_fault+0x40e/0x8e0 [ 45.630392][ T9821] do_user_addr_fault+0xa81/0x1390 [ 45.630862][ T9821] exc_page_fault+0x76/0xf0 [ 45.631273][ T9821] asm_exc_page_fault+0x26/0x30 [ 45.631712][ T9821] page last free pid 5269 tgid 5269 stack trace: [ 45.632281][ T9821] free_unref_folios+0xc73/0x14c0 [ 45.632740][ T9821] folios_put_refs+0x55b/0x640 [ 45.633177][ T9821] free_pages_and_swap_cache+0x26d/0x510 [ 45.633685][ T9821] tlb_flush_mmu+0x3a0/0x680 [ 45.634105][ T9821] tlb_finish_mmu+0xd4/0x200 [ 45.634525][ T9821] exit_mmap+0x44c/0xb70 [ 45.634914][ T9821] __mmput+0x118/0x420 [ 45.635286][ T9821] exit_mm+0x1da/0x2c0 [ 45.635659][ T9821] do_exit+0x652/0x2330 [ 45.636039][ T9821] do_group_exit+0x21c/0x2d0 [ 45.636457][ T9821] __x64_sys_exit_group+0x3f/0x40 [ 45.636915][ T9821] x64_sys_call+0x21ba/0x21c0 [ 45.637342][ T9821] do_syscall_64+0xf3/0x3a0 [ 45.637756][ T9821] entry_SYSCALL_64_after_hwframe+0x77/0x7f [ 45.638290][ T9821] page has been migrated, last migrate reason: numa_misplaced [ 45.638956][ T9821] [ 45.639173][ T9821] Memory state around the buggy address: [ 45.639677][ T9821] ffff888035935300: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [ 45.640397][ T9821] ffff888035935380: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [ 45.641117][ T9821] >ffff888035935400: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [ 45.641837][ T9821] ^ [ 45.642207][ T9821] ffff888035935480: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [ 45.642929][ T9821] ffff888035935500: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [ 45.643650][ T9821] ==================================================================
This commit [1] fixes the issue if an offset inside of b-tree node or length of the request is bigger than b-tree node. However, this fix is still not ready for negative values
of the offset or length. Moreover, negative values of the offset or length doesn't make sense for b-tree's operations. Because we could try to access the memory address outside of the beginning of memory page's addresses range. Also, using of negative values make logic very complicated, unpredictable, and we could access the wrong item(s)
in the b-tree node.
This patch changes b-tree interface by means of converting signed integer arguments of offset and length on u32 type. Such conversion has goal to prevent of using negative values unintentionally or by mistake in b-tree operations.
[1] 'commit a431930c9bac ("hfs: fix slab-out-of-bounds in hfs_bnode_read()")'
Stable-dep-of: 966cb76fb285 ("hfs/hfsplus: fix u32 overflow in check_and_correct_requested_length")
--- fs/hfs/bfind.c | 2 - fs/hfs/bnode.c | 52 ++++++++++++++-------------- fs/hfs/brec.c | 2 - fs/hfs/btree.c | 2 - fs/hfs/btree.h | 71 +++++++++++++++++++------------------- fs/hfs/hfs_fs.h | 88 ++++++++++++++++++++++++++++-------------------- fs/hfs/inode.c | 3 + fs/hfsplus/bfind.c | 2 - fs/hfsplus/bnode.c | 60 ++++++++++++++++---------------- fs/hfsplus/brec.c | 2 - fs/hfsplus/btree.c | 2 - fs/hfsplus/hfsplus_fs.h | 38 ++++++++++---------- 12 files changed, 171 insertions(+), 153 deletions(-)
fs/hfs/bfind.c +1 -1
@@ -164,7 +164,7 @@ release:return res; } -int hfs_brec_read (struct hfs_find_data *fd,void *rec,int rec_len) +int hfs_brec_read (struct hfs_find_data *fd,void *rec,u32 rec_len) {int res;
fs/hfs/bnode.c +26 -26
172 lines not on this page · raw message
fs/hfs/brec.c +1 -1
@@ -62,7 +62,7 @@ u16 hfs_brec_keylen(struct hfs_bnode *noreturn retval; } -int hfs_brec_insert (struct hfs_find_data *fd,void *entry,int entry_len) +int hfs_brec_insert (struct hfs_find_data *fd,void *entry,u32 entry_len) {struct hfs_btree *tree;struct hfs_bnode *node, *new_node;
fs/hfs/btree.c +1 -1
@@ -224,7 +224,7 @@ static struct hfs_bnode *hfs_bmap_new_bm }/* Make sure @tree has enough space for the @rsvd_nodes */ -int hfs_bmap_reserve (struct hfs_btree *tree,int rsvd_nodes) +int hfs_bmap_reserve (struct hfs_btree *tree,u32 rsvd_nodes) {struct inode *inode = tree->inode;u32 count;
fs/hfs/btree.h +36 -35
84 lines not on this page · raw message
fs/hfs/hfs_fs.h +52 -36
125 lines not on this page · raw message
fs/hfs/inode.c +2 -1
@@ -50,7 +50,8 @@ static void hfs_write_failed(struct addr }int hfs_write_begin (struct file *file,struct address_space *mapping, -loff_t pos,unsigned len,struct page **pagep,void **fsdata) +loff_t pos,unsigned int len,struct page **pagep, +void **fsdata) {int ret;
fs/hfsplus/bfind.c +1 -1
@@ -210,7 +210,7 @@ release:return res; } -int hfs_brec_read (struct hfs_find_data *fd,void *rec,int rec_len) +int hfs_brec_read (struct hfs_find_data *fd,void *rec,u32 rec_len) {int res;
fs/hfsplus/bnode.c +30 -30
200 lines not on this page · raw message
fs/hfsplus/brec.c +1 -1
@@ -60,7 +60,7 @@ u16 hfs_brec_keylen(struct hfs_bnode *noreturn retval; } -int hfs_brec_insert (struct hfs_find_data *fd,void *entry,int entry_len) +int hfs_brec_insert (struct hfs_find_data *fd,void *entry,u32 entry_len) {struct hfs_btree *tree;struct hfs_bnode *node, *new_node;
fs/hfsplus/btree.c +1 -1
@@ -344,7 +344,7 @@ static struct hfs_bnode *hfs_bmap_new_bm }/* Make sure @tree has enough space for the @rsvd_nodes */ -int hfs_bmap_reserve (struct hfs_btree *tree,int rsvd_nodes) +int hfs_bmap_reserve (struct hfs_btree *tree,u32 rsvd_nodes) {struct inode *inode = tree->inode;struct hfsplus_inode_info *hip =HFSPLUS_I (inode);
fs/hfsplus/hfsplus_fs.h +19 -19
@@ -388,21 +388,21 @@ u32 hfsplus_calc_btree_clump_size(u32 blstruct hfs_btree *hfs_btree_open (struct super_block *sb,u32 id);void hfs_btree_close (struct hfs_btree *tree);int hfs_btree_write (struct hfs_btree *tree); -int hfs_bmap_reserve (struct hfs_btree *tree,int rsvd_nodes); +int hfs_bmap_reserve (struct hfs_btree *tree,u32 rsvd_nodes);struct hfs_bnode *hfs_bmap_alloc (struct hfs_btree *tree);void hfs_bmap_free (struct hfs_bnode *node);/* bnode.c */ -void hfs_bnode_read (struct hfs_bnode *node,void *buf,int off,int len); -u16 hfs_bnode_read_u16 (struct hfs_bnode *node,int off); -u8 hfs_bnode_read_u8 (struct hfs_bnode *node,int off); -void hfs_bnode_read_key (struct hfs_bnode *node,void *key,int off); -void hfs_bnode_write (struct hfs_bnode *node,void *buf,int off,int len); -void hfs_bnode_write_u16 (struct hfs_bnode *node,int off,u16 data); -void hfs_bnode_clear (struct hfs_bnode *node,int off,int len); -void hfs_bnode_copy (struct hfs_bnode *dst_node,int dst, -struct hfs_bnode *src_node,int src,int len); -void hfs_bnode_move (struct hfs_bnode *node,int dst,int src,int len); +void hfs_bnode_read (struct hfs_bnode *node,void *buf,u32 off,u32 len); +u16 hfs_bnode_read_u16 (struct hfs_bnode *node,u32 off); +u8 hfs_bnode_read_u8 (struct hfs_bnode *node,u32 off); +void hfs_bnode_read_key (struct hfs_bnode *node,void *key,u32 off); +void hfs_bnode_write (struct hfs_bnode *node,void *buf,u32 off,u32 len); +void hfs_bnode_write_u16 (struct hfs_bnode *node,u32 off,u16 data); +void hfs_bnode_clear (struct hfs_bnode *node,u32 off,u32 len); +void hfs_bnode_copy (struct hfs_bnode *dst_node,u32 dst, +struct hfs_bnode *src_node,u32 src,u32 len); +void hfs_bnode_move (struct hfs_bnode *node,u32 dst,u32 src,u32 len);void hfs_bnode_dump (struct hfs_bnode *node);void hfs_bnode_unlink (struct hfs_bnode *node);struct hfs_bnode *hfs_bnode_findhash (struct hfs_btree *tree,u32 cnid);
@@ -417,7 +417,7 @@ bool hfs_bnode_need_zeroout(struct hfs_b/* brec.c */ u16 hfs_brec_lenoff (struct hfs_bnode *node,u16 rec,u16 *off);u16 hfs_brec_keylen (struct hfs_bnode *node,u16 rec); -int hfs_brec_insert (struct hfs_find_data *fd,void *entry,int entry_len); +int hfs_brec_insert (struct hfs_find_data *fd,void *entry,u32 entry_len);int hfs_brec_remove (struct hfs_find_data *fd);/* bfind.c */
@@ -430,7 +430,7 @@ int hfs_find_rec_by_key(struct hfs_bnodeint __hfs_brec_find (struct hfs_bnode *bnode,struct hfs_find_data *fd,search_strategy_t rec_found);int hfs_brec_find (struct hfs_find_data *fd,search_strategy_t do_key_compare); -int hfs_brec_read (struct hfs_find_data *fd,void *rec,int rec_len); +int hfs_brec_read (struct hfs_find_data *fd,void *rec,u32 rec_len);int hfs_brec_goto (struct hfs_find_data *fd,int cnt);/* catalog.c */
@@ -588,14 +588,14 @@ hfsplus_btree_lock_class(struct hfs_btre }static inline -bool is_bnode_offset_valid (struct hfs_bnode *node,int off) +bool is_bnode_offset_valid (struct hfs_bnode *node,u32 off) {bool is_valid = off < node->tree->node_size;if (!is_valid) {pr_err ("requested invalid offset: " "NODE: id %u, type %#x, height %u, " -"node_size %u, offset %d\n" , +"node_size %u, offset %u\n" , node->this, node->type, node->height, node->tree->node_size, off); }
@@ -604,7 +604,7 @@ bool is_bnode_offset_valid(struct hfs_bn }static inline -int check_and_correct_requested_length (struct hfs_bnode *node,int off,int len) +u32 check_and_correct_requested_length (struct hfs_bnode *node,u32 off,u32 len) {unsigned int node_size;
@@ -614,12 +614,12 @@ int check_and_correct_requested_length(s node_size = node->tree->node_size;if ((off + len) > node_size) { -int new_len = (int )node_size - off; +u32 new_len = node_size - off;pr_err ("requested length has been corrected: " "NODE: id %u, type %#x, height %u, " -"node_size %u, offset %d, " -"requested_len %d, corrected_len %d\n" , +"node_size %u, offset %u, " +"requested_len %u, corrected_len %u\n" , node->this, node->type, node->height, node->tree->node_size, off, len, new_len);
Greg Kroah-Hartman
maintainerPATCH 1023/1067 hfs/hfsplus: fix u32 overflow in check_and_correct_requested_length
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Tristan Madani <[email protected]>
[ Upstream commit 966cb76fb2857a4242cab6ea2ea17acf818a3da7 ]
check_and_correct_requested_length() compares (off + len) against node_size using u32 arithmetic. When the caller passes a large len value (e.g. from an underflowed subtraction in hfs_brec_remove()), off + len can wrap past 2^32 and produce a small result, causing the bounds check to pass when it should fail.
For example, with off=14 and len=0xFFFFFFF2 (underflowed from data_off - keyoffset - size in hfs_brec_remove), off + len wraps to 6, which is less than a typical node_size of 512, so the check passes and the subsequent memmove reads ~4GB past the node buffer.
Fix this by widening the addition to u64 before comparing against node_size. This prevents the u32 wrap while keeping the logic straightforward.
--- fs/hfs/bnode.c | 2 +- fs/hfsplus/hfsplus_fs.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-)
fs/hfs/bnode.c +1 -1
@@ -41,7 +41,7 @@ u32 check_and_correct_requested_length(s node_size = node->tree->node_size; -if ((off + len) > node_size) { +if ((u64 )off + len > node_size) {u32 new_len = node_size - off; pr_err("requested length has been corrected: "
fs/hfsplus/hfsplus_fs.h +1 -1
@@ -613,7 +613,7 @@ u32 check_and_correct_requested_length(s node_size = node->tree->node_size; -if ((off + len) > node_size) { +if ((u64 )off + len > node_size) {u32 new_len = node_size - off; pr_err("requested length has been corrected: "
Greg Kroah-Hartman
maintainerPATCH 1024/1067 bpf: Consistently use bpf_rcu_lock_held() everywhere
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Andrii Nakryiko <[email protected]>
[ Upstream commit 48a97ffc6c826640907d13b199e29008f4fe2c15 ]
We have many places which open-code what's now is bpf_rcu_lock_held() macro, so replace all those places with a clean and short macro invocation. For that, move bpf_rcu_lock_held() macro into include/linux/bpf.h.
Stable-dep-of: 2f884d371faf ("bpf: Allow LPM map access from sleepable BPF programs")
--- include/linux/bpf.h | 3 +++ include/linux/bpf_local_storage.h | 3 --- kernel/bpf/hashtab.c | 21 +++++++-------------- kernel/bpf/helpers.c | 12 ++++-------- kernel/bpf/lpm_trie.c | 6 ++---- 5 files changed, 16 insertions(+), 29 deletions(-)
include/linux/bpf.h +3 -0
@@ -1722,6 +1722,9 @@ out:return ret; } +#define bpf_rcu_lock_held () \ + (rcu_read_lock_held() || rcu_read_lock_trace_held() || rcu_read_lock_bh_held()) +#ifdef CONFIG_BPF_SYSCALL DECLARE_PER_CPU (int, bpf_prog_active);extern struct mutex bpf_stats_enabled_mutex;
include/linux/bpf_local_storage.h +0 -3
@@ -17,9 +17,6 @@#define BPF_LOCAL_STORAGE_CACHE_SIZE 16 -#define bpf_rcu_lock_held () \ - (rcu_read_lock_held() || rcu_read_lock_trace_held() || \ - rcu_read_lock_bh_held())struct bpf_local_storage_map_bucket {struct hlist_head list;raw_spinlock_t lock;
kernel/bpf/hashtab.c +7 -14
@@ -670,8 +670,7 @@ static void *__htab_map_lookup_elem(strustruct htab_elem *l;u32 hash, key_size; -WARN_ON_ONCE (!rcu_read_lock_held () && !rcu_read_lock_trace_held () && - !rcu_read_lock_bh_held ()); +WARN_ON_ONCE (!bpf_rcu_lock_held ()); key_size = map->key_size;
@@ -1088,8 +1087,7 @@ static int htab_map_update_elem(struct b/* unknown flags */ return -EINVAL ; -WARN_ON_ONCE (!rcu_read_lock_held () && !rcu_read_lock_trace_held () && - !rcu_read_lock_bh_held ()); +WARN_ON_ONCE (!bpf_rcu_lock_held ()); key_size = map->key_size;
@@ -1190,8 +1188,7 @@ static int htab_lru_map_update_elem(stru/* unknown flags */ return -EINVAL ; -WARN_ON_ONCE (!rcu_read_lock_held () && !rcu_read_lock_trace_held () && - !rcu_read_lock_bh_held ()); +WARN_ON_ONCE (!bpf_rcu_lock_held ()); key_size = map->key_size;
@@ -1259,8 +1256,7 @@ static int __htab_percpu_map_update_elem/* unknown flags */ return -EINVAL ; -WARN_ON_ONCE (!rcu_read_lock_held () && !rcu_read_lock_trace_held () && - !rcu_read_lock_bh_held ()); +WARN_ON_ONCE (!bpf_rcu_lock_held ()); key_size = map->key_size;
@@ -1314,8 +1310,7 @@ static int __htab_lru_percpu_map_update_/* unknown flags */ return -EINVAL ; -WARN_ON_ONCE (!rcu_read_lock_held () && !rcu_read_lock_trace_held () && - !rcu_read_lock_bh_held ()); +WARN_ON_ONCE (!bpf_rcu_lock_held ()); key_size = map->key_size;
@@ -1390,8 +1385,7 @@ static int htab_map_delete_elem(struct bu32 hash, key_size;int ret; -WARN_ON_ONCE (!rcu_read_lock_held () && !rcu_read_lock_trace_held () && - !rcu_read_lock_bh_held ()); +WARN_ON_ONCE (!bpf_rcu_lock_held ()); key_size = map->key_size;
@@ -1426,8 +1420,7 @@ static int htab_lru_map_delete_elem(struu32 hash, key_size;int ret; -WARN_ON_ONCE (!rcu_read_lock_held () && !rcu_read_lock_trace_held () && - !rcu_read_lock_bh_held ()); +WARN_ON_ONCE (!bpf_rcu_lock_held ()); key_size = map->key_size;
kernel/bpf/helpers.c +4 -8
@@ -33,8 +33,7 @@*/ BPF_CALL_2(bpf_map_lookup_elem, struct bpf_map *, map, void *, key) { -WARN_ON_ONCE (!rcu_read_lock_held () && !rcu_read_lock_trace_held () && - !rcu_read_lock_bh_held ()); +WARN_ON_ONCE (!bpf_rcu_lock_held ());return (unsigned long ) map->ops->map_lookup_elem (map, key); }
@@ -50,8 +49,7 @@ const struct bpf_func_proto bpf_map_look BPF_CALL_4(bpf_map_update_elem, struct bpf_map *, map, void *, key, void *, value, u64, flags) { -WARN_ON_ONCE (!rcu_read_lock_held () && !rcu_read_lock_trace_held () && - !rcu_read_lock_bh_held ()); +WARN_ON_ONCE (!bpf_rcu_lock_held ());return map->ops->map_update_elem (map, key, value, flags); }
@@ -68,8 +66,7 @@ const struct bpf_func_proto bpf_map_upda BPF_CALL_2(bpf_map_delete_elem, struct bpf_map *, map, void *, key) { -WARN_ON_ONCE (!rcu_read_lock_held () && !rcu_read_lock_trace_held () && - !rcu_read_lock_bh_held ()); +WARN_ON_ONCE (!bpf_rcu_lock_held ());return map->ops->map_delete_elem (map, key); }
@@ -125,8 +122,7 @@ const struct bpf_func_proto bpf_map_peek BPF_CALL_3(bpf_map_lookup_percpu_elem, struct bpf_map *, map, void *, key, u32, cpu) { -WARN_ON_ONCE (!rcu_read_lock_held () && !rcu_read_lock_trace_held () && - !rcu_read_lock_bh_held ()); +WARN_ON_ONCE (!bpf_rcu_lock_held ());return (unsigned long ) map->ops->map_lookup_percpu_elem (map, key, cpu); }
kernel/bpf/lpm_trie.c +2 -4
@@ -353,8 +353,7 @@ static int trie_update_elem(struct bpf_m*/ slot = &trie->root; -while ((node =rcu_dereference_protected (*slot, -lockdep_is_held (&trie->lock)))) { +while ((node =rcu_dereference_protected (*slot,1 ))) { matchlen =longest_prefix_match (trie, node, key); if (node->prefixlen != matchlen ||
@@ -475,8 +474,7 @@ static int trie_delete_elem(struct bpf_m trim = &trie->root; trim2 = trim; parent =NULL ; -while ((node =rcu_dereference_protected ( - *trim,lockdep_is_held (&trie->lock)))) { +while ((node =rcu_dereference_protected (*trim,1 ))) { matchlen =longest_prefix_match (trie, node, key); if (node->prefixlen != matchlen ||
Greg Kroah-Hartman
maintainerPATCH 1025/1067 bpf: Allow LPM map access from sleepable BPF programs
In reply to Greg Kroah-Hartman6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Vlad Poenaru <[email protected]>
[ Upstream commit 2f884d371fafea137afea504d49ee4a7c8d7985b ]
trie_lookup_elem() annotates its rcu_dereference_check() walks with only rcu_read_lock_bh_held(). Because rcu_dereference_check(p, c) resolves to "c || rcu_read_lock_held()", this passes for XDP/NAPI and classic RCU readers but fails for sleepable BPF programs, which enter via __bpf_prog_enter_sleepable() and hold only rcu_read_lock_trace().
trie_update_elem() and trie_delete_elem() have the same problem in a different form: they walk the trie with plain rcu_dereference(), which asserts rcu_read_lock_held() unconditionally. Both are reachable from sleepable BPF programs via the bpf_map_update_elem / bpf_map_delete_elem helpers, and from the syscall path under classic rcu_read_lock(). In the writer paths the trie is actually protected by trie->lock (an rqspinlock taken across the walk); we never relied on the RCU read-side lock to keep nodes alive there.
A sleepable LSM hook that ends up touching an LPM trie therefore triggers lockdep on debug kernels:
=============================
WARNING: suspicious RCU usage
7.1.0-... Tainted: G E
-----------------------------
kernel/bpf/lpm_trie.c:249 suspicious rcu_dereference_check() usage! 1 lock held by net_tests/540:
#0: (rcu_tasks_trace_srcu_struct){....}-{0:0},
at: __bpf_prog_enter_sleepable+0x26/0x280 Call Trace: dump_stack_lvl lockdep_rcu_suspicious trie_lookup_elem bpf_prog_..._enforce_security_socket_connect bpf_trampoline_... security_socket_connect __sys_connect do_syscall_64
This is lockdep-only -- no UAF, since Tasks Trace RCU does serialize against the trie's reclaim path -- but it spams the console once per distinct callsite on every debug kernel running a sleepable BPF LSM that touches an LPM trie, which is increasingly common.
For the lookup path, switch the rcu_dereference_check() annotation from rcu_read_lock_bh_held() to bpf_rcu_lock_held(), which accepts all three contexts (classic, BH, Tasks Trace). Other map types already follow this convention.
For trie_update_elem() and trie_delete_elem(), annotate the walks as rcu_dereference_protected(*p, 1) -- matching trie_free() in the same file -- since trie->lock is held across the walk. rqspinlock has no lockdep_map, so the predicate degenerates to '1' rather than lockdep_is_held(&trie->lock); the protection is real but not machine-verifiable. trie_get_next_key() also uses bare rcu_dereference() but is reachable only from the BPF syscall, which holds classic rcu_read_lock() before dispatching, so it is left untouched.
--- kernel/bpf/lpm_trie.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
kernel/bpf/lpm_trie.c +2 -2
@@ -236,7 +236,7 @@ static void *trie_lookup_elem(struct bpf/* Start walking the trie from the root node ... */ -for (node =rcu_dereference_check (trie->root,rcu_read_lock_bh_held ()); +for (node =rcu_dereference_check (trie->root,bpf_rcu_lock_held ()); node;) {unsigned int next_bit;size_t matchlen;
@@ -270,7 +270,7 @@ static void *trie_lookup_elem(struct bpf*/ next_bit =extract_bit (key->data, node->prefixlen); node =rcu_dereference_check (node->child[next_bit], -rcu_read_lock_bh_held ()); +bpf_rcu_lock_held ()); }if (!found)